@wrongstack/core 0.298.0 → 0.298.2
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/coordination/index.js +92 -7
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-codecs.d.ts +1 -1
- package/dist/coordination/mailbox-project-server.js +32 -1
- package/dist/coordination/mailbox-types.d.ts +158 -3
- package/dist/coordination/model-matrix.d.ts +16 -0
- package/dist/core/index.js +447 -43
- package/dist/defaults/index.js +86 -63
- package/dist/index.js +816 -674
- package/dist/infrastructure/index.js +21 -2
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +108 -1
- package/dist/models/models-dev-schema.d.ts +308 -0
- package/dist/plugin/index.js +52 -55
- package/dist/plugins/auto-review-plugin.d.ts +18 -13
- package/dist/security/auto-approve-policy.d.ts +37 -0
- package/dist/security/index.js +64 -60
- package/dist/security/permission-helpers.d.ts +93 -0
- package/dist/security/permission-policy.d.ts +2 -52
- package/dist/storage/index.js +21 -2
- package/dist/tools/index.js +30 -2
- package/dist/types/config/autonomy.d.ts +3 -4
- package/dist/types/config/providers.d.ts +13 -1
- package/dist/types/models-registry.d.ts +9 -4
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +46 -0
- package/dist/utils/sage-output-block.js +48 -0
- package/instructions/llm/chimera-review.md +4 -6
- package/package.json +9 -4
- package/skills/auto-review/SKILL.md +11 -76
- package/skills/chimera/SKILL.md +23 -53
package/dist/index.js
CHANGED
|
@@ -1007,6 +1007,328 @@ var init_session_registry = __esm({
|
|
|
1007
1007
|
}
|
|
1008
1008
|
});
|
|
1009
1009
|
|
|
1010
|
+
// src/plugins/review-report-types.ts
|
|
1011
|
+
function validateReportTransition(from, to) {
|
|
1012
|
+
if (from === to) return;
|
|
1013
|
+
const allowed = REPORT_TRANSITION_MATRIX[from];
|
|
1014
|
+
if (!allowed?.has(to)) {
|
|
1015
|
+
throw new InvalidReportTransitionError(from, to);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function reportEventTypeFor(to) {
|
|
1019
|
+
switch (to) {
|
|
1020
|
+
case "actioned":
|
|
1021
|
+
return "actioned";
|
|
1022
|
+
case "completed":
|
|
1023
|
+
return "completed";
|
|
1024
|
+
case "skipped":
|
|
1025
|
+
return "skipped";
|
|
1026
|
+
default:
|
|
1027
|
+
return "reopened";
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
var InvalidReportTransitionError, REPORT_TRANSITION_MATRIX;
|
|
1031
|
+
var init_review_report_types = __esm({
|
|
1032
|
+
"src/plugins/review-report-types.ts"() {
|
|
1033
|
+
"use strict";
|
|
1034
|
+
InvalidReportTransitionError = class extends Error {
|
|
1035
|
+
from;
|
|
1036
|
+
to;
|
|
1037
|
+
constructor(from, to, reason) {
|
|
1038
|
+
const msg = reason ?? `Invalid report transition from "${from}" to "${to}"`;
|
|
1039
|
+
super(msg);
|
|
1040
|
+
this.name = "InvalidReportTransitionError";
|
|
1041
|
+
this.from = from;
|
|
1042
|
+
this.to = to;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
REPORT_TRANSITION_MATRIX = {
|
|
1046
|
+
open: /* @__PURE__ */ new Set(["actioned", "completed", "skipped"]),
|
|
1047
|
+
actioned: /* @__PURE__ */ new Set(["completed", "skipped"]),
|
|
1048
|
+
completed: /* @__PURE__ */ new Set(["open"]),
|
|
1049
|
+
skipped: /* @__PURE__ */ new Set(["open"])
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// src/plugins/review-report-store.ts
|
|
1055
|
+
var review_report_store_exports = {};
|
|
1056
|
+
__export(review_report_store_exports, {
|
|
1057
|
+
JsonlReportStore: () => JsonlReportStore,
|
|
1058
|
+
REPORT_DEFAULT_PAGE_SIZE: () => REPORT_DEFAULT_PAGE_SIZE,
|
|
1059
|
+
REPORT_RETENTION_MS: () => REPORT_RETENTION_MS,
|
|
1060
|
+
REPORT_STORE_FILE: () => REPORT_STORE_FILE,
|
|
1061
|
+
resolveReportStorePath: () => resolveReportStorePath
|
|
1062
|
+
});
|
|
1063
|
+
import { randomUUID as randomUUID28 } from "node:crypto";
|
|
1064
|
+
import * as fsp28 from "node:fs/promises";
|
|
1065
|
+
import * as path67 from "node:path";
|
|
1066
|
+
function resolveReportStorePath(projectDir) {
|
|
1067
|
+
return path67.join(projectDir, REPORT_STORE_FILE);
|
|
1068
|
+
}
|
|
1069
|
+
var REPORT_RETENTION_MS, REPORT_DEFAULT_PAGE_SIZE, NL, REPORT_STORE_FILE, JsonlReportStore;
|
|
1070
|
+
var init_review_report_store = __esm({
|
|
1071
|
+
"src/plugins/review-report-store.ts"() {
|
|
1072
|
+
"use strict";
|
|
1073
|
+
init_file_permissions();
|
|
1074
|
+
init_atomic_write();
|
|
1075
|
+
init_review_report_types();
|
|
1076
|
+
REPORT_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
1077
|
+
REPORT_DEFAULT_PAGE_SIZE = 25;
|
|
1078
|
+
NL = "\n";
|
|
1079
|
+
REPORT_STORE_FILE = "review-reports.jsonl";
|
|
1080
|
+
JsonlReportStore = class {
|
|
1081
|
+
filePath;
|
|
1082
|
+
constructor(projectDir) {
|
|
1083
|
+
this.filePath = resolveReportStorePath(projectDir);
|
|
1084
|
+
}
|
|
1085
|
+
get storePath() {
|
|
1086
|
+
return this.filePath;
|
|
1087
|
+
}
|
|
1088
|
+
// ── Persist (create or reopen) ───────────────────────────────────
|
|
1089
|
+
async persist(input) {
|
|
1090
|
+
return withFileLock(this.filePath, async () => {
|
|
1091
|
+
const existing = await this._findRecord(input.id);
|
|
1092
|
+
if (existing) {
|
|
1093
|
+
const updated = {
|
|
1094
|
+
...existing,
|
|
1095
|
+
counts: input.counts,
|
|
1096
|
+
totalFindings: input.totalFindings,
|
|
1097
|
+
unparseableCount: input.unparseableCount,
|
|
1098
|
+
durationSeconds: input.durationSeconds ?? existing.durationSeconds,
|
|
1099
|
+
rawText: input.rawText || existing.rawText,
|
|
1100
|
+
files: input.files.length > 0 ? input.files : existing.files
|
|
1101
|
+
};
|
|
1102
|
+
await fsp28.appendFile(this.filePath, JSON.stringify({ __report: 1, data: updated }) + NL, {
|
|
1103
|
+
encoding: "utf8",
|
|
1104
|
+
mode: SECRET_FILE_MODE
|
|
1105
|
+
});
|
|
1106
|
+
return updated;
|
|
1107
|
+
}
|
|
1108
|
+
const report = {
|
|
1109
|
+
id: input.id,
|
|
1110
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1111
|
+
sessionId: input.sessionId,
|
|
1112
|
+
agentId: input.agentId,
|
|
1113
|
+
reviewerModel: input.reviewerModel,
|
|
1114
|
+
source: input.source,
|
|
1115
|
+
reviewStatus: input.reviewStatus,
|
|
1116
|
+
lifecycle: "open",
|
|
1117
|
+
files: input.files,
|
|
1118
|
+
counts: input.counts,
|
|
1119
|
+
totalFindings: input.totalFindings,
|
|
1120
|
+
unparseableCount: input.unparseableCount,
|
|
1121
|
+
durationSeconds: input.durationSeconds,
|
|
1122
|
+
rawText: input.rawText,
|
|
1123
|
+
...input.cascadeDepth !== void 0 ? { cascadeDepth: input.cascadeDepth } : {}
|
|
1124
|
+
};
|
|
1125
|
+
const createdEvent = {
|
|
1126
|
+
id: randomUUID28(),
|
|
1127
|
+
reportId: report.id,
|
|
1128
|
+
eventType: "created",
|
|
1129
|
+
fromLifecycle: null,
|
|
1130
|
+
toLifecycle: "open",
|
|
1131
|
+
actorId: "system",
|
|
1132
|
+
actorKind: "system",
|
|
1133
|
+
timestamp: report.reviewedAt
|
|
1134
|
+
};
|
|
1135
|
+
const lines = JSON.stringify({ __report: 1, data: report }) + NL + JSON.stringify({ __reportEvent: 1, data: createdEvent }) + NL;
|
|
1136
|
+
await fsp28.appendFile(this.filePath, lines, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
1137
|
+
return report;
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
// ── Lifecycle transitions ────────────────────────────────────────
|
|
1141
|
+
async transition(reportId, to, actor, opts) {
|
|
1142
|
+
return withFileLock(this.filePath, async () => {
|
|
1143
|
+
const all = await this._readAll();
|
|
1144
|
+
const entry = all.find((e) => e.report.id === reportId);
|
|
1145
|
+
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
1146
|
+
const from = this._materialize(entry).lifecycle;
|
|
1147
|
+
validateReportTransition(from, to);
|
|
1148
|
+
const event = {
|
|
1149
|
+
id: randomUUID28(),
|
|
1150
|
+
reportId,
|
|
1151
|
+
eventType: reportEventTypeFor(to),
|
|
1152
|
+
fromLifecycle: from,
|
|
1153
|
+
toLifecycle: to,
|
|
1154
|
+
actorId: actor.id,
|
|
1155
|
+
actorKind: actor.kind,
|
|
1156
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1157
|
+
reason: opts?.reason
|
|
1158
|
+
};
|
|
1159
|
+
entry.report.lifecycle = to;
|
|
1160
|
+
await fsp28.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL, {
|
|
1161
|
+
encoding: "utf8",
|
|
1162
|
+
mode: SECRET_FILE_MODE
|
|
1163
|
+
});
|
|
1164
|
+
return { ...entry.report };
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
async addNote(reportId, actor, note) {
|
|
1168
|
+
return withFileLock(this.filePath, async () => {
|
|
1169
|
+
const all = await this._readAll();
|
|
1170
|
+
const entry = all.find((e) => e.report.id === reportId);
|
|
1171
|
+
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
1172
|
+
const materialized = this._materialize(entry);
|
|
1173
|
+
const event = {
|
|
1174
|
+
id: randomUUID28(),
|
|
1175
|
+
reportId,
|
|
1176
|
+
eventType: "note_added",
|
|
1177
|
+
fromLifecycle: materialized.lifecycle,
|
|
1178
|
+
toLifecycle: materialized.lifecycle,
|
|
1179
|
+
actorId: actor.id,
|
|
1180
|
+
actorKind: actor.kind,
|
|
1181
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1182
|
+
reason: note
|
|
1183
|
+
};
|
|
1184
|
+
await fsp28.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL, {
|
|
1185
|
+
encoding: "utf8",
|
|
1186
|
+
mode: SECRET_FILE_MODE
|
|
1187
|
+
});
|
|
1188
|
+
return materialized;
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
// ── Query ────────────────────────────────────────────────────────
|
|
1192
|
+
async list(opts) {
|
|
1193
|
+
const all = await this._readAll();
|
|
1194
|
+
let reports = all.map((e) => this._materialize(e));
|
|
1195
|
+
if (opts?.statuses && opts.statuses.length > 0) {
|
|
1196
|
+
const set = new Set(opts.statuses);
|
|
1197
|
+
reports = reports.filter((r) => set.has(r.lifecycle));
|
|
1198
|
+
}
|
|
1199
|
+
if (opts?.sources && opts.sources.length > 0) {
|
|
1200
|
+
const set = new Set(opts.sources);
|
|
1201
|
+
reports = reports.filter((r) => set.has(r.source));
|
|
1202
|
+
}
|
|
1203
|
+
reports.sort((a, b) => b.reviewedAt.localeCompare(a.reviewedAt));
|
|
1204
|
+
const limit = opts?.limit ?? REPORT_DEFAULT_PAGE_SIZE;
|
|
1205
|
+
return reports.slice(0, limit);
|
|
1206
|
+
}
|
|
1207
|
+
async get(id) {
|
|
1208
|
+
const all = await this._readAll();
|
|
1209
|
+
const entry = all.find((e) => e.report.id === id);
|
|
1210
|
+
return entry ? this._materialize(entry) : null;
|
|
1211
|
+
}
|
|
1212
|
+
async getEvents(reportId) {
|
|
1213
|
+
const all = await this._readAllLines();
|
|
1214
|
+
const events = [];
|
|
1215
|
+
for (const line of all) {
|
|
1216
|
+
try {
|
|
1217
|
+
const parsed = JSON.parse(line);
|
|
1218
|
+
if (this._isEventRecord(parsed) && parsed.data.reportId === reportId) {
|
|
1219
|
+
events.push(parsed.data);
|
|
1220
|
+
}
|
|
1221
|
+
} catch {
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return events.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1225
|
+
}
|
|
1226
|
+
// ── Compaction ───────────────────────────────────────────────────
|
|
1227
|
+
async compact(opts) {
|
|
1228
|
+
return withFileLock(this.filePath, async () => {
|
|
1229
|
+
const maxAge = opts?.maxAgeMs ?? REPORT_RETENTION_MS;
|
|
1230
|
+
const now = Date.now();
|
|
1231
|
+
const all = await this._readAll();
|
|
1232
|
+
const kept = [];
|
|
1233
|
+
let removed = 0;
|
|
1234
|
+
let eventsFolded = 0;
|
|
1235
|
+
for (const entry of all) {
|
|
1236
|
+
const age = now - new Date(entry.report.reviewedAt).getTime();
|
|
1237
|
+
const materialized = this._materialize(entry);
|
|
1238
|
+
const isTerminal = materialized.lifecycle === "completed" || materialized.lifecycle === "skipped";
|
|
1239
|
+
if (isTerminal && age > maxAge) {
|
|
1240
|
+
removed++;
|
|
1241
|
+
eventsFolded += entry.events.length;
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
const oldEvents = entry.events.filter(
|
|
1245
|
+
(ev) => now - new Date(ev.timestamp).getTime() > maxAge
|
|
1246
|
+
);
|
|
1247
|
+
if (oldEvents.length > 1) {
|
|
1248
|
+
eventsFolded += oldEvents.length - 1;
|
|
1249
|
+
const newestOld = oldEvents.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1250
|
+
entry.events = [
|
|
1251
|
+
newestOld,
|
|
1252
|
+
...entry.events.filter((ev) => now - new Date(ev.timestamp).getTime() <= maxAge)
|
|
1253
|
+
];
|
|
1254
|
+
}
|
|
1255
|
+
kept.push({ record: { __report: 1, data: entry.report }, events: entry.events });
|
|
1256
|
+
}
|
|
1257
|
+
const lines = [];
|
|
1258
|
+
for (const { record, events } of kept) {
|
|
1259
|
+
if (record) lines.push(JSON.stringify(record));
|
|
1260
|
+
for (const ev of events) {
|
|
1261
|
+
lines.push(JSON.stringify({ __reportEvent: 1, data: ev }));
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
lines.push(
|
|
1265
|
+
JSON.stringify({
|
|
1266
|
+
__reportCompact: 1,
|
|
1267
|
+
compactedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1268
|
+
removedReports: removed,
|
|
1269
|
+
foldedEvents: eventsFolded
|
|
1270
|
+
})
|
|
1271
|
+
);
|
|
1272
|
+
await atomicWrite(this.filePath, lines.join(NL) + NL, { mode: 384 });
|
|
1273
|
+
return { removed, eventsFolded };
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
// ── Private helpers ──────────────────────────────────────────────
|
|
1277
|
+
async _readAllLines() {
|
|
1278
|
+
try {
|
|
1279
|
+
const raw = await fsp28.readFile(this.filePath, "utf8");
|
|
1280
|
+
return raw.split(NL).filter((l) => l.trim().length > 0);
|
|
1281
|
+
} catch (err) {
|
|
1282
|
+
if (err.code === "ENOENT") return [];
|
|
1283
|
+
throw err;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
_isReportRecord(v) {
|
|
1287
|
+
return typeof v === "object" && v !== null && v["__report"] === 1;
|
|
1288
|
+
}
|
|
1289
|
+
_isEventRecord(v) {
|
|
1290
|
+
return typeof v === "object" && v !== null && v["__reportEvent"] === 1;
|
|
1291
|
+
}
|
|
1292
|
+
async _readAll() {
|
|
1293
|
+
const lines = await this._readAllLines();
|
|
1294
|
+
const reports = /* @__PURE__ */ new Map();
|
|
1295
|
+
const eventsMap = /* @__PURE__ */ new Map();
|
|
1296
|
+
for (const line of lines) {
|
|
1297
|
+
try {
|
|
1298
|
+
const parsed = JSON.parse(line);
|
|
1299
|
+
if (this._isReportRecord(parsed)) {
|
|
1300
|
+
reports.set(parsed.data.id, parsed.data);
|
|
1301
|
+
if (!eventsMap.has(parsed.data.id)) eventsMap.set(parsed.data.id, []);
|
|
1302
|
+
} else if (this._isEventRecord(parsed)) {
|
|
1303
|
+
const list = eventsMap.get(parsed.data.reportId);
|
|
1304
|
+
if (list) list.push(parsed.data);
|
|
1305
|
+
}
|
|
1306
|
+
} catch {
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return Array.from(reports.entries()).map(([id, report]) => ({
|
|
1310
|
+
report,
|
|
1311
|
+
events: eventsMap.get(id) ?? []
|
|
1312
|
+
}));
|
|
1313
|
+
}
|
|
1314
|
+
async _findRecord(id) {
|
|
1315
|
+
const all = await this._readAll();
|
|
1316
|
+
const entry = all.find((e) => e.report.id === id);
|
|
1317
|
+
return entry ? this._materialize(entry) : null;
|
|
1318
|
+
}
|
|
1319
|
+
_materialize(entry) {
|
|
1320
|
+
if (entry.events.length === 0) return { ...entry.report };
|
|
1321
|
+
const sorted = [...entry.events].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1322
|
+
const latest = sorted[sorted.length - 1];
|
|
1323
|
+
if (latest.eventType !== "note_added") {
|
|
1324
|
+
return { ...entry.report, lifecycle: latest.toLifecycle };
|
|
1325
|
+
}
|
|
1326
|
+
return { ...entry.report };
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1010
1332
|
// src/plugins/review-finding-types.ts
|
|
1011
1333
|
import { createHash as createHash32 } from "node:crypto";
|
|
1012
1334
|
function normalizeFingerprintTitle(title) {
|
|
@@ -1072,13 +1394,13 @@ __export(review_finding_store_exports, {
|
|
|
1072
1394
|
JsonlFindingStore: () => JsonlFindingStore,
|
|
1073
1395
|
resolveFindingStorePath: () => resolveFindingStorePath
|
|
1074
1396
|
});
|
|
1075
|
-
import { randomUUID as
|
|
1076
|
-
import * as
|
|
1077
|
-
import * as
|
|
1397
|
+
import { randomUUID as randomUUID36 } from "node:crypto";
|
|
1398
|
+
import * as fsp39 from "node:fs/promises";
|
|
1399
|
+
import * as path91 from "node:path";
|
|
1078
1400
|
function resolveFindingStorePath(projectDir) {
|
|
1079
|
-
return
|
|
1401
|
+
return path91.join(projectDir, FINDING_STORE_FILE);
|
|
1080
1402
|
}
|
|
1081
|
-
var FINDING_RESOLVED_RETENTION_MS, FINDING_IGNORED_RETENTION_MS, FINDING_DEFAULT_PAGE_SIZE,
|
|
1403
|
+
var FINDING_RESOLVED_RETENTION_MS, FINDING_IGNORED_RETENTION_MS, FINDING_DEFAULT_PAGE_SIZE, NL2, LINE_SEPARATOR, FINDING_STORE_FILE, JsonlFindingStore;
|
|
1082
1404
|
var init_review_finding_store = __esm({
|
|
1083
1405
|
"src/plugins/review-finding-store.ts"() {
|
|
1084
1406
|
"use strict";
|
|
@@ -1088,8 +1410,8 @@ var init_review_finding_store = __esm({
|
|
|
1088
1410
|
FINDING_RESOLVED_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1089
1411
|
FINDING_IGNORED_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
1090
1412
|
FINDING_DEFAULT_PAGE_SIZE = 50;
|
|
1091
|
-
|
|
1092
|
-
LINE_SEPARATOR =
|
|
1413
|
+
NL2 = "\n";
|
|
1414
|
+
LINE_SEPARATOR = NL2;
|
|
1093
1415
|
FINDING_STORE_FILE = "review-findings.jsonl";
|
|
1094
1416
|
JsonlFindingStore = class {
|
|
1095
1417
|
filePath;
|
|
@@ -1120,7 +1442,7 @@ var init_review_finding_store = __esm({
|
|
|
1120
1442
|
match.finding.status = "active";
|
|
1121
1443
|
match.finding.resolution = void 0;
|
|
1122
1444
|
const updatedRecord = { __finding: 1, data: match.finding };
|
|
1123
|
-
await
|
|
1445
|
+
await fsp39.appendFile(
|
|
1124
1446
|
this.filePath,
|
|
1125
1447
|
JSON.stringify(updatedRecord) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: reopenEvent }) + LINE_SEPARATOR,
|
|
1126
1448
|
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
@@ -1134,7 +1456,7 @@ var init_review_finding_store = __esm({
|
|
|
1134
1456
|
match.finding.status,
|
|
1135
1457
|
context
|
|
1136
1458
|
);
|
|
1137
|
-
await
|
|
1459
|
+
await fsp39.appendFile(
|
|
1138
1460
|
this.filePath,
|
|
1139
1461
|
JSON.stringify({ __findingEvent: 1, data: relinkEvent }) + LINE_SEPARATOR,
|
|
1140
1462
|
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
@@ -1145,7 +1467,7 @@ var init_review_finding_store = __esm({
|
|
|
1145
1467
|
const record = { __finding: 1, data: finding };
|
|
1146
1468
|
const createdEvent = this._makeEvent(finding.id, "created", null, "active", context);
|
|
1147
1469
|
const lines = JSON.stringify(record) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: createdEvent }) + LINE_SEPARATOR;
|
|
1148
|
-
await
|
|
1470
|
+
await fsp39.appendFile(this.filePath, lines, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
1149
1471
|
result.created++;
|
|
1150
1472
|
}
|
|
1151
1473
|
}
|
|
@@ -1161,7 +1483,7 @@ var init_review_finding_store = __esm({
|
|
|
1161
1483
|
validateTransition(from, to);
|
|
1162
1484
|
validateResolution(to, opts?.outcome);
|
|
1163
1485
|
const event = {
|
|
1164
|
-
id:
|
|
1486
|
+
id: randomUUID36(),
|
|
1165
1487
|
findingId,
|
|
1166
1488
|
eventType: to === "resolved" ? "resolved" : to === "ignored" ? "ignored" : this._eventTypeFor(to),
|
|
1167
1489
|
fromStatus: from,
|
|
@@ -1181,7 +1503,7 @@ var init_review_finding_store = __esm({
|
|
|
1181
1503
|
};
|
|
1182
1504
|
}
|
|
1183
1505
|
const updatedRecord = { __finding: 1, data: entry.finding };
|
|
1184
|
-
await
|
|
1506
|
+
await fsp39.appendFile(
|
|
1185
1507
|
this.filePath,
|
|
1186
1508
|
JSON.stringify(updatedRecord) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: event }) + LINE_SEPARATOR,
|
|
1187
1509
|
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
@@ -1298,7 +1620,7 @@ var init_review_finding_store = __esm({
|
|
|
1298
1620
|
// ── Private helpers ──────────────────────────────────────────────
|
|
1299
1621
|
async _readAllLines() {
|
|
1300
1622
|
try {
|
|
1301
|
-
const raw = await
|
|
1623
|
+
const raw = await fsp39.readFile(this.filePath, "utf8");
|
|
1302
1624
|
return raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
|
|
1303
1625
|
} catch (err) {
|
|
1304
1626
|
if (err.code === "ENOENT") return [];
|
|
@@ -1345,7 +1667,7 @@ var init_review_finding_store = __esm({
|
|
|
1345
1667
|
}
|
|
1346
1668
|
_makeEvent(findingId, eventType, fromStatus, toStatus, context) {
|
|
1347
1669
|
return {
|
|
1348
|
-
id:
|
|
1670
|
+
id: randomUUID36(),
|
|
1349
1671
|
findingId,
|
|
1350
1672
|
eventType,
|
|
1351
1673
|
fromStatus,
|
|
@@ -1376,328 +1698,6 @@ var init_review_finding_store = __esm({
|
|
|
1376
1698
|
}
|
|
1377
1699
|
});
|
|
1378
1700
|
|
|
1379
|
-
// src/plugins/review-report-types.ts
|
|
1380
|
-
function validateReportTransition(from, to) {
|
|
1381
|
-
if (from === to) return;
|
|
1382
|
-
const allowed = REPORT_TRANSITION_MATRIX[from];
|
|
1383
|
-
if (!allowed?.has(to)) {
|
|
1384
|
-
throw new InvalidReportTransitionError(from, to);
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
function reportEventTypeFor(to) {
|
|
1388
|
-
switch (to) {
|
|
1389
|
-
case "actioned":
|
|
1390
|
-
return "actioned";
|
|
1391
|
-
case "completed":
|
|
1392
|
-
return "completed";
|
|
1393
|
-
case "skipped":
|
|
1394
|
-
return "skipped";
|
|
1395
|
-
default:
|
|
1396
|
-
return "reopened";
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
var InvalidReportTransitionError, REPORT_TRANSITION_MATRIX;
|
|
1400
|
-
var init_review_report_types = __esm({
|
|
1401
|
-
"src/plugins/review-report-types.ts"() {
|
|
1402
|
-
"use strict";
|
|
1403
|
-
InvalidReportTransitionError = class extends Error {
|
|
1404
|
-
from;
|
|
1405
|
-
to;
|
|
1406
|
-
constructor(from, to, reason) {
|
|
1407
|
-
const msg = reason ?? `Invalid report transition from "${from}" to "${to}"`;
|
|
1408
|
-
super(msg);
|
|
1409
|
-
this.name = "InvalidReportTransitionError";
|
|
1410
|
-
this.from = from;
|
|
1411
|
-
this.to = to;
|
|
1412
|
-
}
|
|
1413
|
-
};
|
|
1414
|
-
REPORT_TRANSITION_MATRIX = {
|
|
1415
|
-
open: /* @__PURE__ */ new Set(["actioned", "completed", "skipped"]),
|
|
1416
|
-
actioned: /* @__PURE__ */ new Set(["completed", "skipped"]),
|
|
1417
|
-
completed: /* @__PURE__ */ new Set(["open"]),
|
|
1418
|
-
skipped: /* @__PURE__ */ new Set(["open"])
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1422
|
-
|
|
1423
|
-
// src/plugins/review-report-store.ts
|
|
1424
|
-
var review_report_store_exports = {};
|
|
1425
|
-
__export(review_report_store_exports, {
|
|
1426
|
-
JsonlReportStore: () => JsonlReportStore,
|
|
1427
|
-
REPORT_DEFAULT_PAGE_SIZE: () => REPORT_DEFAULT_PAGE_SIZE,
|
|
1428
|
-
REPORT_RETENTION_MS: () => REPORT_RETENTION_MS,
|
|
1429
|
-
REPORT_STORE_FILE: () => REPORT_STORE_FILE,
|
|
1430
|
-
resolveReportStorePath: () => resolveReportStorePath
|
|
1431
|
-
});
|
|
1432
|
-
import { randomUUID as randomUUID37 } from "node:crypto";
|
|
1433
|
-
import * as fsp39 from "node:fs/promises";
|
|
1434
|
-
import * as path91 from "node:path";
|
|
1435
|
-
function resolveReportStorePath(projectDir) {
|
|
1436
|
-
return path91.join(projectDir, REPORT_STORE_FILE);
|
|
1437
|
-
}
|
|
1438
|
-
var REPORT_RETENTION_MS, REPORT_DEFAULT_PAGE_SIZE, NL2, REPORT_STORE_FILE, JsonlReportStore;
|
|
1439
|
-
var init_review_report_store = __esm({
|
|
1440
|
-
"src/plugins/review-report-store.ts"() {
|
|
1441
|
-
"use strict";
|
|
1442
|
-
init_file_permissions();
|
|
1443
|
-
init_atomic_write();
|
|
1444
|
-
init_review_report_types();
|
|
1445
|
-
REPORT_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
1446
|
-
REPORT_DEFAULT_PAGE_SIZE = 25;
|
|
1447
|
-
NL2 = "\n";
|
|
1448
|
-
REPORT_STORE_FILE = "review-reports.jsonl";
|
|
1449
|
-
JsonlReportStore = class {
|
|
1450
|
-
filePath;
|
|
1451
|
-
constructor(projectDir) {
|
|
1452
|
-
this.filePath = resolveReportStorePath(projectDir);
|
|
1453
|
-
}
|
|
1454
|
-
get storePath() {
|
|
1455
|
-
return this.filePath;
|
|
1456
|
-
}
|
|
1457
|
-
// ── Persist (create or reopen) ───────────────────────────────────
|
|
1458
|
-
async persist(input) {
|
|
1459
|
-
return withFileLock(this.filePath, async () => {
|
|
1460
|
-
const existing = await this._findRecord(input.id);
|
|
1461
|
-
if (existing) {
|
|
1462
|
-
const updated = {
|
|
1463
|
-
...existing,
|
|
1464
|
-
counts: input.counts,
|
|
1465
|
-
totalFindings: input.totalFindings,
|
|
1466
|
-
unparseableCount: input.unparseableCount,
|
|
1467
|
-
durationSeconds: input.durationSeconds ?? existing.durationSeconds,
|
|
1468
|
-
rawText: input.rawText || existing.rawText,
|
|
1469
|
-
files: input.files.length > 0 ? input.files : existing.files
|
|
1470
|
-
};
|
|
1471
|
-
await fsp39.appendFile(this.filePath, JSON.stringify({ __report: 1, data: updated }) + NL2, {
|
|
1472
|
-
encoding: "utf8",
|
|
1473
|
-
mode: SECRET_FILE_MODE
|
|
1474
|
-
});
|
|
1475
|
-
return updated;
|
|
1476
|
-
}
|
|
1477
|
-
const report = {
|
|
1478
|
-
id: input.id,
|
|
1479
|
-
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1480
|
-
sessionId: input.sessionId,
|
|
1481
|
-
agentId: input.agentId,
|
|
1482
|
-
reviewerModel: input.reviewerModel,
|
|
1483
|
-
source: input.source,
|
|
1484
|
-
reviewStatus: input.reviewStatus,
|
|
1485
|
-
lifecycle: "open",
|
|
1486
|
-
files: input.files,
|
|
1487
|
-
counts: input.counts,
|
|
1488
|
-
totalFindings: input.totalFindings,
|
|
1489
|
-
unparseableCount: input.unparseableCount,
|
|
1490
|
-
durationSeconds: input.durationSeconds,
|
|
1491
|
-
rawText: input.rawText,
|
|
1492
|
-
...input.cascadeDepth !== void 0 ? { cascadeDepth: input.cascadeDepth } : {}
|
|
1493
|
-
};
|
|
1494
|
-
const createdEvent = {
|
|
1495
|
-
id: randomUUID37(),
|
|
1496
|
-
reportId: report.id,
|
|
1497
|
-
eventType: "created",
|
|
1498
|
-
fromLifecycle: null,
|
|
1499
|
-
toLifecycle: "open",
|
|
1500
|
-
actorId: "system",
|
|
1501
|
-
actorKind: "system",
|
|
1502
|
-
timestamp: report.reviewedAt
|
|
1503
|
-
};
|
|
1504
|
-
const lines = JSON.stringify({ __report: 1, data: report }) + NL2 + JSON.stringify({ __reportEvent: 1, data: createdEvent }) + NL2;
|
|
1505
|
-
await fsp39.appendFile(this.filePath, lines, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
1506
|
-
return report;
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
// ── Lifecycle transitions ────────────────────────────────────────
|
|
1510
|
-
async transition(reportId, to, actor, opts) {
|
|
1511
|
-
return withFileLock(this.filePath, async () => {
|
|
1512
|
-
const all = await this._readAll();
|
|
1513
|
-
const entry = all.find((e) => e.report.id === reportId);
|
|
1514
|
-
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
1515
|
-
const from = this._materialize(entry).lifecycle;
|
|
1516
|
-
validateReportTransition(from, to);
|
|
1517
|
-
const event = {
|
|
1518
|
-
id: randomUUID37(),
|
|
1519
|
-
reportId,
|
|
1520
|
-
eventType: reportEventTypeFor(to),
|
|
1521
|
-
fromLifecycle: from,
|
|
1522
|
-
toLifecycle: to,
|
|
1523
|
-
actorId: actor.id,
|
|
1524
|
-
actorKind: actor.kind,
|
|
1525
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1526
|
-
reason: opts?.reason
|
|
1527
|
-
};
|
|
1528
|
-
entry.report.lifecycle = to;
|
|
1529
|
-
await fsp39.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL2, {
|
|
1530
|
-
encoding: "utf8",
|
|
1531
|
-
mode: SECRET_FILE_MODE
|
|
1532
|
-
});
|
|
1533
|
-
return { ...entry.report };
|
|
1534
|
-
});
|
|
1535
|
-
}
|
|
1536
|
-
async addNote(reportId, actor, note) {
|
|
1537
|
-
return withFileLock(this.filePath, async () => {
|
|
1538
|
-
const all = await this._readAll();
|
|
1539
|
-
const entry = all.find((e) => e.report.id === reportId);
|
|
1540
|
-
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
1541
|
-
const materialized = this._materialize(entry);
|
|
1542
|
-
const event = {
|
|
1543
|
-
id: randomUUID37(),
|
|
1544
|
-
reportId,
|
|
1545
|
-
eventType: "note_added",
|
|
1546
|
-
fromLifecycle: materialized.lifecycle,
|
|
1547
|
-
toLifecycle: materialized.lifecycle,
|
|
1548
|
-
actorId: actor.id,
|
|
1549
|
-
actorKind: actor.kind,
|
|
1550
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1551
|
-
reason: note
|
|
1552
|
-
};
|
|
1553
|
-
await fsp39.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL2, {
|
|
1554
|
-
encoding: "utf8",
|
|
1555
|
-
mode: SECRET_FILE_MODE
|
|
1556
|
-
});
|
|
1557
|
-
return materialized;
|
|
1558
|
-
});
|
|
1559
|
-
}
|
|
1560
|
-
// ── Query ────────────────────────────────────────────────────────
|
|
1561
|
-
async list(opts) {
|
|
1562
|
-
const all = await this._readAll();
|
|
1563
|
-
let reports = all.map((e) => this._materialize(e));
|
|
1564
|
-
if (opts?.statuses && opts.statuses.length > 0) {
|
|
1565
|
-
const set = new Set(opts.statuses);
|
|
1566
|
-
reports = reports.filter((r) => set.has(r.lifecycle));
|
|
1567
|
-
}
|
|
1568
|
-
if (opts?.sources && opts.sources.length > 0) {
|
|
1569
|
-
const set = new Set(opts.sources);
|
|
1570
|
-
reports = reports.filter((r) => set.has(r.source));
|
|
1571
|
-
}
|
|
1572
|
-
reports.sort((a, b) => b.reviewedAt.localeCompare(a.reviewedAt));
|
|
1573
|
-
const limit = opts?.limit ?? REPORT_DEFAULT_PAGE_SIZE;
|
|
1574
|
-
return reports.slice(0, limit);
|
|
1575
|
-
}
|
|
1576
|
-
async get(id) {
|
|
1577
|
-
const all = await this._readAll();
|
|
1578
|
-
const entry = all.find((e) => e.report.id === id);
|
|
1579
|
-
return entry ? this._materialize(entry) : null;
|
|
1580
|
-
}
|
|
1581
|
-
async getEvents(reportId) {
|
|
1582
|
-
const all = await this._readAllLines();
|
|
1583
|
-
const events = [];
|
|
1584
|
-
for (const line of all) {
|
|
1585
|
-
try {
|
|
1586
|
-
const parsed = JSON.parse(line);
|
|
1587
|
-
if (this._isEventRecord(parsed) && parsed.data.reportId === reportId) {
|
|
1588
|
-
events.push(parsed.data);
|
|
1589
|
-
}
|
|
1590
|
-
} catch {
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
return events.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1594
|
-
}
|
|
1595
|
-
// ── Compaction ───────────────────────────────────────────────────
|
|
1596
|
-
async compact(opts) {
|
|
1597
|
-
return withFileLock(this.filePath, async () => {
|
|
1598
|
-
const maxAge = opts?.maxAgeMs ?? REPORT_RETENTION_MS;
|
|
1599
|
-
const now = Date.now();
|
|
1600
|
-
const all = await this._readAll();
|
|
1601
|
-
const kept = [];
|
|
1602
|
-
let removed = 0;
|
|
1603
|
-
let eventsFolded = 0;
|
|
1604
|
-
for (const entry of all) {
|
|
1605
|
-
const age = now - new Date(entry.report.reviewedAt).getTime();
|
|
1606
|
-
const materialized = this._materialize(entry);
|
|
1607
|
-
const isTerminal = materialized.lifecycle === "completed" || materialized.lifecycle === "skipped";
|
|
1608
|
-
if (isTerminal && age > maxAge) {
|
|
1609
|
-
removed++;
|
|
1610
|
-
eventsFolded += entry.events.length;
|
|
1611
|
-
continue;
|
|
1612
|
-
}
|
|
1613
|
-
const oldEvents = entry.events.filter(
|
|
1614
|
-
(ev) => now - new Date(ev.timestamp).getTime() > maxAge
|
|
1615
|
-
);
|
|
1616
|
-
if (oldEvents.length > 1) {
|
|
1617
|
-
eventsFolded += oldEvents.length - 1;
|
|
1618
|
-
const newestOld = oldEvents.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1619
|
-
entry.events = [
|
|
1620
|
-
newestOld,
|
|
1621
|
-
...entry.events.filter((ev) => now - new Date(ev.timestamp).getTime() <= maxAge)
|
|
1622
|
-
];
|
|
1623
|
-
}
|
|
1624
|
-
kept.push({ record: { __report: 1, data: entry.report }, events: entry.events });
|
|
1625
|
-
}
|
|
1626
|
-
const lines = [];
|
|
1627
|
-
for (const { record, events } of kept) {
|
|
1628
|
-
if (record) lines.push(JSON.stringify(record));
|
|
1629
|
-
for (const ev of events) {
|
|
1630
|
-
lines.push(JSON.stringify({ __reportEvent: 1, data: ev }));
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
lines.push(
|
|
1634
|
-
JSON.stringify({
|
|
1635
|
-
__reportCompact: 1,
|
|
1636
|
-
compactedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1637
|
-
removedReports: removed,
|
|
1638
|
-
foldedEvents: eventsFolded
|
|
1639
|
-
})
|
|
1640
|
-
);
|
|
1641
|
-
await atomicWrite(this.filePath, lines.join(NL2) + NL2, { mode: 384 });
|
|
1642
|
-
return { removed, eventsFolded };
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
// ── Private helpers ──────────────────────────────────────────────
|
|
1646
|
-
async _readAllLines() {
|
|
1647
|
-
try {
|
|
1648
|
-
const raw = await fsp39.readFile(this.filePath, "utf8");
|
|
1649
|
-
return raw.split(NL2).filter((l) => l.trim().length > 0);
|
|
1650
|
-
} catch (err) {
|
|
1651
|
-
if (err.code === "ENOENT") return [];
|
|
1652
|
-
throw err;
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
_isReportRecord(v) {
|
|
1656
|
-
return typeof v === "object" && v !== null && v["__report"] === 1;
|
|
1657
|
-
}
|
|
1658
|
-
_isEventRecord(v) {
|
|
1659
|
-
return typeof v === "object" && v !== null && v["__reportEvent"] === 1;
|
|
1660
|
-
}
|
|
1661
|
-
async _readAll() {
|
|
1662
|
-
const lines = await this._readAllLines();
|
|
1663
|
-
const reports = /* @__PURE__ */ new Map();
|
|
1664
|
-
const eventsMap = /* @__PURE__ */ new Map();
|
|
1665
|
-
for (const line of lines) {
|
|
1666
|
-
try {
|
|
1667
|
-
const parsed = JSON.parse(line);
|
|
1668
|
-
if (this._isReportRecord(parsed)) {
|
|
1669
|
-
reports.set(parsed.data.id, parsed.data);
|
|
1670
|
-
if (!eventsMap.has(parsed.data.id)) eventsMap.set(parsed.data.id, []);
|
|
1671
|
-
} else if (this._isEventRecord(parsed)) {
|
|
1672
|
-
const list = eventsMap.get(parsed.data.reportId);
|
|
1673
|
-
if (list) list.push(parsed.data);
|
|
1674
|
-
}
|
|
1675
|
-
} catch {
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
return Array.from(reports.entries()).map(([id, report]) => ({
|
|
1679
|
-
report,
|
|
1680
|
-
events: eventsMap.get(id) ?? []
|
|
1681
|
-
}));
|
|
1682
|
-
}
|
|
1683
|
-
async _findRecord(id) {
|
|
1684
|
-
const all = await this._readAll();
|
|
1685
|
-
const entry = all.find((e) => e.report.id === id);
|
|
1686
|
-
return entry ? this._materialize(entry) : null;
|
|
1687
|
-
}
|
|
1688
|
-
_materialize(entry) {
|
|
1689
|
-
if (entry.events.length === 0) return { ...entry.report };
|
|
1690
|
-
const sorted = [...entry.events].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1691
|
-
const latest = sorted[sorted.length - 1];
|
|
1692
|
-
if (latest.eventType !== "note_added") {
|
|
1693
|
-
return { ...entry.report, lifecycle: latest.toLifecycle };
|
|
1694
|
-
}
|
|
1695
|
-
return { ...entry.report };
|
|
1696
|
-
}
|
|
1697
|
-
};
|
|
1698
|
-
}
|
|
1699
|
-
});
|
|
1700
|
-
|
|
1701
1701
|
// src/boot.ts
|
|
1702
1702
|
import * as fs9 from "node:fs/promises";
|
|
1703
1703
|
import * as os3 from "node:os";
|
|
@@ -3524,7 +3524,7 @@ var CONFIG_BEHAVIOR_DEFAULTS = {
|
|
|
3524
3524
|
// DEFAULT_STATUSLINE_MODE (packages/tui/src/components/settings-picker-model.ts).
|
|
3525
3525
|
statuslineMode: "minimum",
|
|
3526
3526
|
thinkingWord: DEFAULT_TUI_THINKING_WORD,
|
|
3527
|
-
showAgentSwarmPanel:
|
|
3527
|
+
showAgentSwarmPanel: "bottom"
|
|
3528
3528
|
},
|
|
3529
3529
|
circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG },
|
|
3530
3530
|
modelRuntime: {
|
|
@@ -4014,13 +4014,32 @@ function inlineDefinition(model) {
|
|
|
4014
4014
|
if (maxOutput !== void 0) definition.maxOutput = maxOutput;
|
|
4015
4015
|
const capabilities = readCapabilities(model);
|
|
4016
4016
|
if (capabilities) definition.capabilities = capabilities;
|
|
4017
|
+
const { id: _id, ...rest } = model;
|
|
4018
|
+
if (Object.keys(rest).length > 0) {
|
|
4019
|
+
definition.modelsDev = rest;
|
|
4020
|
+
}
|
|
4017
4021
|
return definition;
|
|
4018
4022
|
}
|
|
4019
4023
|
function mergeDefinitions(base, patch) {
|
|
4024
|
+
const mergedModelsDev = (() => {
|
|
4025
|
+
if (!base?.modelsDev && !patch.modelsDev) return void 0;
|
|
4026
|
+
const baseMd = base?.modelsDev ?? {};
|
|
4027
|
+
const patchMd = patch.modelsDev ?? {};
|
|
4028
|
+
const result = { ...baseMd, ...patchMd };
|
|
4029
|
+
for (const nestedKey of ["limit", "cost", "modalities"]) {
|
|
4030
|
+
const bn = baseMd?.[nestedKey];
|
|
4031
|
+
const pn = patchMd?.[nestedKey];
|
|
4032
|
+
if (bn || pn) {
|
|
4033
|
+
result[nestedKey] = { ...bn ?? {}, ...pn ?? {} };
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
return result;
|
|
4037
|
+
})();
|
|
4020
4038
|
return {
|
|
4021
4039
|
...base,
|
|
4022
4040
|
...patch,
|
|
4023
|
-
...base?.capabilities || patch.capabilities ? { capabilities: { ...base?.capabilities, ...patch.capabilities } } : {}
|
|
4041
|
+
...base?.capabilities || patch.capabilities ? { capabilities: { ...base?.capabilities, ...patch.capabilities } } : {},
|
|
4042
|
+
...mergedModelsDev ? { modelsDev: mergedModelsDev } : {}
|
|
4024
4043
|
};
|
|
4025
4044
|
}
|
|
4026
4045
|
function normalizeInlineProviderModels(config, warn) {
|
|
@@ -15021,6 +15040,49 @@ function quoteWin32CmdArg(arg) {
|
|
|
15021
15040
|
return `"${arg}"`;
|
|
15022
15041
|
}
|
|
15023
15042
|
|
|
15043
|
+
// src/utils/sage-output-block.ts
|
|
15044
|
+
var SAGE_INJECTOR_HEADINGS = /* @__PURE__ */ new Set([
|
|
15045
|
+
"--- SAGE: task-aware project knowledge (Memory Injector) ---",
|
|
15046
|
+
"--- SAGE: related project knowledge (Memory Injector) ---"
|
|
15047
|
+
]);
|
|
15048
|
+
var SAGE_MEMORY_LINE = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*<\/memory>(?: .*)?$/;
|
|
15049
|
+
var SAGE_MEMORY_LINE_TRUNCATED = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*…$/;
|
|
15050
|
+
function splitSageOutputBlock(output) {
|
|
15051
|
+
if (!output.includes("--- SAGE: ")) return { body: output, sageLines: [] };
|
|
15052
|
+
const lines = output.split("\n");
|
|
15053
|
+
for (let sageIdx = lines.length - 1; sageIdx >= 0; sageIdx--) {
|
|
15054
|
+
if (!SAGE_INJECTOR_HEADINGS.has(lines[sageIdx] ?? "")) continue;
|
|
15055
|
+
const candidate = lines.slice(sageIdx);
|
|
15056
|
+
if (candidate.length < 2) continue;
|
|
15057
|
+
const memoryLines = candidate.slice(1).filter((line) => line.trim().length > 0);
|
|
15058
|
+
if (memoryLines.length === 0) continue;
|
|
15059
|
+
const bodyOk = memoryLines.every(
|
|
15060
|
+
(line, index) => SAGE_MEMORY_LINE.test(line) || index === memoryLines.length - 1 && SAGE_MEMORY_LINE_TRUNCATED.test(line)
|
|
15061
|
+
);
|
|
15062
|
+
if (!bodyOk) continue;
|
|
15063
|
+
let end = candidate.length;
|
|
15064
|
+
while (end > 1 && candidate[end - 1].trim().length === 0) end--;
|
|
15065
|
+
return {
|
|
15066
|
+
body: lines.slice(0, sageIdx).join("\n").trimEnd(),
|
|
15067
|
+
sageLines: candidate.slice(0, end)
|
|
15068
|
+
};
|
|
15069
|
+
}
|
|
15070
|
+
return { body: output, sageLines: [] };
|
|
15071
|
+
}
|
|
15072
|
+
function capSageLines(sageLines, maxChars) {
|
|
15073
|
+
if (sageLines.length < 2) return [];
|
|
15074
|
+
const header = sageLines[0];
|
|
15075
|
+
if (header.length >= maxChars) return [];
|
|
15076
|
+
const out = [header];
|
|
15077
|
+
let used = header.length;
|
|
15078
|
+
for (const line of sageLines.slice(1)) {
|
|
15079
|
+
if (used + 1 + line.length > maxChars) break;
|
|
15080
|
+
out.push(line);
|
|
15081
|
+
used += 1 + line.length;
|
|
15082
|
+
}
|
|
15083
|
+
return out.length > 1 ? out : [];
|
|
15084
|
+
}
|
|
15085
|
+
|
|
15024
15086
|
// src/chronicle/sqlite-query.ts
|
|
15025
15087
|
var MAX_LIMIT = 1e4;
|
|
15026
15088
|
function encodeCursor2(cursor) {
|
|
@@ -30654,8 +30716,16 @@ function resolveSubagentModelTarget(config, role, opts = {}) {
|
|
|
30654
30716
|
const resolution = resolveModelMatrixResolution(config.modelMatrix, role);
|
|
30655
30717
|
const matrixTarget = resolveModelTargetFromEntry(config, resolution?.entry);
|
|
30656
30718
|
const implementationTarget = opts.implementationTarget ?? resolveImplementationModelTarget(config);
|
|
30719
|
+
const isAvailable = (providerId, model) => {
|
|
30720
|
+
if (!opts.statusTracker) return true;
|
|
30721
|
+
if (!providerId || !model) return true;
|
|
30722
|
+
return opts.statusTracker.isAvailable(providerId, model);
|
|
30723
|
+
};
|
|
30724
|
+
const matrixEffective = materializeTarget(config, matrixTarget);
|
|
30725
|
+
const matrixIsAvailable = !matrixEffective || isAvailable(matrixEffective.provider, matrixEffective.model);
|
|
30657
30726
|
if (!roleNeedsIndependentReviewModel(role)) {
|
|
30658
30727
|
if (!matrixTarget) return void 0;
|
|
30728
|
+
if (!matrixIsAvailable) return void 0;
|
|
30659
30729
|
return {
|
|
30660
30730
|
...matrixTarget,
|
|
30661
30731
|
source: "matrix",
|
|
@@ -30664,16 +30734,27 @@ function resolveSubagentModelTarget(config, role, opts = {}) {
|
|
|
30664
30734
|
}
|
|
30665
30735
|
const matrixRef = materializeTarget(config, matrixTarget);
|
|
30666
30736
|
if (resolution?.source === "role" || resolution?.source === "phase") {
|
|
30667
|
-
|
|
30737
|
+
if (!matrixTarget) return void 0;
|
|
30738
|
+
if (!matrixIsAvailable) return void 0;
|
|
30739
|
+
return {
|
|
30740
|
+
...matrixTarget,
|
|
30741
|
+
source: "matrix",
|
|
30742
|
+
matrixSource: resolution.source
|
|
30743
|
+
};
|
|
30668
30744
|
}
|
|
30669
30745
|
if (matrixRef && !sameModelReference(matrixRef, implementationTarget)) {
|
|
30746
|
+
if (!matrixIsAvailable) return void 0;
|
|
30670
30747
|
return {
|
|
30671
30748
|
...matrixTarget ?? {},
|
|
30672
30749
|
source: "matrix",
|
|
30673
30750
|
matrixSource: resolution?.source
|
|
30674
30751
|
};
|
|
30675
30752
|
}
|
|
30676
|
-
const diverse =
|
|
30753
|
+
const diverse = chooseDiverseModelTargetWithTracker(
|
|
30754
|
+
config,
|
|
30755
|
+
implementationTarget,
|
|
30756
|
+
opts.statusTracker
|
|
30757
|
+
);
|
|
30677
30758
|
if (diverse) {
|
|
30678
30759
|
return {
|
|
30679
30760
|
provider: diverse.provider,
|
|
@@ -30687,6 +30768,7 @@ function resolveSubagentModelTarget(config, role, opts = {}) {
|
|
|
30687
30768
|
};
|
|
30688
30769
|
}
|
|
30689
30770
|
if (!matrixTarget) return void 0;
|
|
30771
|
+
if (!matrixIsAvailable) return void 0;
|
|
30690
30772
|
return {
|
|
30691
30773
|
...matrixTarget,
|
|
30692
30774
|
source: "matrix",
|
|
@@ -30723,6 +30805,14 @@ function chooseDiverseModelTarget(config, avoid) {
|
|
|
30723
30805
|
candidates.sort((a, b) => modelDiversityScore(b, avoid) - modelDiversityScore(a, avoid));
|
|
30724
30806
|
return candidates[0];
|
|
30725
30807
|
}
|
|
30808
|
+
function chooseDiverseModelTargetWithTracker(config, avoid, tracker) {
|
|
30809
|
+
if (!tracker) return chooseDiverseModelTarget(config, avoid);
|
|
30810
|
+
const ranked = collectConfiguredModelTargets(config).filter((candidate) => !sameModelReference(candidate, avoid)).sort((a, b) => modelDiversityScore(b, avoid) - modelDiversityScore(a, avoid));
|
|
30811
|
+
for (const candidate of ranked) {
|
|
30812
|
+
if (tracker.isAvailable(candidate.provider, candidate.model)) return candidate;
|
|
30813
|
+
}
|
|
30814
|
+
return void 0;
|
|
30815
|
+
}
|
|
30726
30816
|
function collectConfiguredModelTargets(config) {
|
|
30727
30817
|
const seen = /* @__PURE__ */ new Set();
|
|
30728
30818
|
const out = [];
|
|
@@ -37785,6 +37875,36 @@ function isMailboxLeader(agentId, role) {
|
|
|
37785
37875
|
function isMailboxMessageVisibleTo(message, agentId, role) {
|
|
37786
37876
|
return message.audience !== "leaders" || isMailboxLeader(agentId, role);
|
|
37787
37877
|
}
|
|
37878
|
+
function isAffectedBySessionAffinity(message) {
|
|
37879
|
+
return message.sessionAffinity !== void 0;
|
|
37880
|
+
}
|
|
37881
|
+
async function acceptMailboxMessageForSession(message, currentSessionId, ctx) {
|
|
37882
|
+
if (!isAffectedBySessionAffinity(message)) return true;
|
|
37883
|
+
const affinity = message.sessionAffinity;
|
|
37884
|
+
if (affinity === null || typeof affinity !== "object" || Array.isArray(affinity)) {
|
|
37885
|
+
return false;
|
|
37886
|
+
}
|
|
37887
|
+
if (affinity.sessionId !== void 0 && typeof affinity.sessionId !== "string" || affinity.reportId !== void 0 && typeof affinity.reportId !== "string") {
|
|
37888
|
+
return false;
|
|
37889
|
+
}
|
|
37890
|
+
if (!currentSessionId) {
|
|
37891
|
+
return ctx?.allowUnscoped === true;
|
|
37892
|
+
}
|
|
37893
|
+
if (typeof affinity.sessionId === "string" && affinity.sessionId.length > 0) {
|
|
37894
|
+
if (affinity.sessionId !== currentSessionId) return false;
|
|
37895
|
+
return true;
|
|
37896
|
+
}
|
|
37897
|
+
if (affinity.reportId && ctx?.resolveChimeraReportSessionId) {
|
|
37898
|
+
try {
|
|
37899
|
+
const resolved = await ctx.resolveChimeraReportSessionId(affinity.reportId);
|
|
37900
|
+
if (resolved === currentSessionId) return true;
|
|
37901
|
+
if (resolved !== void 0) return false;
|
|
37902
|
+
} catch {
|
|
37903
|
+
}
|
|
37904
|
+
}
|
|
37905
|
+
if (ctx?.allowUnscoped === true) return true;
|
|
37906
|
+
return false;
|
|
37907
|
+
}
|
|
37788
37908
|
function validateSendType(type, to) {
|
|
37789
37909
|
if (type === "control") {
|
|
37790
37910
|
throw new TypeError(
|
|
@@ -38409,7 +38529,7 @@ function makeMailboxTool(opts = {}) {
|
|
|
38409
38529
|
case "ack":
|
|
38410
38530
|
return executeAck(mb, callerId, i);
|
|
38411
38531
|
case "query":
|
|
38412
|
-
return executeQuery(mb, callerId, identity2.role, i);
|
|
38532
|
+
return executeQuery(mb, callerId, callerSessionId, identity2.role, i);
|
|
38413
38533
|
case "status":
|
|
38414
38534
|
return executeStatus(mb);
|
|
38415
38535
|
case "online":
|
|
@@ -38438,12 +38558,18 @@ async function executeCheck(mb, agentId, sessionId, aliases, role, i) {
|
|
|
38438
38558
|
)
|
|
38439
38559
|
);
|
|
38440
38560
|
const seen = /* @__PURE__ */ new Set();
|
|
38441
|
-
const
|
|
38561
|
+
const candidates = batches.flat().filter((m) => {
|
|
38442
38562
|
if (seen.has(m.id)) return false;
|
|
38443
38563
|
if (!isMailboxMessageVisibleTo(m, agentId, role)) return false;
|
|
38444
38564
|
seen.add(m.id);
|
|
38445
38565
|
return true;
|
|
38446
38566
|
});
|
|
38567
|
+
const messages = [];
|
|
38568
|
+
for (const m of candidates) {
|
|
38569
|
+
if (await acceptMailboxMessageForSession(m, sessionId)) {
|
|
38570
|
+
messages.push(m);
|
|
38571
|
+
}
|
|
38572
|
+
}
|
|
38447
38573
|
const acked = markRead || completed ? await mb.ackMany({
|
|
38448
38574
|
acks: messages.map((m) => ({
|
|
38449
38575
|
messageId: m.id,
|
|
@@ -38524,7 +38650,7 @@ async function executeAck(mb, agentId, i) {
|
|
|
38524
38650
|
summary: `Message ${messageId} acknowledged. Read by ${Object.keys(updated.readBy).length} agent(s), Completed: ${updated.completed}.`
|
|
38525
38651
|
};
|
|
38526
38652
|
}
|
|
38527
|
-
async function executeQuery(mb, agentId, role, i) {
|
|
38653
|
+
async function executeQuery(mb, agentId, sessionId, role, i) {
|
|
38528
38654
|
const limit = i.limit ?? 50;
|
|
38529
38655
|
const messages = await mb.query({
|
|
38530
38656
|
to: i.to,
|
|
@@ -38537,7 +38663,12 @@ async function executeQuery(mb, agentId, role, i) {
|
|
|
38537
38663
|
sessionId: i.sessionId,
|
|
38538
38664
|
limit
|
|
38539
38665
|
});
|
|
38540
|
-
const visible =
|
|
38666
|
+
const visible = [];
|
|
38667
|
+
for (const message of messages) {
|
|
38668
|
+
if (isMailboxMessageVisibleTo(message, agentId, role) && await acceptMailboxMessageForSession(message, sessionId)) {
|
|
38669
|
+
visible.push(message);
|
|
38670
|
+
}
|
|
38671
|
+
}
|
|
38541
38672
|
return { ok: true, count: visible.length, messages: visible, summary: `${visible.length} message(s).` };
|
|
38542
38673
|
}
|
|
38543
38674
|
async function executeStatus(mb) {
|
|
@@ -39968,6 +40099,14 @@ var SEND_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
|
|
|
39968
40099
|
"senderSessionId",
|
|
39969
40100
|
"ttlMs",
|
|
39970
40101
|
"taskContext"
|
|
40102
|
+
// 'sessionAffinity' is NOT in the allow-list. The trust-line contract:
|
|
40103
|
+
// session-affinity tokens are stamped ONLY by trusted internal callers
|
|
40104
|
+
// (chimera/auto-review pipelines) that call `mailbox.send()` directly
|
|
40105
|
+
// and never go through this boundary codec. Allowing the field at the
|
|
40106
|
+
// boundary would let any actor with `mail.send.informational` stamp
|
|
40107
|
+
// another session's id and bypass the receiver-side filter. The
|
|
40108
|
+
// receiver trusts the sender-asserted `sessionId`; the boundary must
|
|
40109
|
+
// refuse the field entirely.
|
|
39971
40110
|
]);
|
|
39972
40111
|
var ACK_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
|
|
39973
40112
|
"messageId",
|
|
@@ -40004,7 +40143,15 @@ function parseMailboxSendInput(payload, actor) {
|
|
|
40004
40143
|
const priority = validatePriority(rawPriority);
|
|
40005
40144
|
const audience = validateAudience(payload["audience"]);
|
|
40006
40145
|
const replyTo = optionalString2(payload, "replyTo", "send");
|
|
40007
|
-
return {
|
|
40146
|
+
return {
|
|
40147
|
+
to,
|
|
40148
|
+
type: typeResult.type,
|
|
40149
|
+
subject: subject2,
|
|
40150
|
+
body,
|
|
40151
|
+
priority,
|
|
40152
|
+
audience,
|
|
40153
|
+
replyTo
|
|
40154
|
+
};
|
|
40008
40155
|
}
|
|
40009
40156
|
function parseMailboxQueryInput(payload, actor) {
|
|
40010
40157
|
assertCapability(actor, "mail.read.self", "query");
|
|
@@ -48740,6 +48887,7 @@ function createHqPublisherFromEnv(options) {
|
|
|
48740
48887
|
|
|
48741
48888
|
// src/mailbox-attach.ts
|
|
48742
48889
|
init_error();
|
|
48890
|
+
init_review_report_store();
|
|
48743
48891
|
|
|
48744
48892
|
// src/core/mailbox-loop.ts
|
|
48745
48893
|
init_error();
|
|
@@ -49122,9 +49270,43 @@ function attachMailboxCheckerInner(a, source) {
|
|
|
49122
49270
|
agentId: () => ensureRegistered(),
|
|
49123
49271
|
role: () => resolveMailboxIdentity(a.ctx).role,
|
|
49124
49272
|
aliases: [baseIdOf()],
|
|
49125
|
-
sessionId: () => a.ctx.session.id
|
|
49273
|
+
sessionId: () => a.ctx.session.id,
|
|
49274
|
+
// ACK is deferred to the session-affinity wrapper so messages dropped
|
|
49275
|
+
// by the filter are NOT marked as read by this agent.
|
|
49276
|
+
ack: false
|
|
49126
49277
|
};
|
|
49127
49278
|
const checkMailbox2 = createMailboxChecker(mailboxCheckerOptions);
|
|
49279
|
+
const reportStore = new JsonlReportStore(
|
|
49280
|
+
resolveProjectDir(a.ctx.projectRoot, wstackGlobalRoot())
|
|
49281
|
+
);
|
|
49282
|
+
const sessionAffinityCtx = {
|
|
49283
|
+
resolveChimeraReportSessionId: async (reportId) => (await reportStore.get(reportId))?.sessionId
|
|
49284
|
+
};
|
|
49285
|
+
const applySessionAffinityFilter = (checker, ack) => {
|
|
49286
|
+
return async () => {
|
|
49287
|
+
const currentSessionId = a.ctx.session.id;
|
|
49288
|
+
const agentId = ack ? ensureRegistered() : null;
|
|
49289
|
+
const messages = await checker();
|
|
49290
|
+
const filtered = [];
|
|
49291
|
+
for (const m of messages) {
|
|
49292
|
+
if (await acceptMailboxMessageForSession(m, currentSessionId, sessionAffinityCtx)) {
|
|
49293
|
+
filtered.push(m);
|
|
49294
|
+
}
|
|
49295
|
+
}
|
|
49296
|
+
if (ack && filtered.length > 0 && agentId) {
|
|
49297
|
+
void getMailbox().ackMany({
|
|
49298
|
+
acks: filtered.map((m) => ({
|
|
49299
|
+
messageId: m.id,
|
|
49300
|
+
readerId: agentId,
|
|
49301
|
+
read: true
|
|
49302
|
+
}))
|
|
49303
|
+
}).catch(() => {
|
|
49304
|
+
});
|
|
49305
|
+
}
|
|
49306
|
+
return filtered;
|
|
49307
|
+
};
|
|
49308
|
+
};
|
|
49309
|
+
const sessionScopedCheckMailbox = applySessionAffinityFilter(checkMailbox2, true);
|
|
49128
49310
|
const checkMailboxAwareness = createMailboxChecker({
|
|
49129
49311
|
...mailboxCheckerOptions,
|
|
49130
49312
|
// Exclude out-of-band types (control) from awareness polling.
|
|
@@ -49134,6 +49316,7 @@ function attachMailboxCheckerInner(a, source) {
|
|
|
49134
49316
|
include: (m) => !MAILBOX_TYPE_PROPERTIES[m.type]?.outOfBand,
|
|
49135
49317
|
ack: false
|
|
49136
49318
|
});
|
|
49319
|
+
const sessionScopedCheckMailboxAwareness = applySessionAffinityFilter(checkMailboxAwareness, false);
|
|
49137
49320
|
const AWARENESS_FALLBACK_INTERVAL_MS = MAILBOX_AWARENESS_INTERVAL_MS;
|
|
49138
49321
|
const AWARENESS_PUSH_DEBOUNCE_MS = 500;
|
|
49139
49322
|
let pollInFlight = false;
|
|
@@ -49143,7 +49326,7 @@ function attachMailboxCheckerInner(a, source) {
|
|
|
49143
49326
|
if (awarenessDisposed || pollInFlight) return;
|
|
49144
49327
|
pollInFlight = true;
|
|
49145
49328
|
try {
|
|
49146
|
-
const messages = await
|
|
49329
|
+
const messages = await sessionScopedCheckMailboxAwareness();
|
|
49147
49330
|
if (!awarenessDisposed && messages.length > 0 && a.ctx.meta["coordinationContextMode"] !== "background") {
|
|
49148
49331
|
setBtwNote(a.ctx, buildMailboxBtwAwarenessBlock(messages).text);
|
|
49149
49332
|
}
|
|
@@ -49176,7 +49359,7 @@ function attachMailboxCheckerInner(a, source) {
|
|
|
49176
49359
|
if (pushDebounceTimer !== null) clearTimeout(pushDebounceTimer);
|
|
49177
49360
|
pushUnsub?.();
|
|
49178
49361
|
});
|
|
49179
|
-
return
|
|
49362
|
+
return sessionScopedCheckMailbox;
|
|
49180
49363
|
}
|
|
49181
49364
|
function attachFleetPulse(a, cfg) {
|
|
49182
49365
|
if (!a.ctx.projectRoot || cfg?.enabled === false) {
|
|
@@ -49267,7 +49450,7 @@ function requestLimitExtension(opts) {
|
|
|
49267
49450
|
init_errors();
|
|
49268
49451
|
|
|
49269
49452
|
// src/core/streaming-response-builder.ts
|
|
49270
|
-
import { randomUUID as
|
|
49453
|
+
import { randomUUID as randomUUID29 } from "node:crypto";
|
|
49271
49454
|
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
49272
49455
|
function buildResponse(state) {
|
|
49273
49456
|
const content = [];
|
|
@@ -49327,7 +49510,7 @@ function handleContentBlockStart(state, ev) {
|
|
|
49327
49510
|
state.textBuffers.push("");
|
|
49328
49511
|
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
49329
49512
|
} else if (kind === "tool_use") {
|
|
49330
|
-
const id = ev.id ??
|
|
49513
|
+
const id = ev.id ?? randomUUID29();
|
|
49331
49514
|
state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
|
|
49332
49515
|
state.blockOrder.push({ kind: "tool", id });
|
|
49333
49516
|
state.currentTextIndex = -1;
|
|
@@ -49562,7 +49745,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
49562
49745
|
|
|
49563
49746
|
// src/observability/network-telemetry.ts
|
|
49564
49747
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
49565
|
-
import { createHash as createHash23, randomUUID as
|
|
49748
|
+
import { createHash as createHash23, randomUUID as randomUUID30 } from "node:crypto";
|
|
49566
49749
|
import { channel } from "node:diagnostics_channel";
|
|
49567
49750
|
var storage = new AsyncLocalStorage();
|
|
49568
49751
|
var requests = /* @__PURE__ */ new WeakMap();
|
|
@@ -49599,7 +49782,7 @@ function subscribe() {
|
|
|
49599
49782
|
const requestBytes = numberField2(request, "contentLength");
|
|
49600
49783
|
const state = {
|
|
49601
49784
|
...context,
|
|
49602
|
-
requestId:
|
|
49785
|
+
requestId: randomUUID30(),
|
|
49603
49786
|
...target,
|
|
49604
49787
|
...requestBytes !== void 0 ? { requestBytes } : {},
|
|
49605
49788
|
startedAt,
|
|
@@ -49734,7 +49917,7 @@ function hash3(value) {
|
|
|
49734
49917
|
}
|
|
49735
49918
|
|
|
49736
49919
|
// src/core/provider-runner.ts
|
|
49737
|
-
import { randomUUID as
|
|
49920
|
+
import { randomUUID as randomUUID31 } from "node:crypto";
|
|
49738
49921
|
function providerLogCtx(p, r) {
|
|
49739
49922
|
return {
|
|
49740
49923
|
providerId: p.id,
|
|
@@ -49746,11 +49929,11 @@ function providerLogCtx(p, r) {
|
|
|
49746
49929
|
}
|
|
49747
49930
|
async function runProviderWithRetry(opts) {
|
|
49748
49931
|
const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
|
|
49749
|
-
const logicalRequestId =
|
|
49932
|
+
const logicalRequestId = randomUUID31();
|
|
49750
49933
|
const promptManifest = createChroniclePromptManifest(request);
|
|
49751
49934
|
let attempt = 0;
|
|
49752
49935
|
for (; ; ) {
|
|
49753
|
-
const attemptId =
|
|
49936
|
+
const attemptId = randomUUID31();
|
|
49754
49937
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49755
49938
|
const startedNs = process.hrtime.bigint();
|
|
49756
49939
|
const correlation = {
|
|
@@ -50667,51 +50850,6 @@ ${text2}` : text2;
|
|
|
50667
50850
|
|
|
50668
50851
|
// src/core/agent-tools.ts
|
|
50669
50852
|
init_error();
|
|
50670
|
-
|
|
50671
|
-
// src/utils/sage-output-block.ts
|
|
50672
|
-
var SAGE_INJECTOR_HEADINGS = /* @__PURE__ */ new Set([
|
|
50673
|
-
"--- SAGE: task-aware project knowledge (Memory Injector) ---",
|
|
50674
|
-
"--- SAGE: related project knowledge (Memory Injector) ---"
|
|
50675
|
-
]);
|
|
50676
|
-
var SAGE_MEMORY_LINE = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*<\/memory>(?: .*)?$/;
|
|
50677
|
-
var SAGE_MEMORY_LINE_TRUNCATED = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*…$/;
|
|
50678
|
-
function splitSageOutputBlock(output) {
|
|
50679
|
-
if (!output.includes("--- SAGE: ")) return { body: output, sageLines: [] };
|
|
50680
|
-
const lines = output.split("\n");
|
|
50681
|
-
for (let sageIdx = lines.length - 1; sageIdx >= 0; sageIdx--) {
|
|
50682
|
-
if (!SAGE_INJECTOR_HEADINGS.has(lines[sageIdx] ?? "")) continue;
|
|
50683
|
-
const candidate = lines.slice(sageIdx);
|
|
50684
|
-
if (candidate.length < 2) continue;
|
|
50685
|
-
const memoryLines = candidate.slice(1).filter((line) => line.trim().length > 0);
|
|
50686
|
-
if (memoryLines.length === 0) continue;
|
|
50687
|
-
const bodyOk = memoryLines.every(
|
|
50688
|
-
(line, index) => SAGE_MEMORY_LINE.test(line) || index === memoryLines.length - 1 && SAGE_MEMORY_LINE_TRUNCATED.test(line)
|
|
50689
|
-
);
|
|
50690
|
-
if (!bodyOk) continue;
|
|
50691
|
-
let end = candidate.length;
|
|
50692
|
-
while (end > 1 && candidate[end - 1].trim().length === 0) end--;
|
|
50693
|
-
return {
|
|
50694
|
-
body: lines.slice(0, sageIdx).join("\n").trimEnd(),
|
|
50695
|
-
sageLines: candidate.slice(0, end)
|
|
50696
|
-
};
|
|
50697
|
-
}
|
|
50698
|
-
return { body: output, sageLines: [] };
|
|
50699
|
-
}
|
|
50700
|
-
function capSageLines(sageLines, maxChars) {
|
|
50701
|
-
if (sageLines.length < 2) return [];
|
|
50702
|
-
const header = sageLines[0];
|
|
50703
|
-
if (header.length >= maxChars) return [];
|
|
50704
|
-
const out = [header];
|
|
50705
|
-
let used = header.length;
|
|
50706
|
-
for (const line of sageLines.slice(1)) {
|
|
50707
|
-
if (used + 1 + line.length > maxChars) break;
|
|
50708
|
-
out.push(line);
|
|
50709
|
-
used += 1 + line.length;
|
|
50710
|
-
}
|
|
50711
|
-
return out.length > 1 ? out : [];
|
|
50712
|
-
}
|
|
50713
|
-
|
|
50714
|
-
// src/core/agent-tools.ts
|
|
50715
50853
|
var DIFF_TOOL_NAMES = /* @__PURE__ */ new Set(["edit", "write", "replace", "patch", "diff"]);
|
|
50716
50854
|
var DIFF_TOOL_EVENT_PREVIEW_MAX = 16e3;
|
|
50717
50855
|
var SAGE_EVENT_MAX = 2e3;
|
|
@@ -53274,7 +53412,7 @@ var AutonomousRunner = class {
|
|
|
53274
53412
|
// src/storage/goal-store.ts
|
|
53275
53413
|
init_atomic_write();
|
|
53276
53414
|
init_error();
|
|
53277
|
-
import * as
|
|
53415
|
+
import * as fsp29 from "node:fs/promises";
|
|
53278
53416
|
init_errors();
|
|
53279
53417
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
53280
53418
|
function goalFilePath(projectRoot) {
|
|
@@ -53284,7 +53422,7 @@ async function loadGoal(filePath, events, warn) {
|
|
|
53284
53422
|
const t0 = Date.now();
|
|
53285
53423
|
let raw;
|
|
53286
53424
|
try {
|
|
53287
|
-
raw = await
|
|
53425
|
+
raw = await fsp29.readFile(filePath, "utf8");
|
|
53288
53426
|
} catch (err) {
|
|
53289
53427
|
const code = err.code;
|
|
53290
53428
|
if (code === "ENOENT") {
|
|
@@ -53593,7 +53731,7 @@ var KNOWN_TOKEN_NAMES = Object.values(KNOWN_TOKEN_GROUPS).flat();
|
|
|
53593
53731
|
// src/execution/design-kit-loader.ts
|
|
53594
53732
|
import { existsSync as existsSync7 } from "node:fs";
|
|
53595
53733
|
import * as fs28 from "node:fs/promises";
|
|
53596
|
-
import * as
|
|
53734
|
+
import * as path68 from "node:path";
|
|
53597
53735
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
53598
53736
|
var KIT_FILE = "KIT.md";
|
|
53599
53737
|
var TOKENS_FILE = "tokens.json";
|
|
@@ -53699,7 +53837,7 @@ var DefaultDesignKitLoader = class {
|
|
|
53699
53837
|
}
|
|
53700
53838
|
for (const e of entries) {
|
|
53701
53839
|
if (!e.isDirectory()) continue;
|
|
53702
|
-
const kitFile =
|
|
53840
|
+
const kitFile = path68.join(dir, e.name, KIT_FILE);
|
|
53703
53841
|
try {
|
|
53704
53842
|
const raw = await fs28.readFile(kitFile, "utf8");
|
|
53705
53843
|
const fm = parseKitFrontmatter(raw);
|
|
@@ -53774,7 +53912,7 @@ var DefaultDesignKitLoader = class {
|
|
|
53774
53912
|
const m = await this.find(id);
|
|
53775
53913
|
let tokens;
|
|
53776
53914
|
if (m) {
|
|
53777
|
-
const tokensPath =
|
|
53915
|
+
const tokensPath = path68.join(path68.dirname(m.path), TOKENS_FILE);
|
|
53778
53916
|
try {
|
|
53779
53917
|
const raw = await fs28.readFile(tokensPath, "utf8");
|
|
53780
53918
|
tokens = JSON.parse(raw);
|
|
@@ -53836,12 +53974,12 @@ function mergeKitTokens(base, own) {
|
|
|
53836
53974
|
}
|
|
53837
53975
|
function resolveBundledDesignKitsDir() {
|
|
53838
53976
|
try {
|
|
53839
|
-
const here =
|
|
53977
|
+
const here = path68.dirname(fileURLToPath7(import.meta.url));
|
|
53840
53978
|
const candidates = [
|
|
53841
|
-
|
|
53842
|
-
|
|
53843
|
-
|
|
53844
|
-
|
|
53979
|
+
path68.join(here, "design-kits"),
|
|
53980
|
+
path68.join(here, "..", "design-kits"),
|
|
53981
|
+
path68.join(here, "..", "..", "design-kits"),
|
|
53982
|
+
path68.join(here, "..", "..", "..", "design-kits")
|
|
53845
53983
|
];
|
|
53846
53984
|
for (const c of candidates) {
|
|
53847
53985
|
if (existsSync7(c)) return c;
|
|
@@ -53880,10 +54018,10 @@ function _resetDesignKitLoaderMemo() {
|
|
|
53880
54018
|
// src/execution/design-project-store.ts
|
|
53881
54019
|
import { existsSync as existsSync8 } from "node:fs";
|
|
53882
54020
|
import * as fs29 from "node:fs/promises";
|
|
53883
|
-
import * as
|
|
54021
|
+
import * as path69 from "node:path";
|
|
53884
54022
|
var DESIGN_DIR = ".design";
|
|
53885
54023
|
function designProjectDir(projectRoot) {
|
|
53886
|
-
return
|
|
54024
|
+
return path69.join(projectRoot, DESIGN_DIR);
|
|
53887
54025
|
}
|
|
53888
54026
|
var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
|
|
53889
54027
|
var rulesCache = /* @__PURE__ */ new Map();
|
|
@@ -53898,7 +54036,7 @@ async function loadProjectDesignRules(projectRoot) {
|
|
|
53898
54036
|
let rules;
|
|
53899
54037
|
for (const name of RULE_FILES) {
|
|
53900
54038
|
try {
|
|
53901
|
-
const txt = await fs29.readFile(
|
|
54039
|
+
const txt = await fs29.readFile(path69.join(designProjectDir(projectRoot), name), "utf8");
|
|
53902
54040
|
if (txt.trim()) {
|
|
53903
54041
|
rules = txt.trim();
|
|
53904
54042
|
break;
|
|
@@ -53924,7 +54062,7 @@ function parseOverrides(value) {
|
|
|
53924
54062
|
}
|
|
53925
54063
|
async function loadActiveKit(projectRoot) {
|
|
53926
54064
|
try {
|
|
53927
|
-
const raw = await fs29.readFile(
|
|
54065
|
+
const raw = await fs29.readFile(path69.join(designProjectDir(projectRoot), "active.json"), "utf8");
|
|
53928
54066
|
const parsed = JSON.parse(raw);
|
|
53929
54067
|
if (parsed && typeof parsed.kit === "string") {
|
|
53930
54068
|
return {
|
|
@@ -53958,7 +54096,7 @@ function applyTokenOverrides(tokens, overrides) {
|
|
|
53958
54096
|
async function ensureDesignDir(projectRoot) {
|
|
53959
54097
|
const dir = designProjectDir(projectRoot);
|
|
53960
54098
|
await fs29.mkdir(dir, { recursive: true });
|
|
53961
|
-
const gi =
|
|
54099
|
+
const gi = path69.join(dir, ".gitignore");
|
|
53962
54100
|
if (!existsSync8(gi)) {
|
|
53963
54101
|
try {
|
|
53964
54102
|
await fs29.writeFile(gi, "*\n");
|
|
@@ -53972,11 +54110,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
|
|
|
53972
54110
|
const dir = await ensureDesignDir(projectRoot);
|
|
53973
54111
|
const record = { kit, stack: stack ?? null };
|
|
53974
54112
|
if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
|
|
53975
|
-
await fs29.writeFile(
|
|
54113
|
+
await fs29.writeFile(path69.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
|
|
53976
54114
|
`);
|
|
53977
54115
|
const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
|
|
53978
54116
|
`;
|
|
53979
|
-
await fs29.appendFile(
|
|
54117
|
+
await fs29.appendFile(path69.join(dir, "decisions.md"), line);
|
|
53980
54118
|
} catch {
|
|
53981
54119
|
}
|
|
53982
54120
|
}
|
|
@@ -53992,11 +54130,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
|
|
|
53992
54130
|
const dir = await ensureDesignDir(projectRoot);
|
|
53993
54131
|
const record = { kit: active.kit, stack: active.stack ?? null };
|
|
53994
54132
|
if (Object.keys(merged).length > 0) record.overrides = merged;
|
|
53995
|
-
await fs29.writeFile(
|
|
54133
|
+
await fs29.writeFile(path69.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
|
|
53996
54134
|
`);
|
|
53997
54135
|
const keys = Object.keys(patch).join(",");
|
|
53998
54136
|
await fs29.appendFile(
|
|
53999
|
-
|
|
54137
|
+
path69.join(dir, "decisions.md"),
|
|
54000
54138
|
`- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
|
|
54001
54139
|
`
|
|
54002
54140
|
);
|
|
@@ -54006,7 +54144,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
|
|
|
54006
54144
|
}
|
|
54007
54145
|
async function clearPersistedActiveKit(projectRoot) {
|
|
54008
54146
|
try {
|
|
54009
|
-
await fs29.rm(
|
|
54147
|
+
await fs29.rm(path69.join(designProjectDir(projectRoot), "active.json"), { force: true });
|
|
54010
54148
|
} catch {
|
|
54011
54149
|
}
|
|
54012
54150
|
}
|
|
@@ -56924,7 +57062,7 @@ ${summaryText}` : summaryText;
|
|
|
56924
57062
|
|
|
56925
57063
|
// src/execution/parallel-eternal-engine.ts
|
|
56926
57064
|
init_error();
|
|
56927
|
-
import { randomUUID as
|
|
57065
|
+
import { randomUUID as randomUUID32 } from "node:crypto";
|
|
56928
57066
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
56929
57067
|
var ParallelEternalEngine = class {
|
|
56930
57068
|
constructor(opts) {
|
|
@@ -57001,7 +57139,7 @@ var ParallelEternalEngine = class {
|
|
|
57001
57139
|
this.state = "running";
|
|
57002
57140
|
await this.persistState("running");
|
|
57003
57141
|
const config = {
|
|
57004
|
-
coordinatorId: `parallel-${
|
|
57142
|
+
coordinatorId: `parallel-${randomUUID32().slice(0, 8)}`,
|
|
57005
57143
|
maxConcurrent: this.slots,
|
|
57006
57144
|
doneCondition: { type: "all_tasks_done" }
|
|
57007
57145
|
};
|
|
@@ -57055,7 +57193,7 @@ var ParallelEternalEngine = class {
|
|
|
57055
57193
|
}
|
|
57056
57194
|
if (!this.coordinator) {
|
|
57057
57195
|
const config = {
|
|
57058
|
-
coordinatorId: `parallel-${
|
|
57196
|
+
coordinatorId: `parallel-${randomUUID32().slice(0, 8)}`,
|
|
57059
57197
|
maxConcurrent: this.slots,
|
|
57060
57198
|
doneCondition: { type: "all_tasks_done" }
|
|
57061
57199
|
};
|
|
@@ -57141,7 +57279,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
57141
57279
|
const task = expectDefined(tasks[i]);
|
|
57142
57280
|
const route = routes[i] ?? null;
|
|
57143
57281
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
57144
|
-
const taskId =
|
|
57282
|
+
const taskId = randomUUID32();
|
|
57145
57283
|
const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
|
|
57146
57284
|
` : "";
|
|
57147
57285
|
const spec = {
|
|
@@ -57964,7 +58102,7 @@ Summarize the following message range:`;
|
|
|
57964
58102
|
|
|
57965
58103
|
// src/execution/skill-loader.ts
|
|
57966
58104
|
import * as fs30 from "node:fs/promises";
|
|
57967
|
-
import * as
|
|
58105
|
+
import * as path70 from "node:path";
|
|
57968
58106
|
|
|
57969
58107
|
// src/skills/foreign-sources.ts
|
|
57970
58108
|
var FOREIGN_SKILL_TOOLS = [
|
|
@@ -58034,7 +58172,7 @@ async function entryIsDirectory(dir, entry) {
|
|
|
58034
58172
|
if (entry.isDirectory()) return true;
|
|
58035
58173
|
if (entry.isSymbolicLink()) {
|
|
58036
58174
|
try {
|
|
58037
|
-
return (await fs30.stat(
|
|
58175
|
+
return (await fs30.stat(path70.join(dir, entry.name))).isDirectory();
|
|
58038
58176
|
} catch {
|
|
58039
58177
|
return false;
|
|
58040
58178
|
}
|
|
@@ -58058,7 +58196,7 @@ var DefaultSkillLoader = class {
|
|
|
58058
58196
|
for (const tool of FOREIGN_SKILL_TOOLS) {
|
|
58059
58197
|
if (!foreignIds.includes(tool.id)) continue;
|
|
58060
58198
|
dirs.push({
|
|
58061
|
-
dir:
|
|
58199
|
+
dir: path70.join(root, "." + tool.id, tool.subdir),
|
|
58062
58200
|
source: "foreign",
|
|
58063
58201
|
originTool: tool.id
|
|
58064
58202
|
});
|
|
@@ -58088,7 +58226,7 @@ var DefaultSkillLoader = class {
|
|
|
58088
58226
|
);
|
|
58089
58227
|
for (const e of entries) {
|
|
58090
58228
|
if (!await entryIsDirectory(dir, e)) continue;
|
|
58091
|
-
const skillFile =
|
|
58229
|
+
const skillFile = path70.join(dir, e.name, "SKILL.md");
|
|
58092
58230
|
let raw;
|
|
58093
58231
|
try {
|
|
58094
58232
|
raw = await fs30.readFile(skillFile, "utf8");
|
|
@@ -58217,7 +58355,7 @@ var DefaultSkillLoader = class {
|
|
|
58217
58355
|
if (cached2 !== void 0) return cached2;
|
|
58218
58356
|
const m = await this.find(name);
|
|
58219
58357
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
58220
|
-
const savePath =
|
|
58358
|
+
const savePath = path70.join(path70.dirname(m.path), "SKILL.save.md");
|
|
58221
58359
|
let result;
|
|
58222
58360
|
try {
|
|
58223
58361
|
result = await fs30.readFile(savePath, "utf8");
|
|
@@ -58251,13 +58389,13 @@ function parseDescriptionFromText(desc) {
|
|
|
58251
58389
|
|
|
58252
58390
|
// src/execution/prompt-loader.ts
|
|
58253
58391
|
import * as fs32 from "node:fs/promises";
|
|
58254
|
-
import * as
|
|
58392
|
+
import * as path72 from "node:path";
|
|
58255
58393
|
|
|
58256
58394
|
// src/storage/prompt-store.ts
|
|
58257
58395
|
init_atomic_write();
|
|
58258
58396
|
import { createHash as createHash26 } from "node:crypto";
|
|
58259
58397
|
import * as fs31 from "node:fs/promises";
|
|
58260
|
-
import * as
|
|
58398
|
+
import * as path71 from "node:path";
|
|
58261
58399
|
var SCHEMA_VERSION3 = 2;
|
|
58262
58400
|
function promptChecksum(content) {
|
|
58263
58401
|
return createHash26("sha256").update(content, "utf8").digest("hex");
|
|
@@ -58326,7 +58464,7 @@ var DefaultPromptStore = class {
|
|
|
58326
58464
|
if (!file.endsWith(".json")) continue;
|
|
58327
58465
|
try {
|
|
58328
58466
|
const raw = JSON.parse(
|
|
58329
|
-
await fs31.readFile(
|
|
58467
|
+
await fs31.readFile(path71.join(this.dir, file), "utf8")
|
|
58330
58468
|
);
|
|
58331
58469
|
const migrated = migratePromptEntry(raw.entry);
|
|
58332
58470
|
if (migrated) entries.push(migrated);
|
|
@@ -58340,7 +58478,7 @@ var DefaultPromptStore = class {
|
|
|
58340
58478
|
);
|
|
58341
58479
|
}
|
|
58342
58480
|
async get(id) {
|
|
58343
|
-
const file =
|
|
58481
|
+
const file = path71.join(this.dir, `${id}.json`);
|
|
58344
58482
|
try {
|
|
58345
58483
|
const raw = JSON.parse(await fs31.readFile(file, "utf8"));
|
|
58346
58484
|
return migratePromptEntry(raw.entry);
|
|
@@ -58350,12 +58488,12 @@ var DefaultPromptStore = class {
|
|
|
58350
58488
|
}
|
|
58351
58489
|
async save(entry) {
|
|
58352
58490
|
await ensureDir(this.dir);
|
|
58353
|
-
const file =
|
|
58491
|
+
const file = path71.join(this.dir, `${entry.id}.json`);
|
|
58354
58492
|
const raw = { version: SCHEMA_VERSION3, entry };
|
|
58355
58493
|
await atomicWrite(file, JSON.stringify(raw, null, 2));
|
|
58356
58494
|
}
|
|
58357
58495
|
async delete(id) {
|
|
58358
|
-
const file =
|
|
58496
|
+
const file = path71.join(this.dir, `${id}.json`);
|
|
58359
58497
|
try {
|
|
58360
58498
|
await fs31.unlink(file);
|
|
58361
58499
|
return true;
|
|
@@ -58444,7 +58582,7 @@ var DefaultPromptLoader = class {
|
|
|
58444
58582
|
constructor(opts) {
|
|
58445
58583
|
this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
|
|
58446
58584
|
this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
|
|
58447
|
-
this.builtinDir = opts.bundledDir ?
|
|
58585
|
+
this.builtinDir = opts.bundledDir ? path72.join(opts.bundledDir, "prompts") : void 0;
|
|
58448
58586
|
}
|
|
58449
58587
|
async list() {
|
|
58450
58588
|
if (this.cache) return this.cache;
|
|
@@ -58576,7 +58714,7 @@ async function walkJson(dir) {
|
|
|
58576
58714
|
return out;
|
|
58577
58715
|
}
|
|
58578
58716
|
for (const e of entries) {
|
|
58579
|
-
const full =
|
|
58717
|
+
const full = path72.join(dir, e.name);
|
|
58580
58718
|
if (e.isDirectory()) {
|
|
58581
58719
|
out.push(...await walkJson(full));
|
|
58582
58720
|
} else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
|
|
@@ -58763,9 +58901,9 @@ function readPolicy(ctx) {
|
|
|
58763
58901
|
}
|
|
58764
58902
|
|
|
58765
58903
|
// src/execution/tool-executor.ts
|
|
58766
|
-
import { randomUUID as
|
|
58904
|
+
import { randomUUID as randomUUID34 } from "node:crypto";
|
|
58767
58905
|
import * as fs34 from "node:fs/promises";
|
|
58768
|
-
import * as
|
|
58906
|
+
import * as path75 from "node:path";
|
|
58769
58907
|
import { isDeepStrictEqual } from "node:util";
|
|
58770
58908
|
|
|
58771
58909
|
// src/observability/process-telemetry.ts
|
|
@@ -58857,7 +58995,7 @@ function emit(name, event) {
|
|
|
58857
58995
|
|
|
58858
58996
|
// src/security/kanban-boundary.ts
|
|
58859
58997
|
import { realpath as realpath3 } from "node:fs/promises";
|
|
58860
|
-
import * as
|
|
58998
|
+
import * as path73 from "node:path";
|
|
58861
58999
|
import {
|
|
58862
59000
|
evaluateKanbanBoundaryOpaque,
|
|
58863
59001
|
evaluateKanbanBoundaryPath,
|
|
@@ -58946,10 +59084,10 @@ function resolveKanbanIdentity(ctx) {
|
|
|
58946
59084
|
async function extractCandidatePaths(toolName, input, ctx) {
|
|
58947
59085
|
if (toolName === "patch" && typeof input["patch"] === "string") {
|
|
58948
59086
|
const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
|
|
58949
|
-
const directory =
|
|
59087
|
+
const directory = path73.isAbsolute(directoryInput) ? directoryInput : path73.resolve(ctx.workingDir, directoryInput);
|
|
58950
59088
|
const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
|
|
58951
59089
|
const targets = extractPatchTargets(input["patch"], strip).map(
|
|
58952
|
-
(target) => relativeToProject(
|
|
59090
|
+
(target) => relativeToProject(path73.resolve(directory, target), ctx.projectRoot)
|
|
58953
59091
|
);
|
|
58954
59092
|
return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
|
|
58955
59093
|
}
|
|
@@ -58959,7 +59097,7 @@ async function extractCandidatePaths(toolName, input, ctx) {
|
|
|
58959
59097
|
collectPathValues(input, values, pathKeys);
|
|
58960
59098
|
if (toolName === "scaffold" && typeof input["name"] === "string") {
|
|
58961
59099
|
const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
|
|
58962
|
-
values.push(
|
|
59100
|
+
values.push(path73.join(cwd, input["name"]));
|
|
58963
59101
|
}
|
|
58964
59102
|
const candidates = [
|
|
58965
59103
|
...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
|
|
@@ -58967,21 +59105,21 @@ async function extractCandidatePaths(toolName, input, ctx) {
|
|
|
58967
59105
|
return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
|
|
58968
59106
|
}
|
|
58969
59107
|
async function canonicalizeCandidatePath(candidate, ctx) {
|
|
58970
|
-
if (
|
|
58971
|
-
const absolute =
|
|
59108
|
+
if (path73.isAbsolute(candidate)) return candidate;
|
|
59109
|
+
const absolute = path73.resolve(ctx.projectRoot, candidate);
|
|
58972
59110
|
const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
58973
59111
|
let probe2 = absolute;
|
|
58974
59112
|
const missingSegments = [];
|
|
58975
59113
|
while (true) {
|
|
58976
59114
|
try {
|
|
58977
|
-
const canonical =
|
|
59115
|
+
const canonical = path73.join(await realpath3(probe2), ...missingSegments);
|
|
58978
59116
|
return relativeToProject(canonical, canonicalRoot);
|
|
58979
59117
|
} catch (cause) {
|
|
58980
59118
|
const code = cause.code;
|
|
58981
59119
|
if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
|
|
58982
|
-
const parent =
|
|
59120
|
+
const parent = path73.dirname(probe2);
|
|
58983
59121
|
if (parent === probe2) return absolute;
|
|
58984
|
-
missingSegments.unshift(
|
|
59122
|
+
missingSegments.unshift(path73.basename(probe2));
|
|
58985
59123
|
probe2 = parent;
|
|
58986
59124
|
}
|
|
58987
59125
|
}
|
|
@@ -59004,12 +59142,12 @@ function splitPathList(value) {
|
|
|
59004
59142
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
59005
59143
|
}
|
|
59006
59144
|
function resolveInputPath(value, ctx) {
|
|
59007
|
-
const absolute =
|
|
59145
|
+
const absolute = path73.isAbsolute(value) ? value : path73.resolve(ctx.workingDir, value);
|
|
59008
59146
|
return relativeToProject(absolute, ctx.projectRoot);
|
|
59009
59147
|
}
|
|
59010
59148
|
function relativeToProject(absolute, projectRoot) {
|
|
59011
|
-
const relative19 =
|
|
59012
|
-
return relative19.startsWith("../") ||
|
|
59149
|
+
const relative19 = path73.relative(projectRoot, absolute).replace(/\\/g, "/");
|
|
59150
|
+
return relative19.startsWith("../") || path73.isAbsolute(relative19) ? absolute : relative19 || ".";
|
|
59013
59151
|
}
|
|
59014
59152
|
function extractPatchTargets(patchText, strip) {
|
|
59015
59153
|
const targets = [];
|
|
@@ -59041,9 +59179,9 @@ init_error();
|
|
|
59041
59179
|
init_errors();
|
|
59042
59180
|
|
|
59043
59181
|
// src/execution/tool-executor-support.ts
|
|
59044
|
-
import { createHash as createHash28, randomUUID as
|
|
59182
|
+
import { createHash as createHash28, randomUUID as randomUUID33 } from "node:crypto";
|
|
59045
59183
|
import * as fs33 from "node:fs/promises";
|
|
59046
|
-
import * as
|
|
59184
|
+
import * as path74 from "node:path";
|
|
59047
59185
|
|
|
59048
59186
|
// src/types/tool.ts
|
|
59049
59187
|
var ToolErrorCategory = /* @__PURE__ */ ((ToolErrorCategory2) => {
|
|
@@ -59181,11 +59319,11 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
|
59181
59319
|
return content;
|
|
59182
59320
|
}
|
|
59183
59321
|
try {
|
|
59184
|
-
const dir =
|
|
59322
|
+
const dir = path74.join(wstackGlobalRoot(), "tool-output");
|
|
59185
59323
|
await fs33.mkdir(dir, { recursive: true });
|
|
59186
59324
|
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
59187
59325
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
59188
|
-
const filePath =
|
|
59326
|
+
const filePath = path74.join(dir, `${stamp}-${safeTool}-${randomUUID33()}.log`);
|
|
59189
59327
|
await fs33.writeFile(filePath, content, "utf8");
|
|
59190
59328
|
const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
59191
59329
|
const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
|
|
@@ -59771,7 +59909,7 @@ ${errorDetails}`,
|
|
|
59771
59909
|
const inputPath = use.input && typeof use.input === "object" ? use.input.path : void 0;
|
|
59772
59910
|
const caps = tool.capabilities ?? [];
|
|
59773
59911
|
const hasFileCapability = caps.includes("fs.read") || caps.includes("fs.write");
|
|
59774
|
-
const absPath = hasFileCapability && typeof inputPath === "string" ?
|
|
59912
|
+
const absPath = hasFileCapability && typeof inputPath === "string" ? path75.isAbsolute(inputPath) ? inputPath : path75.resolve(ctx.projectRoot, inputPath) : void 0;
|
|
59775
59913
|
let writeTargetExisted;
|
|
59776
59914
|
if (tool.name === "write" && caps.includes("fs.write") && absPath) {
|
|
59777
59915
|
writeTargetExisted = await fs34.stat(absPath).then(
|
|
@@ -59926,7 +60064,7 @@ ${post.additionalContext}`;
|
|
|
59926
60064
|
const bridge = async (toolName, input) => {
|
|
59927
60065
|
const nestedUse = {
|
|
59928
60066
|
type: "tool_use",
|
|
59929
|
-
id: `nested-${
|
|
60067
|
+
id: `nested-${randomUUID34()}`,
|
|
59930
60068
|
name: toolName,
|
|
59931
60069
|
input
|
|
59932
60070
|
};
|
|
@@ -60038,7 +60176,7 @@ ${post.additionalContext}`;
|
|
|
60038
60176
|
]);
|
|
60039
60177
|
let output;
|
|
60040
60178
|
const execute = () => typeof tool.executeStream === "function" ? this.runStreamedTool(tool, input, ctx, combined, toolUseId) : (async () => tool.execute(input, ctx, { signal: combined }))();
|
|
60041
|
-
const telemetryToolCallId = toolUseId ?? `nested-${
|
|
60179
|
+
const telemetryToolCallId = toolUseId ?? `nested-${randomUUID34()}`;
|
|
60042
60180
|
const toolPromise = this.opts.events ? runWithNetworkTelemetry(
|
|
60043
60181
|
{
|
|
60044
60182
|
events: this.opts.events,
|
|
@@ -60591,27 +60729,27 @@ function codexModelMeta(id) {
|
|
|
60591
60729
|
// src/models/mode-store.ts
|
|
60592
60730
|
init_atomic_write();
|
|
60593
60731
|
import * as fs35 from "node:fs/promises";
|
|
60594
|
-
import * as
|
|
60732
|
+
import * as path77 from "node:path";
|
|
60595
60733
|
|
|
60596
60734
|
// src/types/mode-prompts.ts
|
|
60597
60735
|
import { readFileSync as readFileSync17, statSync as statSync7 } from "node:fs";
|
|
60598
|
-
import * as
|
|
60736
|
+
import * as path76 from "node:path";
|
|
60599
60737
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
60600
60738
|
function modePrompt(id) {
|
|
60601
60739
|
for (const dir of modePromptDirCandidates()) {
|
|
60602
60740
|
try {
|
|
60603
|
-
return readFileSync17(
|
|
60741
|
+
return readFileSync17(path76.join(dir, `${id}.md`), "utf8").trimEnd();
|
|
60604
60742
|
} catch {
|
|
60605
60743
|
}
|
|
60606
60744
|
}
|
|
60607
60745
|
return "";
|
|
60608
60746
|
}
|
|
60609
60747
|
function modePromptDirCandidates() {
|
|
60610
|
-
const here =
|
|
60748
|
+
const here = path76.dirname(fileURLToPath8(import.meta.url));
|
|
60611
60749
|
const candidates = [
|
|
60612
|
-
|
|
60613
|
-
|
|
60614
|
-
|
|
60750
|
+
path76.resolve(here, "../../instructions/modes"),
|
|
60751
|
+
path76.resolve(here, "../instructions/modes"),
|
|
60752
|
+
path76.resolve(here, "instructions/modes")
|
|
60615
60753
|
];
|
|
60616
60754
|
return candidates.sort((a, b) => Number(!isDirectory4(a)) - Number(!isDirectory4(b)));
|
|
60617
60755
|
}
|
|
@@ -60843,7 +60981,7 @@ var DefaultModeStore = class {
|
|
|
60843
60981
|
}
|
|
60844
60982
|
async loadActiveMode() {
|
|
60845
60983
|
try {
|
|
60846
|
-
const configPath =
|
|
60984
|
+
const configPath = path77.join(this.configDir, "mode.json");
|
|
60847
60985
|
const content = await fs35.readFile(configPath, "utf8");
|
|
60848
60986
|
const data = JSON.parse(content);
|
|
60849
60987
|
this.activeModeId = data.activeMode ?? null;
|
|
@@ -60854,7 +60992,7 @@ var DefaultModeStore = class {
|
|
|
60854
60992
|
async saveActiveMode() {
|
|
60855
60993
|
try {
|
|
60856
60994
|
await fs35.mkdir(this.configDir, { recursive: true });
|
|
60857
|
-
const configPath =
|
|
60995
|
+
const configPath = path77.join(this.configDir, "mode.json");
|
|
60858
60996
|
await atomicWrite(
|
|
60859
60997
|
configPath,
|
|
60860
60998
|
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
@@ -60869,11 +61007,11 @@ async function loadProjectModes(modesDir) {
|
|
|
60869
61007
|
const entries = await fs35.readdir(modesDir);
|
|
60870
61008
|
for (const entry of entries) {
|
|
60871
61009
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
60872
|
-
const filePath =
|
|
61010
|
+
const filePath = path77.join(modesDir, entry);
|
|
60873
61011
|
const stat33 = await fs35.stat(filePath);
|
|
60874
61012
|
if (!stat33.isFile()) continue;
|
|
60875
61013
|
const content = await fs35.readFile(filePath, "utf8");
|
|
60876
|
-
const id =
|
|
61014
|
+
const id = path77.basename(entry, path77.extname(entry));
|
|
60877
61015
|
modes.push({
|
|
60878
61016
|
id,
|
|
60879
61017
|
name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -60889,7 +61027,7 @@ async function loadProjectModes(modesDir) {
|
|
|
60889
61027
|
async function loadUserModes(modesDir) {
|
|
60890
61028
|
const modes = [];
|
|
60891
61029
|
try {
|
|
60892
|
-
const manifestPath =
|
|
61030
|
+
const manifestPath = path77.join(modesDir, "modes.json");
|
|
60893
61031
|
const content = await fs35.readFile(manifestPath, "utf8");
|
|
60894
61032
|
const manifest = JSON.parse(content);
|
|
60895
61033
|
for (const mode of manifest.modes) {
|
|
@@ -60902,13 +61040,13 @@ async function loadUserModes(modesDir) {
|
|
|
60902
61040
|
|
|
60903
61041
|
// src/models/models-registry.ts
|
|
60904
61042
|
import * as fs36 from "node:fs/promises";
|
|
60905
|
-
import * as
|
|
61043
|
+
import * as path78 from "node:path";
|
|
60906
61044
|
init_atomic_write();
|
|
60907
61045
|
init_error();
|
|
60908
61046
|
init_errors();
|
|
60909
61047
|
var DEFAULT_URL = "https://models.dev/api.json";
|
|
60910
61048
|
var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
|
|
60911
|
-
var DEFAULT_TTL_SECONDS =
|
|
61049
|
+
var DEFAULT_TTL_SECONDS = 3 * 3600;
|
|
60912
61050
|
var DEFAULT_REFRESH_TIMEOUT_MS = 15e3;
|
|
60913
61051
|
var FAMILY_BY_NPM = {
|
|
60914
61052
|
"@ai-sdk/anthropic": "anthropic",
|
|
@@ -60982,7 +61120,7 @@ var DefaultModelsRegistry = class {
|
|
|
60982
61120
|
this.overlay = opts.overlay;
|
|
60983
61121
|
this.overlayUrl = opts.overlayUrl;
|
|
60984
61122
|
this.overlayFile = opts.overlayFile;
|
|
60985
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
61123
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path78.join(path78.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
60986
61124
|
this.logger = opts.logger ?? noOpLogger;
|
|
60987
61125
|
}
|
|
60988
61126
|
async load(opts = {}) {
|
|
@@ -61257,7 +61395,7 @@ var DefaultModelsRegistry = class {
|
|
|
61257
61395
|
}
|
|
61258
61396
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
61259
61397
|
cacheLocation() {
|
|
61260
|
-
return
|
|
61398
|
+
return path78.resolve(this.cacheFile);
|
|
61261
61399
|
}
|
|
61262
61400
|
};
|
|
61263
61401
|
var REASONING_EFFORTS = /* @__PURE__ */ new Set([
|
|
@@ -62340,7 +62478,7 @@ function validateTrustPolicy(value) {
|
|
|
62340
62478
|
}
|
|
62341
62479
|
|
|
62342
62480
|
// src/security/yolo-risk.ts
|
|
62343
|
-
import * as
|
|
62481
|
+
import * as path79 from "node:path";
|
|
62344
62482
|
var CATASTROPHIC_PATTERNS = [
|
|
62345
62483
|
/\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
|
|
62346
62484
|
// make a filesystem — wipes a partition
|
|
@@ -62400,9 +62538,9 @@ function getInputString(input, key) {
|
|
|
62400
62538
|
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
62401
62539
|
if (!projectRoot) return false;
|
|
62402
62540
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
62403
|
-
const resolved =
|
|
62404
|
-
const relative19 =
|
|
62405
|
-
return !!relative19 && !relative19.startsWith("..") && !
|
|
62541
|
+
const resolved = path79.resolve(projectRoot, rawPath);
|
|
62542
|
+
const relative19 = path79.relative(projectRoot, resolved);
|
|
62543
|
+
return !!relative19 && !relative19.startsWith("..") && !path79.isAbsolute(relative19);
|
|
62406
62544
|
}
|
|
62407
62545
|
function tokenizeShell(command) {
|
|
62408
62546
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
@@ -62589,7 +62727,68 @@ function isClearlyDestructiveBashCommand(command, projectRoot) {
|
|
|
62589
62727
|
return false;
|
|
62590
62728
|
}
|
|
62591
62729
|
|
|
62592
|
-
// src/security/
|
|
62730
|
+
// src/security/auto-approve-policy.ts
|
|
62731
|
+
var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
62732
|
+
allowedCapabilities;
|
|
62733
|
+
constructor(allowedCapabilities) {
|
|
62734
|
+
this.allowedCapabilities = allowedCapabilities ?? [
|
|
62735
|
+
ToolCapabilities.FS_READ,
|
|
62736
|
+
ToolCapabilities.NET_OUTBOUND
|
|
62737
|
+
];
|
|
62738
|
+
}
|
|
62739
|
+
static isMcpTool(name) {
|
|
62740
|
+
return name.startsWith("mcp__");
|
|
62741
|
+
}
|
|
62742
|
+
async evaluate(tool) {
|
|
62743
|
+
const caps = tool.capabilities ?? [];
|
|
62744
|
+
const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
|
|
62745
|
+
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
62746
|
+
const mcpProxyAllowed = this.allowedCapabilities.includes(ToolCapabilities.MCP_PROXY);
|
|
62747
|
+
const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
|
|
62748
|
+
(c) => !this.allowedCapabilities.includes(c)
|
|
62749
|
+
);
|
|
62750
|
+
const blocked = tool.permission === "deny" || isMcp && !mcpProxyAllowed || !hasAllowedCap || dangerousNotAllowed.length > 0;
|
|
62751
|
+
if (blocked) {
|
|
62752
|
+
const reason = isMcp && !mcpProxyAllowed ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow mcp.proxy explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
62753
|
+
return {
|
|
62754
|
+
permission: "deny",
|
|
62755
|
+
source: "subagent_guard",
|
|
62756
|
+
reason
|
|
62757
|
+
};
|
|
62758
|
+
}
|
|
62759
|
+
return { permission: "auto", source: "yolo" };
|
|
62760
|
+
}
|
|
62761
|
+
async trust() {
|
|
62762
|
+
}
|
|
62763
|
+
async deny() {
|
|
62764
|
+
}
|
|
62765
|
+
denyOnce() {
|
|
62766
|
+
}
|
|
62767
|
+
allowOnce() {
|
|
62768
|
+
}
|
|
62769
|
+
async explain(tool) {
|
|
62770
|
+
const decision = await this.evaluate(tool);
|
|
62771
|
+
return {
|
|
62772
|
+
toolName: tool.name,
|
|
62773
|
+
subject: null,
|
|
62774
|
+
steps: [
|
|
62775
|
+
{
|
|
62776
|
+
rule: "subagent auto",
|
|
62777
|
+
matched: decision.permission === "auto",
|
|
62778
|
+
decision: decision.permission,
|
|
62779
|
+
source: decision.source,
|
|
62780
|
+
detail: decision.reason ?? `subagent policy: ${decision.permission}`
|
|
62781
|
+
}
|
|
62782
|
+
],
|
|
62783
|
+
winnerIndex: 0,
|
|
62784
|
+
decision
|
|
62785
|
+
};
|
|
62786
|
+
}
|
|
62787
|
+
async reload() {
|
|
62788
|
+
}
|
|
62789
|
+
};
|
|
62790
|
+
|
|
62791
|
+
// src/security/permission-helpers.ts
|
|
62593
62792
|
function matchesTrust(patterns, subject2) {
|
|
62594
62793
|
return patterns.includes(subject2) || matchAny(patterns, subject2);
|
|
62595
62794
|
}
|
|
@@ -62684,6 +62883,8 @@ function shellCommandReadsSensitivePath(command) {
|
|
|
62684
62883
|
}
|
|
62685
62884
|
return false;
|
|
62686
62885
|
}
|
|
62886
|
+
|
|
62887
|
+
// src/security/permission-policy.ts
|
|
62687
62888
|
var DefaultPermissionPolicy = class {
|
|
62688
62889
|
policy = {};
|
|
62689
62890
|
loaded = false;
|
|
@@ -63426,71 +63627,12 @@ var DefaultPermissionPolicy = class {
|
|
|
63426
63627
|
return void 0;
|
|
63427
63628
|
}
|
|
63428
63629
|
};
|
|
63429
|
-
var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
63430
|
-
allowedCapabilities;
|
|
63431
|
-
constructor(allowedCapabilities) {
|
|
63432
|
-
this.allowedCapabilities = allowedCapabilities ?? [
|
|
63433
|
-
ToolCapabilities.FS_READ,
|
|
63434
|
-
ToolCapabilities.NET_OUTBOUND
|
|
63435
|
-
];
|
|
63436
|
-
}
|
|
63437
|
-
static isMcpTool(name) {
|
|
63438
|
-
return name.startsWith("mcp__");
|
|
63439
|
-
}
|
|
63440
|
-
async evaluate(tool) {
|
|
63441
|
-
const caps = tool.capabilities ?? [];
|
|
63442
|
-
const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
|
|
63443
|
-
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
63444
|
-
const mcpProxyAllowed = this.allowedCapabilities.includes(ToolCapabilities.MCP_PROXY);
|
|
63445
|
-
const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
|
|
63446
|
-
(c) => !this.allowedCapabilities.includes(c)
|
|
63447
|
-
);
|
|
63448
|
-
const blocked = tool.permission === "deny" || isMcp && !mcpProxyAllowed || !hasAllowedCap || dangerousNotAllowed.length > 0;
|
|
63449
|
-
if (blocked) {
|
|
63450
|
-
const reason = isMcp && !mcpProxyAllowed ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow mcp.proxy explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
63451
|
-
return {
|
|
63452
|
-
permission: "deny",
|
|
63453
|
-
source: "subagent_guard",
|
|
63454
|
-
reason
|
|
63455
|
-
};
|
|
63456
|
-
}
|
|
63457
|
-
return { permission: "auto", source: "yolo" };
|
|
63458
|
-
}
|
|
63459
|
-
async trust() {
|
|
63460
|
-
}
|
|
63461
|
-
async deny() {
|
|
63462
|
-
}
|
|
63463
|
-
denyOnce() {
|
|
63464
|
-
}
|
|
63465
|
-
allowOnce() {
|
|
63466
|
-
}
|
|
63467
|
-
async explain(tool) {
|
|
63468
|
-
const decision = await this.evaluate(tool);
|
|
63469
|
-
return {
|
|
63470
|
-
toolName: tool.name,
|
|
63471
|
-
subject: null,
|
|
63472
|
-
steps: [
|
|
63473
|
-
{
|
|
63474
|
-
rule: "subagent auto",
|
|
63475
|
-
matched: decision.permission === "auto",
|
|
63476
|
-
decision: decision.permission,
|
|
63477
|
-
source: decision.source,
|
|
63478
|
-
detail: decision.reason ?? `subagent policy: ${decision.permission}`
|
|
63479
|
-
}
|
|
63480
|
-
],
|
|
63481
|
-
winnerIndex: 0,
|
|
63482
|
-
decision
|
|
63483
|
-
};
|
|
63484
|
-
}
|
|
63485
|
-
async reload() {
|
|
63486
|
-
}
|
|
63487
|
-
};
|
|
63488
63630
|
|
|
63489
63631
|
// src/storage/attachment-store.ts
|
|
63490
63632
|
init_atomic_write();
|
|
63491
63633
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
63492
|
-
import * as
|
|
63493
|
-
import * as
|
|
63634
|
+
import * as fsp30 from "node:fs/promises";
|
|
63635
|
+
import * as path80 from "node:path";
|
|
63494
63636
|
var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
|
|
63495
63637
|
var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
|
|
63496
63638
|
var DefaultAttachmentStore = class {
|
|
@@ -63510,8 +63652,8 @@ var DefaultAttachmentStore = class {
|
|
|
63510
63652
|
let spooledPath;
|
|
63511
63653
|
let data = input.data;
|
|
63512
63654
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
63513
|
-
await
|
|
63514
|
-
spooledPath =
|
|
63655
|
+
await fsp30.mkdir(this.spoolDir, { recursive: true });
|
|
63656
|
+
spooledPath = path80.join(this.spoolDir, `${id}.bin`);
|
|
63515
63657
|
await atomicWrite(spooledPath, input.data, {
|
|
63516
63658
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
63517
63659
|
});
|
|
@@ -63573,7 +63715,7 @@ var DefaultAttachmentStore = class {
|
|
|
63573
63715
|
for (const att of this.items.values()) {
|
|
63574
63716
|
if (att.path) toDelete.push(att.path);
|
|
63575
63717
|
}
|
|
63576
|
-
await Promise.all(toDelete.map((p) =>
|
|
63718
|
+
await Promise.all(toDelete.map((p) => fsp30.unlink(p).catch(() => void 0)));
|
|
63577
63719
|
}
|
|
63578
63720
|
this.items.clear();
|
|
63579
63721
|
this.refs.length = 0;
|
|
@@ -63581,7 +63723,7 @@ var DefaultAttachmentStore = class {
|
|
|
63581
63723
|
}
|
|
63582
63724
|
async toBlock(att) {
|
|
63583
63725
|
if (att.kind === "image") {
|
|
63584
|
-
const data = att.data ?? (att.path ? await
|
|
63726
|
+
const data = att.data ?? (att.path ? await fsp30.readFile(att.path, { encoding: "base64" }) : "");
|
|
63585
63727
|
return {
|
|
63586
63728
|
type: "image",
|
|
63587
63729
|
source: {
|
|
@@ -63591,7 +63733,7 @@ var DefaultAttachmentStore = class {
|
|
|
63591
63733
|
}
|
|
63592
63734
|
};
|
|
63593
63735
|
}
|
|
63594
|
-
const raw = att.data ?? (att.path ? await
|
|
63736
|
+
const raw = att.data ?? (att.path ? await fsp30.readFile(att.path, "utf8") : "");
|
|
63595
63737
|
const label = att.meta.filename ? `<file path="${att.meta.filename}">` : "<pasted>";
|
|
63596
63738
|
const close = att.meta.filename ? "</file>" : "</pasted>";
|
|
63597
63739
|
return { type: "text", text: `${label}
|
|
@@ -63758,13 +63900,13 @@ function deepFreeze(obj) {
|
|
|
63758
63900
|
init_atomic_write();
|
|
63759
63901
|
init_error();
|
|
63760
63902
|
init_errors();
|
|
63761
|
-
import * as
|
|
63762
|
-
import { randomUUID as
|
|
63903
|
+
import * as fsp31 from "node:fs/promises";
|
|
63904
|
+
import { randomUUID as randomUUID35 } from "node:crypto";
|
|
63763
63905
|
async function loadPlan(filePath, events) {
|
|
63764
63906
|
const t0 = Date.now();
|
|
63765
63907
|
let raw;
|
|
63766
63908
|
try {
|
|
63767
|
-
raw = await
|
|
63909
|
+
raw = await fsp31.readFile(filePath, "utf8");
|
|
63768
63910
|
} catch (err) {
|
|
63769
63911
|
events?.emit("storage.error", {
|
|
63770
63912
|
sessionId: "~boot~",
|
|
@@ -63857,7 +63999,7 @@ function emptyPlan(sessionId, title) {
|
|
|
63857
63999
|
function addPlanItem(plan, title, details) {
|
|
63858
64000
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
63859
64001
|
const item = {
|
|
63860
|
-
id: `plan_${Date.now()}_${
|
|
64002
|
+
id: `plan_${Date.now()}_${randomUUID35().slice(0, 6)}`,
|
|
63861
64003
|
title,
|
|
63862
64004
|
details,
|
|
63863
64005
|
status: "open",
|
|
@@ -63929,7 +64071,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
|
|
|
63929
64071
|
if (subtasks && subtasks.length > 0) {
|
|
63930
64072
|
for (const st of subtasks) {
|
|
63931
64073
|
todos.push({
|
|
63932
|
-
id: `todo_${Date.now()}_${
|
|
64074
|
+
id: `todo_${Date.now()}_${randomUUID35().slice(0, 6)}`,
|
|
63933
64075
|
content: st,
|
|
63934
64076
|
status: "pending",
|
|
63935
64077
|
promotedFromPlan: item.id
|
|
@@ -64074,8 +64216,8 @@ ${cat}:`);
|
|
|
64074
64216
|
// src/storage/queue-store.ts
|
|
64075
64217
|
init_atomic_write();
|
|
64076
64218
|
init_error();
|
|
64077
|
-
import * as
|
|
64078
|
-
import * as
|
|
64219
|
+
import * as fsp32 from "node:fs/promises";
|
|
64220
|
+
import * as path81 from "node:path";
|
|
64079
64221
|
var QUEUE_MAX_ITEMS = 100;
|
|
64080
64222
|
var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
|
|
64081
64223
|
var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
|
|
@@ -64108,7 +64250,7 @@ var QueueStore = class {
|
|
|
64108
64250
|
traceId;
|
|
64109
64251
|
logger;
|
|
64110
64252
|
constructor(opts) {
|
|
64111
|
-
this.file =
|
|
64253
|
+
this.file = path81.join(opts.dir, "queue.json");
|
|
64112
64254
|
this.events = opts.events;
|
|
64113
64255
|
this.traceId = opts.traceId;
|
|
64114
64256
|
this.logger = opts.logger;
|
|
@@ -64177,7 +64319,7 @@ var QueueStore = class {
|
|
|
64177
64319
|
const t0 = Date.now();
|
|
64178
64320
|
let raw;
|
|
64179
64321
|
try {
|
|
64180
|
-
const stat33 = await
|
|
64322
|
+
const stat33 = await fsp32.stat(this.file);
|
|
64181
64323
|
if (stat33.size > QUEUE_MAX_BYTES) {
|
|
64182
64324
|
this.logWarn("Queue file exceeds the safe read budget; ignoring it", {
|
|
64183
64325
|
event: "queue_store.read_budget_exceeded",
|
|
@@ -64197,7 +64339,7 @@ var QueueStore = class {
|
|
|
64197
64339
|
});
|
|
64198
64340
|
return [];
|
|
64199
64341
|
}
|
|
64200
|
-
raw = await
|
|
64342
|
+
raw = await fsp32.readFile(this.file, "utf8");
|
|
64201
64343
|
} catch (err) {
|
|
64202
64344
|
const code = err.code;
|
|
64203
64345
|
if (code === "ENOENT") {
|
|
@@ -64284,7 +64426,7 @@ var QueueStore = class {
|
|
|
64284
64426
|
async clear() {
|
|
64285
64427
|
const t0 = Date.now();
|
|
64286
64428
|
try {
|
|
64287
|
-
await
|
|
64429
|
+
await fsp32.unlink(this.file);
|
|
64288
64430
|
this.events?.emit("storage.write", {
|
|
64289
64431
|
sessionId: this.traceId ?? "~boot~",
|
|
64290
64432
|
store: "queue",
|
|
@@ -64324,9 +64466,9 @@ function isPersistedQueueItem(v) {
|
|
|
64324
64466
|
// src/storage/recovery-lock.ts
|
|
64325
64467
|
init_atomic_write();
|
|
64326
64468
|
init_pid();
|
|
64327
|
-
import * as
|
|
64469
|
+
import * as fsp33 from "node:fs/promises";
|
|
64328
64470
|
import * as os10 from "node:os";
|
|
64329
|
-
import * as
|
|
64471
|
+
import * as path82 from "node:path";
|
|
64330
64472
|
var LOCK_FILE = "active.json";
|
|
64331
64473
|
var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
64332
64474
|
var RecoveryLock = class {
|
|
@@ -64337,7 +64479,7 @@ var RecoveryLock = class {
|
|
|
64337
64479
|
sessionStore;
|
|
64338
64480
|
probe;
|
|
64339
64481
|
constructor(opts) {
|
|
64340
|
-
this.file =
|
|
64482
|
+
this.file = path82.join(opts.dir, LOCK_FILE);
|
|
64341
64483
|
this.pid = opts.pid ?? process.pid;
|
|
64342
64484
|
this.hostname = opts.hostname ?? os10.hostname();
|
|
64343
64485
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -64411,7 +64553,7 @@ var RecoveryLock = class {
|
|
|
64411
64553
|
* null return before calling this.
|
|
64412
64554
|
*/
|
|
64413
64555
|
async write(sessionId) {
|
|
64414
|
-
await ensureDir(
|
|
64556
|
+
await ensureDir(path82.dirname(this.file));
|
|
64415
64557
|
const lock = {
|
|
64416
64558
|
v: 1,
|
|
64417
64559
|
sessionId,
|
|
@@ -64420,7 +64562,7 @@ var RecoveryLock = class {
|
|
|
64420
64562
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
64421
64563
|
};
|
|
64422
64564
|
try {
|
|
64423
|
-
await
|
|
64565
|
+
await fsp33.writeFile(this.file, JSON.stringify(lock), { flag: "wx", mode: 384 });
|
|
64424
64566
|
} catch (err) {
|
|
64425
64567
|
const code = err.code;
|
|
64426
64568
|
if (code === "EEXIST") {
|
|
@@ -64436,7 +64578,7 @@ var RecoveryLock = class {
|
|
|
64436
64578
|
*/
|
|
64437
64579
|
async clear() {
|
|
64438
64580
|
try {
|
|
64439
|
-
await
|
|
64581
|
+
await fsp33.unlink(this.file);
|
|
64440
64582
|
} catch (err) {
|
|
64441
64583
|
const code = err.code;
|
|
64442
64584
|
if (code === "ENOENT") return;
|
|
@@ -64446,7 +64588,7 @@ var RecoveryLock = class {
|
|
|
64446
64588
|
async readLock() {
|
|
64447
64589
|
let raw;
|
|
64448
64590
|
try {
|
|
64449
|
-
raw = await
|
|
64591
|
+
raw = await fsp33.readFile(this.file, "utf8");
|
|
64450
64592
|
} catch (err) {
|
|
64451
64593
|
const code = err.code;
|
|
64452
64594
|
if (code === "ENOENT") return null;
|
|
@@ -65058,12 +65200,12 @@ function renderPlainText(meta, events) {
|
|
|
65058
65200
|
// src/storage/todos-checkpoint.ts
|
|
65059
65201
|
init_atomic_write();
|
|
65060
65202
|
init_error();
|
|
65061
|
-
import * as
|
|
65203
|
+
import * as fsp34 from "node:fs/promises";
|
|
65062
65204
|
async function loadTodosCheckpoint(filePath, events, traceId, sessionId) {
|
|
65063
65205
|
const t0 = Date.now();
|
|
65064
65206
|
let raw;
|
|
65065
65207
|
try {
|
|
65066
|
-
raw = await
|
|
65208
|
+
raw = await fsp34.readFile(filePath, "utf8");
|
|
65067
65209
|
} catch (err) {
|
|
65068
65210
|
events?.emit("storage.error", {
|
|
65069
65211
|
sessionId: sessionId ?? "~boot~",
|
|
@@ -70427,8 +70569,8 @@ var PHASE_EVENT_NAMES = [
|
|
|
70427
70569
|
|
|
70428
70570
|
// src/goal/phase-store.ts
|
|
70429
70571
|
init_atomic_write();
|
|
70430
|
-
import * as
|
|
70431
|
-
import * as
|
|
70572
|
+
import * as fsp35 from "node:fs/promises";
|
|
70573
|
+
import * as path83 from "node:path";
|
|
70432
70574
|
var PHASE_STORE_VERSION = 1;
|
|
70433
70575
|
var PhaseStore = class {
|
|
70434
70576
|
baseDir;
|
|
@@ -70436,7 +70578,7 @@ var PhaseStore = class {
|
|
|
70436
70578
|
constructor(opts) {
|
|
70437
70579
|
this.baseDir = opts.baseDir;
|
|
70438
70580
|
this.legacyBaseDirs = (opts.legacyBaseDirs ?? []).filter(
|
|
70439
|
-
(dir) =>
|
|
70581
|
+
(dir) => path83.resolve(dir) !== path83.resolve(this.baseDir)
|
|
70440
70582
|
);
|
|
70441
70583
|
}
|
|
70442
70584
|
async save(graph) {
|
|
@@ -70451,7 +70593,7 @@ var PhaseStore = class {
|
|
|
70451
70593
|
const current = await this.loadFromPath(filePath);
|
|
70452
70594
|
if (current) return current;
|
|
70453
70595
|
for (const legacyDir of this.legacyBaseDirs) {
|
|
70454
|
-
const legacyPath =
|
|
70596
|
+
const legacyPath = path83.join(legacyDir, `${graphId}.json`);
|
|
70455
70597
|
const legacy = await this.loadFromPath(legacyPath);
|
|
70456
70598
|
if (!legacy) continue;
|
|
70457
70599
|
try {
|
|
@@ -70467,19 +70609,19 @@ var PhaseStore = class {
|
|
|
70467
70609
|
async delete(graphId) {
|
|
70468
70610
|
const paths = [
|
|
70469
70611
|
this.getFilePath(graphId),
|
|
70470
|
-
...this.legacyBaseDirs.map((dir) =>
|
|
70612
|
+
...this.legacyBaseDirs.map((dir) => path83.join(dir, `${graphId}.json`))
|
|
70471
70613
|
];
|
|
70472
|
-
await Promise.all(paths.map((filePath) =>
|
|
70614
|
+
await Promise.all(paths.map((filePath) => fsp35.unlink(filePath).catch(() => void 0)));
|
|
70473
70615
|
}
|
|
70474
70616
|
async list() {
|
|
70475
70617
|
try {
|
|
70476
70618
|
await this.migrateLegacyGraphs();
|
|
70477
|
-
const entries = await
|
|
70619
|
+
const entries = await fsp35.readdir(this.baseDir, { withFileTypes: true });
|
|
70478
70620
|
const graphs = [];
|
|
70479
70621
|
for (const entry of entries) {
|
|
70480
70622
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
70481
70623
|
try {
|
|
70482
|
-
const raw = await
|
|
70624
|
+
const raw = await fsp35.readFile(path83.join(this.baseDir, entry.name), "utf8");
|
|
70483
70625
|
const serialized = JSON.parse(raw);
|
|
70484
70626
|
const done = serialized.completedPhaseIds.length;
|
|
70485
70627
|
const total = serialized.phases.length;
|
|
@@ -70498,11 +70640,11 @@ var PhaseStore = class {
|
|
|
70498
70640
|
}
|
|
70499
70641
|
}
|
|
70500
70642
|
getFilePath(graphId) {
|
|
70501
|
-
return
|
|
70643
|
+
return path83.join(this.baseDir, `${graphId}.json`);
|
|
70502
70644
|
}
|
|
70503
70645
|
async loadFromPath(filePath) {
|
|
70504
70646
|
try {
|
|
70505
|
-
const raw = await
|
|
70647
|
+
const raw = await fsp35.readFile(filePath, "utf8");
|
|
70506
70648
|
const serialized = JSON.parse(raw);
|
|
70507
70649
|
return this.deserializeGraph(serialized);
|
|
70508
70650
|
} catch {
|
|
@@ -70511,11 +70653,11 @@ var PhaseStore = class {
|
|
|
70511
70653
|
}
|
|
70512
70654
|
async migrateLegacyGraphs() {
|
|
70513
70655
|
for (const legacyDir of this.legacyBaseDirs) {
|
|
70514
|
-
const entries = await
|
|
70656
|
+
const entries = await fsp35.readdir(legacyDir, { withFileTypes: true }).catch(() => []);
|
|
70515
70657
|
for (const entry of entries) {
|
|
70516
70658
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
70517
|
-
const legacyPath =
|
|
70518
|
-
const currentPath = this.getFilePath(
|
|
70659
|
+
const legacyPath = path83.join(legacyDir, entry.name);
|
|
70660
|
+
const currentPath = this.getFilePath(path83.basename(entry.name, ".json"));
|
|
70519
70661
|
if (await this.pathExists(currentPath)) {
|
|
70520
70662
|
await this.removeMigratedLegacyFile(legacyDir, legacyPath);
|
|
70521
70663
|
continue;
|
|
@@ -70529,15 +70671,15 @@ var PhaseStore = class {
|
|
|
70529
70671
|
}
|
|
70530
70672
|
async pathExists(filePath) {
|
|
70531
70673
|
try {
|
|
70532
|
-
await
|
|
70674
|
+
await fsp35.access(filePath);
|
|
70533
70675
|
return true;
|
|
70534
70676
|
} catch {
|
|
70535
70677
|
return false;
|
|
70536
70678
|
}
|
|
70537
70679
|
}
|
|
70538
70680
|
async removeMigratedLegacyFile(legacyDir, legacyPath) {
|
|
70539
|
-
await
|
|
70540
|
-
await
|
|
70681
|
+
await fsp35.unlink(legacyPath).catch(() => void 0);
|
|
70682
|
+
await fsp35.rmdir(legacyDir).catch(() => void 0);
|
|
70541
70683
|
}
|
|
70542
70684
|
serializeGraph(graph) {
|
|
70543
70685
|
return {
|
|
@@ -70663,8 +70805,8 @@ var PhaseStore = class {
|
|
|
70663
70805
|
|
|
70664
70806
|
// src/goal/checkpoint.ts
|
|
70665
70807
|
init_atomic_write();
|
|
70666
|
-
import * as
|
|
70667
|
-
import * as
|
|
70808
|
+
import * as fsp36 from "node:fs/promises";
|
|
70809
|
+
import * as path84 from "node:path";
|
|
70668
70810
|
var CheckpointManager = class {
|
|
70669
70811
|
store;
|
|
70670
70812
|
maxCheckpoints;
|
|
@@ -70675,10 +70817,10 @@ var CheckpointManager = class {
|
|
|
70675
70817
|
this.store = opts.store;
|
|
70676
70818
|
this.maxCheckpoints = opts.maxCheckpoints ?? 10;
|
|
70677
70819
|
this.maxCheckpointAgeMs = opts.maxCheckpointAgeMs ?? 7 * 24 * 60 * 60 * 1e3;
|
|
70678
|
-
this.baseDir = opts.baseDir ??
|
|
70820
|
+
this.baseDir = opts.baseDir ?? path84.join(opts.store.baseDir, ".checkpoints");
|
|
70679
70821
|
}
|
|
70680
70822
|
async initialize() {
|
|
70681
|
-
await
|
|
70823
|
+
await fsp36.mkdir(this.baseDir, { recursive: true });
|
|
70682
70824
|
await this.loadFromDisk();
|
|
70683
70825
|
}
|
|
70684
70826
|
async saveCheckpoint(graph, label) {
|
|
@@ -70741,14 +70883,14 @@ var CheckpointManager = class {
|
|
|
70741
70883
|
return true;
|
|
70742
70884
|
}
|
|
70743
70885
|
async saveToDisk(checkpoint) {
|
|
70744
|
-
const filePath =
|
|
70886
|
+
const filePath = path84.join(this.baseDir, `${checkpoint.graphId}.json`);
|
|
70745
70887
|
const serialized = {
|
|
70746
70888
|
...checkpoint
|
|
70747
70889
|
};
|
|
70748
70890
|
await withFileLock(filePath, async () => {
|
|
70749
70891
|
let existing = [];
|
|
70750
70892
|
try {
|
|
70751
|
-
const raw = await
|
|
70893
|
+
const raw = await fsp36.readFile(filePath, "utf8");
|
|
70752
70894
|
const parsed = JSON.parse(raw);
|
|
70753
70895
|
if (Array.isArray(parsed)) {
|
|
70754
70896
|
existing = parsed;
|
|
@@ -70762,23 +70904,23 @@ var CheckpointManager = class {
|
|
|
70762
70904
|
async deleteFromDisk(checkpointId) {
|
|
70763
70905
|
let entries;
|
|
70764
70906
|
try {
|
|
70765
|
-
entries = await
|
|
70907
|
+
entries = await fsp36.readdir(this.baseDir);
|
|
70766
70908
|
} catch {
|
|
70767
70909
|
return;
|
|
70768
70910
|
}
|
|
70769
70911
|
for (const filename of entries) {
|
|
70770
70912
|
if (!filename.endsWith(".json")) continue;
|
|
70771
|
-
const filePath =
|
|
70913
|
+
const filePath = path84.join(this.baseDir, filename);
|
|
70772
70914
|
try {
|
|
70773
70915
|
await withFileLock(filePath, async () => {
|
|
70774
|
-
const raw = await
|
|
70916
|
+
const raw = await fsp36.readFile(filePath, "utf8");
|
|
70775
70917
|
const parsed = JSON.parse(raw);
|
|
70776
70918
|
if (!Array.isArray(parsed)) return;
|
|
70777
70919
|
const existing = parsed;
|
|
70778
70920
|
const filtered = existing.filter((c) => c.id !== checkpointId);
|
|
70779
70921
|
if (filtered.length !== existing.length) {
|
|
70780
70922
|
if (filtered.length === 0) {
|
|
70781
|
-
await
|
|
70923
|
+
await fsp36.unlink(filePath);
|
|
70782
70924
|
} else {
|
|
70783
70925
|
await atomicWrite(filePath, JSON.stringify(filtered, null, 2), { mode: 384 });
|
|
70784
70926
|
}
|
|
@@ -70791,15 +70933,15 @@ var CheckpointManager = class {
|
|
|
70791
70933
|
async loadFromDisk() {
|
|
70792
70934
|
let entries;
|
|
70793
70935
|
try {
|
|
70794
|
-
entries = await
|
|
70936
|
+
entries = await fsp36.readdir(this.baseDir);
|
|
70795
70937
|
} catch {
|
|
70796
70938
|
return;
|
|
70797
70939
|
}
|
|
70798
70940
|
for (const filename of entries) {
|
|
70799
70941
|
if (!filename.endsWith(".json")) continue;
|
|
70800
|
-
const filePath =
|
|
70942
|
+
const filePath = path84.join(this.baseDir, filename);
|
|
70801
70943
|
try {
|
|
70802
|
-
const raw = await
|
|
70944
|
+
const raw = await fsp36.readFile(filePath, "utf8");
|
|
70803
70945
|
const parsed = JSON.parse(raw);
|
|
70804
70946
|
if (!Array.isArray(parsed)) continue;
|
|
70805
70947
|
const checkpoints = parsed;
|
|
@@ -71676,7 +71818,7 @@ function countShellHooks(hooks) {
|
|
|
71676
71818
|
|
|
71677
71819
|
// src/hq/session-bridge.ts
|
|
71678
71820
|
import * as fs38 from "node:fs";
|
|
71679
|
-
import * as
|
|
71821
|
+
import * as fsp37 from "node:fs/promises";
|
|
71680
71822
|
|
|
71681
71823
|
// src/types/session-markers.ts
|
|
71682
71824
|
var SESSION_MARKER_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -72134,12 +72276,12 @@ function startSessionTelemetryBridge(opts) {
|
|
|
72134
72276
|
if (disposed || tailing) return;
|
|
72135
72277
|
tailing = true;
|
|
72136
72278
|
try {
|
|
72137
|
-
const stat33 = await
|
|
72279
|
+
const stat33 = await fsp37.stat(sessionFile).catch(() => null);
|
|
72138
72280
|
if (disposed) return;
|
|
72139
72281
|
if (!stat33) return;
|
|
72140
72282
|
setupWatcher2();
|
|
72141
72283
|
if (stat33.size <= offset) return;
|
|
72142
|
-
const fd = await
|
|
72284
|
+
const fd = await fsp37.open(sessionFile, "r");
|
|
72143
72285
|
try {
|
|
72144
72286
|
if (disposed) return;
|
|
72145
72287
|
const len = stat33.size - offset;
|
|
@@ -72568,7 +72710,7 @@ function startCostTelemetryBridge(opts) {
|
|
|
72568
72710
|
init_atomic_write();
|
|
72569
72711
|
import { createHash as createHash29 } from "node:crypto";
|
|
72570
72712
|
import * as fs39 from "node:fs/promises";
|
|
72571
|
-
import * as
|
|
72713
|
+
import * as path85 from "node:path";
|
|
72572
72714
|
|
|
72573
72715
|
// src/hq/write-queues.ts
|
|
72574
72716
|
var BestEffortBatchQueue = class _BestEffortBatchQueue {
|
|
@@ -72663,7 +72805,7 @@ var HqKanbanStore = class {
|
|
|
72663
72805
|
writers = /* @__PURE__ */ new Map();
|
|
72664
72806
|
mergeChains = /* @__PURE__ */ new Map();
|
|
72665
72807
|
constructor(dataDir) {
|
|
72666
|
-
this.dirPath =
|
|
72808
|
+
this.dirPath = path85.join(dataDir, "kanban");
|
|
72667
72809
|
}
|
|
72668
72810
|
async load(projectId) {
|
|
72669
72811
|
const cached2 = this.cache.get(projectId);
|
|
@@ -72771,7 +72913,7 @@ var HqKanbanStore = class {
|
|
|
72771
72913
|
}
|
|
72772
72914
|
filePath(projectId) {
|
|
72773
72915
|
const safe = createHash29("sha256").update(projectId).digest("hex");
|
|
72774
|
-
return
|
|
72916
|
+
return path85.join(this.dirPath, `${safe}.json`);
|
|
72775
72917
|
}
|
|
72776
72918
|
};
|
|
72777
72919
|
function emptyKanbanSnapshot(projectId) {
|
|
@@ -72790,7 +72932,7 @@ function compareKanbanRecord(a, b) {
|
|
|
72790
72932
|
init_file_permissions();
|
|
72791
72933
|
init_atomic_write();
|
|
72792
72934
|
import * as fs41 from "node:fs/promises";
|
|
72793
|
-
import * as
|
|
72935
|
+
import * as path86 from "node:path";
|
|
72794
72936
|
|
|
72795
72937
|
// src/hq/persistence/jsonl-io.ts
|
|
72796
72938
|
import * as fs40 from "node:fs/promises";
|
|
@@ -73012,7 +73154,7 @@ var HqEventLog = class {
|
|
|
73012
73154
|
counted = false;
|
|
73013
73155
|
hydration;
|
|
73014
73156
|
constructor(opts) {
|
|
73015
|
-
this.filePath =
|
|
73157
|
+
this.filePath = path86.join(opts.dataDir, "events.jsonl");
|
|
73016
73158
|
this.maxLines = opts.maxLines ?? DEFAULT_EVENT_LOG_MAX_LINES;
|
|
73017
73159
|
this.rotateKeep = opts.rotateKeep ?? DEFAULT_EVENT_LOG_ROTATE_KEEP;
|
|
73018
73160
|
this.maxBytes = opts.maxBytes ?? DEFAULT_EVENT_LOG_MAX_BYTES;
|
|
@@ -73238,7 +73380,7 @@ async function readRecentEvents(filePath, limit, typeFilter) {
|
|
|
73238
73380
|
// src/hq/persistence/simple-log.ts
|
|
73239
73381
|
init_atomic_write();
|
|
73240
73382
|
import * as fs42 from "node:fs/promises";
|
|
73241
|
-
import * as
|
|
73383
|
+
import * as path87 from "node:path";
|
|
73242
73384
|
var HqSimpleLog = class {
|
|
73243
73385
|
filePath;
|
|
73244
73386
|
maxLines;
|
|
@@ -73249,7 +73391,7 @@ var HqSimpleLog = class {
|
|
|
73249
73391
|
counted = false;
|
|
73250
73392
|
hydration;
|
|
73251
73393
|
constructor(opts) {
|
|
73252
|
-
this.filePath =
|
|
73394
|
+
this.filePath = path87.join(opts.dataDir, opts.filename);
|
|
73253
73395
|
this.maxLines = opts.maxLines ?? Infinity;
|
|
73254
73396
|
this.rotateKeep = opts.rotateKeep ?? this.maxLines;
|
|
73255
73397
|
this.readLimit = opts.readLimit ?? Infinity;
|
|
@@ -73320,12 +73462,12 @@ var HqSimpleLog = class {
|
|
|
73320
73462
|
// src/hq/persistence/snapshot-store.ts
|
|
73321
73463
|
init_atomic_write();
|
|
73322
73464
|
import * as fs43 from "node:fs/promises";
|
|
73323
|
-
import * as
|
|
73465
|
+
import * as path88 from "node:path";
|
|
73324
73466
|
var HqSnapshotStore = class {
|
|
73325
73467
|
filePath;
|
|
73326
73468
|
writer;
|
|
73327
73469
|
constructor(opts) {
|
|
73328
|
-
this.filePath =
|
|
73470
|
+
this.filePath = path88.join(opts.dataDir, "snapshot.json");
|
|
73329
73471
|
this.writer = new BestEffortLatestQueue(
|
|
73330
73472
|
(snapshot) => atomicWrite(this.filePath, JSON.stringify(snapshot), { mode: 384 })
|
|
73331
73473
|
);
|
|
@@ -73353,7 +73495,7 @@ var HqSnapshotStore = class {
|
|
|
73353
73495
|
init_file_permissions();
|
|
73354
73496
|
init_atomic_write();
|
|
73355
73497
|
import * as fs44 from "node:fs/promises";
|
|
73356
|
-
import * as
|
|
73498
|
+
import * as path89 from "node:path";
|
|
73357
73499
|
var HqTimeseriesStore = class {
|
|
73358
73500
|
filePath;
|
|
73359
73501
|
bucketMs;
|
|
@@ -73366,7 +73508,7 @@ var HqTimeseriesStore = class {
|
|
|
73366
73508
|
loaded = false;
|
|
73367
73509
|
loadPromise;
|
|
73368
73510
|
constructor(opts) {
|
|
73369
|
-
this.filePath =
|
|
73511
|
+
this.filePath = path89.join(opts.dataDir, "timeseries.jsonl");
|
|
73370
73512
|
this.bucketMs = opts.bucketMs ?? 5 * 60 * 1e3;
|
|
73371
73513
|
this.maxBuckets = opts.maxBuckets ?? 2016;
|
|
73372
73514
|
this.writer = new BestEffortLatestQueue((snapshot) => this.flushInternal(snapshot));
|
|
@@ -75571,8 +75713,8 @@ function definePlugin(metadata, factory) {
|
|
|
75571
75713
|
// src/plugins/auto-review-plugin.ts
|
|
75572
75714
|
import { spawn as spawn9 } from "node:child_process";
|
|
75573
75715
|
import { createHash as createHash31 } from "node:crypto";
|
|
75574
|
-
import * as
|
|
75575
|
-
import * as
|
|
75716
|
+
import * as fsp38 from "node:fs/promises";
|
|
75717
|
+
import * as path90 from "node:path";
|
|
75576
75718
|
init_error();
|
|
75577
75719
|
|
|
75578
75720
|
// src/plugins/review-claim-registry.ts
|
|
@@ -75864,7 +76006,7 @@ function trimKnownFingerprints(map, max) {
|
|
|
75864
76006
|
map.delete(oldest);
|
|
75865
76007
|
}
|
|
75866
76008
|
}
|
|
75867
|
-
function buildReviewerModelPool(provider, model, fallbackModels = []) {
|
|
76009
|
+
function buildReviewerModelPool(provider, model, fallbackModels = [], statusTracker) {
|
|
75868
76010
|
const primaryProvider = provider.trim();
|
|
75869
76011
|
const primaryModel = model.trim();
|
|
75870
76012
|
const primaryRef = primaryProvider && primaryModel ? `${primaryProvider}/${primaryModel}` : "";
|
|
@@ -75879,12 +76021,20 @@ function buildReviewerModelPool(provider, model, fallbackModels = []) {
|
|
|
75879
76021
|
if (seen.has(normalized)) continue;
|
|
75880
76022
|
seen.add(ref);
|
|
75881
76023
|
seen.add(normalized);
|
|
76024
|
+
if (statusTracker && !statusTracker.isAvailable(parsed.provider.trim(), parsed.model.trim())) {
|
|
76025
|
+
continue;
|
|
76026
|
+
}
|
|
75882
76027
|
pool.push(normalized);
|
|
75883
76028
|
}
|
|
75884
76029
|
return pool;
|
|
75885
76030
|
}
|
|
75886
|
-
function selectRoundRobinReviewerAssignment(pool, cursor, fallbackProvider = "", fallbackModel = "") {
|
|
75887
|
-
|
|
76031
|
+
function selectRoundRobinReviewerAssignment(pool, cursor, fallbackProvider = "", fallbackModel = "", statusTracker) {
|
|
76032
|
+
const filteredPool = statusTracker ? pool.filter((ref) => {
|
|
76033
|
+
const parsed2 = parseModelRef(ref);
|
|
76034
|
+
if (!parsed2.provider?.trim() || !parsed2.model.trim()) return false;
|
|
76035
|
+
return statusTracker.isAvailable(parsed2.provider.trim(), parsed2.model.trim());
|
|
76036
|
+
}) : pool;
|
|
76037
|
+
if (filteredPool.length === 0) {
|
|
75888
76038
|
return {
|
|
75889
76039
|
provider: fallbackProvider,
|
|
75890
76040
|
model: fallbackModel,
|
|
@@ -75892,13 +76042,13 @@ function selectRoundRobinReviewerAssignment(pool, cursor, fallbackProvider = "",
|
|
|
75892
76042
|
nextCursor: cursor + 1
|
|
75893
76043
|
};
|
|
75894
76044
|
}
|
|
75895
|
-
const len =
|
|
76045
|
+
const len = filteredPool.length;
|
|
75896
76046
|
const idx = (cursor % len + len) % len;
|
|
75897
|
-
const primaryRef =
|
|
76047
|
+
const primaryRef = filteredPool[idx];
|
|
75898
76048
|
const parsed = parseModelRef(primaryRef);
|
|
75899
76049
|
const provider = parsed.provider?.trim() || fallbackProvider;
|
|
75900
76050
|
const model = parsed.model.trim() || fallbackModel;
|
|
75901
|
-
const fallbackModels = [...
|
|
76051
|
+
const fallbackModels = [...filteredPool.slice(idx + 1), ...filteredPool.slice(0, idx)];
|
|
75902
76052
|
return {
|
|
75903
76053
|
provider,
|
|
75904
76054
|
model,
|
|
@@ -75940,57 +76090,49 @@ function parseReviewSeverity(text2) {
|
|
|
75940
76090
|
const result = { critical: 0, high: 0, medium: 0 };
|
|
75941
76091
|
if (!text2) return result;
|
|
75942
76092
|
for (const level of ["critical", "high", "medium"]) {
|
|
75943
|
-
const
|
|
75944
|
-
const countMatch = text2.match(countRe);
|
|
76093
|
+
const countMatch = text2.match(new RegExp(`###\\s*${level}\\s*\\((\\d+)\\)`, "i"));
|
|
75945
76094
|
if (countMatch?.[1]) {
|
|
75946
76095
|
result[level] = Number.parseInt(countMatch[1], 10);
|
|
75947
76096
|
continue;
|
|
75948
76097
|
}
|
|
75949
|
-
const
|
|
75950
|
-
|
|
75951
|
-
|
|
75952
|
-
|
|
75953
|
-
result[level] = items ? items.length : 0;
|
|
75954
|
-
}
|
|
76098
|
+
const section = text2.match(
|
|
76099
|
+
new RegExp(`###\\s*${level}[^\\n]*\\n([\\s\\S]*?)(?=###|$)`, "i")
|
|
76100
|
+
)?.[1];
|
|
76101
|
+
result[level] = section?.match(/^\s*\d+\.\s/gm)?.length ?? 0;
|
|
75955
76102
|
}
|
|
75956
76103
|
return result;
|
|
75957
76104
|
}
|
|
75958
|
-
var SECURITY_KEYWORDS = [
|
|
75959
|
-
"injection",
|
|
75960
|
-
"xss",
|
|
75961
|
-
"csrf",
|
|
75962
|
-
"ssrf",
|
|
75963
|
-
"sql",
|
|
75964
|
-
"secret",
|
|
75965
|
-
"credential",
|
|
75966
|
-
"password",
|
|
75967
|
-
"api key",
|
|
75968
|
-
"token",
|
|
75969
|
-
"auth",
|
|
75970
|
-
"shell injection",
|
|
75971
|
-
"command injection",
|
|
75972
|
-
"innerhtml",
|
|
75973
|
-
"deserialization",
|
|
75974
|
-
"path traversal",
|
|
75975
|
-
"hardcoded",
|
|
75976
|
-
"privilege",
|
|
75977
|
-
"owasp"
|
|
75978
|
-
];
|
|
75979
76105
|
function decideCascadeAgents(text2, severities) {
|
|
75980
76106
|
const agents = /* @__PURE__ */ new Set();
|
|
75981
|
-
if (severities.critical > 0 || severities.high > 0)
|
|
75982
|
-
|
|
75983
|
-
|
|
75984
|
-
|
|
75985
|
-
|
|
75986
|
-
|
|
75987
|
-
|
|
76107
|
+
if (severities.critical > 0 || severities.high > 0) agents.add("bug-hunter");
|
|
76108
|
+
const securityKeywords = [
|
|
76109
|
+
"injection",
|
|
76110
|
+
"xss",
|
|
76111
|
+
"csrf",
|
|
76112
|
+
"ssrf",
|
|
76113
|
+
"sql",
|
|
76114
|
+
"secret",
|
|
76115
|
+
"credential",
|
|
76116
|
+
"password",
|
|
76117
|
+
"api key",
|
|
76118
|
+
"token",
|
|
76119
|
+
"auth",
|
|
76120
|
+
"shell injection",
|
|
76121
|
+
"command injection",
|
|
76122
|
+
"innerhtml",
|
|
76123
|
+
"deserialization",
|
|
76124
|
+
"path traversal",
|
|
76125
|
+
"hardcoded",
|
|
76126
|
+
"privilege",
|
|
76127
|
+
"owasp"
|
|
76128
|
+
];
|
|
76129
|
+
const criticalHighSections = text2.toLowerCase().matchAll(/###\s*(?:critical|high)[^\n]*\n([\s\S]*?)(?=###|$)/gi);
|
|
76130
|
+
for (const section of criticalHighSections) {
|
|
75988
76131
|
const body = section[1] ?? "";
|
|
75989
|
-
if (
|
|
76132
|
+
if (securityKeywords.some((keyword) => body.includes(keyword))) {
|
|
75990
76133
|
agents.add("security-scanner");
|
|
75991
76134
|
break;
|
|
75992
76135
|
}
|
|
75993
|
-
section = criticalHighRe.exec(lower);
|
|
75994
76136
|
}
|
|
75995
76137
|
return [...agents];
|
|
75996
76138
|
}
|
|
@@ -76057,9 +76199,9 @@ async function snapshotChangedFiles(cwd) {
|
|
|
76057
76199
|
if (file.path.startsWith(".wrongstack/")) continue;
|
|
76058
76200
|
if (budget <= 0) break;
|
|
76059
76201
|
try {
|
|
76060
|
-
const stat33 = await
|
|
76202
|
+
const stat33 = await fsp38.stat(path90.join(cwd, file.path));
|
|
76061
76203
|
if (!stat33.isFile() || stat33.size > MAX_SNAPSHOT_FILE_BYTES) continue;
|
|
76062
|
-
const content = await
|
|
76204
|
+
const content = await fsp38.readFile(path90.join(cwd, file.path), "utf8");
|
|
76063
76205
|
budget -= content.length;
|
|
76064
76206
|
snapshots.push({
|
|
76065
76207
|
...file,
|
|
@@ -76083,9 +76225,8 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
76083
76225
|
"Detects git-tracked file edits and dispatches review subagents",
|
|
76084
76226
|
"with configurable provider/model/fallback.",
|
|
76085
76227
|
"",
|
|
76086
|
-
"
|
|
76087
|
-
"
|
|
76088
|
-
"automatically spawned to investigate.",
|
|
76228
|
+
"Reports are persisted and shown as passive notifications.",
|
|
76229
|
+
"They never wake the leader or spawn mutating follow-up agents.",
|
|
76089
76230
|
"",
|
|
76090
76231
|
"Commands:",
|
|
76091
76232
|
" /auto-review Show current status and config",
|
|
@@ -76100,11 +76241,8 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
76100
76241
|
" debounceMs debounce window (default 15000)",
|
|
76101
76242
|
" maxFilesPerBatch max files per review (default 15)",
|
|
76102
76243
|
" maxConcurrentReviews max parallel reviews (default 2)",
|
|
76103
|
-
|
|
76104
|
-
"
|
|
76105
|
-
' findings reach this severity. "high" fires',
|
|
76106
|
-
' on any High+ finding; "critical" only on Critical.',
|
|
76107
|
-
" maxCascadeDepth max fix+re-review cycles (default 2, 0=off)"
|
|
76244
|
+
" cascadeOn off | critical | high (default off)",
|
|
76245
|
+
" maxCascadeDepth max fix+re-review cycles (default 2)"
|
|
76108
76246
|
].join("\n"),
|
|
76109
76247
|
async run(args) {
|
|
76110
76248
|
const cfg = getConfig();
|
|
@@ -76120,7 +76258,6 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
76120
76258
|
};
|
|
76121
76259
|
}
|
|
76122
76260
|
const inFlight = getInFlightCount();
|
|
76123
|
-
const cascadeDesc = cfg.cascadeOn === "off" ? "disabled" : `on ${cfg.cascadeOn}+ \u2192 spawns security-scanner/bug-hunter`;
|
|
76124
76261
|
return {
|
|
76125
76262
|
message: [
|
|
76126
76263
|
`\u{1F4CB} Auto Review \u2014 ${cfg.enabled ? "\u2705 enabled" : "\u23F8\uFE0F disabled"}`,
|
|
@@ -76131,7 +76268,7 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
76131
76268
|
` Debounce: ${cfg.debounceMs} ms`,
|
|
76132
76269
|
` Max files: ${cfg.maxFilesPerBatch}`,
|
|
76133
76270
|
` Max parallel: ${cfg.maxConcurrentReviews}`,
|
|
76134
|
-
` Cascade: ${
|
|
76271
|
+
` Cascade: ${cfg.cascadeOn === "off" ? "off" : `on ${cfg.cascadeOn}+ \u2192 spawns security-scanner/bug-hunter`}`,
|
|
76135
76272
|
` Max depth: ${cfg.maxCascadeDepth} re-review cycle(s)`,
|
|
76136
76273
|
` In-flight: ${inFlight} review(s)`,
|
|
76137
76274
|
"",
|
|
@@ -76409,7 +76546,7 @@ function createAutoReviewPlugin() {
|
|
|
76409
76546
|
for (const f of allChanged) {
|
|
76410
76547
|
if (f.path.startsWith(".wrongstack/")) continue;
|
|
76411
76548
|
try {
|
|
76412
|
-
await
|
|
76549
|
+
await fsp38.access(path90.join(cwd, f.path));
|
|
76413
76550
|
existing.push(f);
|
|
76414
76551
|
} catch {
|
|
76415
76552
|
}
|
|
@@ -76419,8 +76556,8 @@ function createAutoReviewPlugin() {
|
|
|
76419
76556
|
const filesWithContent = [];
|
|
76420
76557
|
for (const f of toReview) {
|
|
76421
76558
|
try {
|
|
76422
|
-
const absPath =
|
|
76423
|
-
const content = await
|
|
76559
|
+
const absPath = path90.join(cwd, f.path);
|
|
76560
|
+
const content = await fsp38.readFile(absPath, "utf8");
|
|
76424
76561
|
filesWithContent.push({ path: f.path, status: f.status, content });
|
|
76425
76562
|
} catch {
|
|
76426
76563
|
}
|
|
@@ -76526,7 +76663,7 @@ init_review_finding_store();
|
|
|
76526
76663
|
|
|
76527
76664
|
// src/plugins/review-finding-parser.ts
|
|
76528
76665
|
init_review_finding_types();
|
|
76529
|
-
import { randomUUID as
|
|
76666
|
+
import { randomUUID as randomUUID37 } from "node:crypto";
|
|
76530
76667
|
var SUGGEST_LINE = /^\s*(?:→|->|=>)\s*(.+)$/;
|
|
76531
76668
|
var DURATION_LINE = /^Duration:\s*(\d+)s\s*$/im;
|
|
76532
76669
|
function parseChimeraReviewReport(reportText, context = {}) {
|
|
@@ -76534,7 +76671,7 @@ function parseChimeraReviewReport(reportText, context = {}) {
|
|
|
76534
76671
|
return { findings: [], unparseableCount: 0 };
|
|
76535
76672
|
}
|
|
76536
76673
|
const findings = [];
|
|
76537
|
-
const reportId = context.reportId ??
|
|
76674
|
+
const reportId = context.reportId ?? randomUUID37();
|
|
76538
76675
|
let unparseableCount = 0;
|
|
76539
76676
|
let durationSeconds;
|
|
76540
76677
|
let currentSeverity = null;
|
|
@@ -76635,7 +76772,7 @@ function parseFindingSegment(segment, severity, context) {
|
|
|
76635
76772
|
const fullDesc = suggestions.length > 0 ? cleanDesc + "\n" + suggestions.map((s) => " \u2192 " + s).join("\n") : cleanDesc;
|
|
76636
76773
|
const suggestedFix = suggestions.length > 0 ? suggestions.join("\n") : void 0;
|
|
76637
76774
|
return {
|
|
76638
|
-
id:
|
|
76775
|
+
id: randomUUID37(),
|
|
76639
76776
|
fingerprint: computeFindingFingerprint(file ?? "", line ?? null, title),
|
|
76640
76777
|
severity,
|
|
76641
76778
|
source: normalizeFindingSource(context.reviewType),
|
|
@@ -76646,7 +76783,7 @@ function parseFindingSegment(segment, severity, context) {
|
|
|
76646
76783
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
76647
76784
|
status: "active",
|
|
76648
76785
|
originReport: {
|
|
76649
|
-
reportId: context.reportId ??
|
|
76786
|
+
reportId: context.reportId ?? randomUUID37(),
|
|
76650
76787
|
sessionId: context.sessionId ?? "",
|
|
76651
76788
|
agentId: context.agentId ?? "",
|
|
76652
76789
|
reviewerModel: context.reviewerModel ?? ""
|
|
@@ -77439,7 +77576,9 @@ function createChimeraPlugin() {
|
|
|
77439
77576
|
api.log.info("[chimera] skipped \u2014 changed files already have reviews in progress");
|
|
77440
77577
|
return;
|
|
77441
77578
|
}
|
|
77442
|
-
api.log.info(
|
|
77579
|
+
api.log.info(
|
|
77580
|
+
`[chimera] emitted review_needed event (${emittedBundle.files.length} files)`
|
|
77581
|
+
);
|
|
77443
77582
|
} catch (err) {
|
|
77444
77583
|
api.log.warn(`[chimera] session.ended handler failed: ${toErrorMessage(err)}`);
|
|
77445
77584
|
}
|
|
@@ -87978,6 +88117,7 @@ export {
|
|
|
87978
88117
|
ReplayLogStore,
|
|
87979
88118
|
ReplayProviderRunner,
|
|
87980
88119
|
RunController,
|
|
88120
|
+
SAGE_INJECTOR_HEADINGS,
|
|
87981
88121
|
SECURITY_SCANNER_AGENT,
|
|
87982
88122
|
SEMANTIC_TUNE_KNOBS,
|
|
87983
88123
|
SESSION_MARKER_EVENT_TYPES,
|
|
@@ -88107,6 +88247,7 @@ export {
|
|
|
88107
88247
|
buildWin32CmdShimInvocation,
|
|
88108
88248
|
canCaptureNewLearned,
|
|
88109
88249
|
canonicalProjectRoot,
|
|
88250
|
+
capSageLines,
|
|
88110
88251
|
captureLearnedFromAgentOutput,
|
|
88111
88252
|
captureLearnedFromAgentOutputDetailed,
|
|
88112
88253
|
checkConnectivity,
|
|
@@ -88688,6 +88829,7 @@ export {
|
|
|
88688
88829
|
slugify2 as slugify,
|
|
88689
88830
|
slugifyProjectAgentRole,
|
|
88690
88831
|
smartDefaultFallbackChain,
|
|
88832
|
+
splitSageOutputBlock,
|
|
88691
88833
|
sqliteCachePragmas,
|
|
88692
88834
|
sshManagerServer,
|
|
88693
88835
|
stableStringify3 as stableStringify,
|