@inerrata-corporation/errata 2.0.16 → 2.0.17-dev.3369
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/consolidate-worker.mjs +26 -10
- package/errata.mjs +311 -100
- package/package.json +1 -1
- package/pass-worker.mjs +12 -11
package/consolidate-worker.mjs
CHANGED
|
@@ -802,6 +802,12 @@ function identityId(input) {
|
|
|
802
802
|
}
|
|
803
803
|
case "Claim":
|
|
804
804
|
return canonicalClaimId(input.text, input.scope);
|
|
805
|
+
case "WitnessedVulnerability":
|
|
806
|
+
return genericCanonicalId("dvuln", {
|
|
807
|
+
workspaceId: input.workspaceId,
|
|
808
|
+
target: input.target.replace(/\\/g, "/").replace(/^\.\//, ""),
|
|
809
|
+
statement: canonicalClaimId(input.statement.trim())
|
|
810
|
+
});
|
|
805
811
|
case "DesignProblem":
|
|
806
812
|
return `${DESIGN_PROBLEM_PREFIX}${canonicalClaimId(input.statement.trim())}`.slice(
|
|
807
813
|
0,
|
|
@@ -15258,7 +15264,7 @@ var init_edge_rules = __esm({
|
|
|
15258
15264
|
// anchor anything at anything, e.g. at a public `Package`, quietly re-creating
|
|
15259
15265
|
// the code↔collective bridge the membrane exists to prevent.
|
|
15260
15266
|
ANCHORED_AT: {
|
|
15261
|
-
from: ["Problem", "Solution", "RootCause", "Claim"],
|
|
15267
|
+
from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim"],
|
|
15262
15268
|
to: ["Symbol"]
|
|
15263
15269
|
},
|
|
15264
15270
|
// ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
|
|
@@ -15303,7 +15309,7 @@ var init_edge_rules = __esm({
|
|
|
15303
15309
|
// `Component` joined the target set with OM-agent-anchors: an agent-named
|
|
15304
15310
|
// component ("React Router") is the same knowledge→named-unit anchor shape as
|
|
15305
15311
|
// a Tool — the knowledge is ABOUT it, not dependent on it.
|
|
15306
|
-
CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15312
|
+
CONCERNS: { from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15307
15313
|
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15308
15314
|
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15309
15315
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
@@ -16410,7 +16416,8 @@ CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
|
16410
16416
|
-- its table row. Partial, so it maintains a few thousand rows instead of adding
|
|
16411
16417
|
-- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
|
|
16412
16418
|
DROP INDEX IF EXISTS nodes_live_fingerprint;
|
|
16413
|
-
|
|
16419
|
+
DROP INDEX IF EXISTS nodes_instance_fingerprint;
|
|
16420
|
+
CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
|
|
16414
16421
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
16415
16422
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
16416
16423
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
@@ -16481,7 +16488,7 @@ function rowToEdge(r) {
|
|
|
16481
16488
|
attrs: JSON.parse(r.attrs_json)
|
|
16482
16489
|
};
|
|
16483
16490
|
}
|
|
16484
|
-
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
|
|
16491
|
+
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
16485
16492
|
var SqliteGraphStore = class {
|
|
16486
16493
|
db;
|
|
16487
16494
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
@@ -17159,8 +17166,8 @@ var SqliteGraphStore = class {
|
|
|
17159
17166
|
const r2 = this.db.prepare(
|
|
17160
17167
|
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
17161
17168
|
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
17162
|
-
FROM nodes INDEXED BY
|
|
17163
|
-
WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
|
|
17169
|
+
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
17170
|
+
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
17164
17171
|
).get();
|
|
17165
17172
|
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
17166
17173
|
}
|
|
@@ -19389,7 +19396,7 @@ function generalizeFileRefs(text, fileLexicon, level = 1) {
|
|
|
19389
19396
|
}
|
|
19390
19397
|
|
|
19391
19398
|
// src/instance-ingest.ts
|
|
19392
|
-
var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
|
|
19399
|
+
var INSTANCE_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
19393
19400
|
var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
|
|
19394
19401
|
var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
|
|
19395
19402
|
var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
|
|
@@ -19509,6 +19516,10 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19509
19516
|
attrs: {
|
|
19510
19517
|
scope: stripCodebaseScope(n.attrs["scope"]),
|
|
19511
19518
|
...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
|
|
19519
|
+
...n.attrs["privateFinding"] ? { privateFinding: true } : {},
|
|
19520
|
+
...n.attrs["assessment"] ? { assessment: n.attrs["assessment"] } : {},
|
|
19521
|
+
...n.attrs["sourceClass"] ? { sourceClass: n.attrs["sourceClass"] } : {},
|
|
19522
|
+
...n.attrs["resolutionWitnessed"] ? { resolutionWitnessed: true, resolvedAt: n.attrs["resolvedAt"] } : {},
|
|
19512
19523
|
...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {},
|
|
19513
19524
|
...preserved.length > 0 ? { preservedSymbols: preserved } : {},
|
|
19514
19525
|
...internal.length > 0 ? { internalSymbols: internal } : {}
|
|
@@ -19523,6 +19534,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19523
19534
|
const semanticEndpoints = /* @__PURE__ */ new Map();
|
|
19524
19535
|
const includedByLabel = /* @__PURE__ */ new Map();
|
|
19525
19536
|
const composition = {
|
|
19537
|
+
privateTargetMissing: 0,
|
|
19526
19538
|
candidates: 0,
|
|
19527
19539
|
shipped: 0,
|
|
19528
19540
|
cloudSourced: 0,
|
|
@@ -19568,11 +19580,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19568
19580
|
if (walked.has(n.id)) continue;
|
|
19569
19581
|
walked.add(n.id);
|
|
19570
19582
|
composition.candidates++;
|
|
19583
|
+
if ((n.attrs["privateFinding"] || n.label === "Vulnerability") && !opts.project) {
|
|
19584
|
+
composition.privateTargetMissing++;
|
|
19585
|
+
continue;
|
|
19586
|
+
}
|
|
19571
19587
|
if (n.attrs["source"] === "cloud") {
|
|
19572
19588
|
composition.cloudSourced++;
|
|
19573
19589
|
continue;
|
|
19574
19590
|
}
|
|
19575
|
-
if (
|
|
19591
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
|
|
19576
19592
|
composition.falsePositive++;
|
|
19577
19593
|
continue;
|
|
19578
19594
|
}
|
|
@@ -19595,7 +19611,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19595
19611
|
composition.terminalRefused++;
|
|
19596
19612
|
continue;
|
|
19597
19613
|
}
|
|
19598
|
-
if (!noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
19614
|
+
if (!n.attrs["privateFinding"] && !noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
19599
19615
|
composition.notNovel++;
|
|
19600
19616
|
continue;
|
|
19601
19617
|
}
|
|
@@ -19638,7 +19654,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19638
19654
|
}
|
|
19639
19655
|
if (targets.length === 0) continue;
|
|
19640
19656
|
const shipped = shippedById.get(sourceId);
|
|
19641
|
-
if (shipped) {
|
|
19657
|
+
if (shipped && !shipped.attrs["privateFinding"]) {
|
|
19642
19658
|
const anchors = (label) => targets.filter((x) => x.t.label === label).map(
|
|
19643
19659
|
(x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
|
|
19644
19660
|
).filter((a) => a != null).sort();
|
package/errata.mjs
CHANGED
|
@@ -139,6 +139,7 @@ var init_castalia = __esm({
|
|
|
139
139
|
ROUTING_REASONS = [
|
|
140
140
|
/** Harness org (CTF lane, e2e battery, canary) — forced org-private. */
|
|
141
141
|
"harness-clamp",
|
|
142
|
+
"witnessed-vulnerability-private",
|
|
142
143
|
/** A signed gateway write target named the destination. */
|
|
143
144
|
"signed-target-public",
|
|
144
145
|
"signed-target-org",
|
|
@@ -1080,6 +1081,12 @@ function identityId(input) {
|
|
|
1080
1081
|
}
|
|
1081
1082
|
case "Claim":
|
|
1082
1083
|
return canonicalClaimId(input.text, input.scope);
|
|
1084
|
+
case "WitnessedVulnerability":
|
|
1085
|
+
return genericCanonicalId("dvuln", {
|
|
1086
|
+
workspaceId: input.workspaceId,
|
|
1087
|
+
target: input.target.replace(/\\/g, "/").replace(/^\.\//, ""),
|
|
1088
|
+
statement: canonicalClaimId(input.statement.trim())
|
|
1089
|
+
});
|
|
1083
1090
|
case "DesignProblem":
|
|
1084
1091
|
return `${DESIGN_PROBLEM_PREFIX}${canonicalClaimId(input.statement.trim())}`.slice(
|
|
1085
1092
|
0,
|
|
@@ -15849,7 +15856,7 @@ var init_edge_rules = __esm({
|
|
|
15849
15856
|
// anchor anything at anything, e.g. at a public `Package`, quietly re-creating
|
|
15850
15857
|
// the code↔collective bridge the membrane exists to prevent.
|
|
15851
15858
|
ANCHORED_AT: {
|
|
15852
|
-
from: ["Problem", "Solution", "RootCause", "Claim"],
|
|
15859
|
+
from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim"],
|
|
15853
15860
|
to: ["Symbol"]
|
|
15854
15861
|
},
|
|
15855
15862
|
// ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
|
|
@@ -15894,7 +15901,7 @@ var init_edge_rules = __esm({
|
|
|
15894
15901
|
// `Component` joined the target set with OM-agent-anchors: an agent-named
|
|
15895
15902
|
// component ("React Router") is the same knowledge→named-unit anchor shape as
|
|
15896
15903
|
// a Tool — the knowledge is ABOUT it, not dependent on it.
|
|
15897
|
-
CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15904
|
+
CONCERNS: { from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15898
15905
|
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15899
15906
|
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15900
15907
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
@@ -18274,7 +18281,8 @@ CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
|
18274
18281
|
-- its table row. Partial, so it maintains a few thousand rows instead of adding
|
|
18275
18282
|
-- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
|
|
18276
18283
|
DROP INDEX IF EXISTS nodes_live_fingerprint;
|
|
18277
|
-
|
|
18284
|
+
DROP INDEX IF EXISTS nodes_instance_fingerprint;
|
|
18285
|
+
CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
|
|
18278
18286
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
18279
18287
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
18280
18288
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
@@ -18288,7 +18296,7 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
|
|
|
18288
18296
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
18289
18297
|
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
|
|
18290
18298
|
`;
|
|
18291
|
-
FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
|
|
18299
|
+
FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
18292
18300
|
SqliteGraphStore = class {
|
|
18293
18301
|
db;
|
|
18294
18302
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
@@ -18966,8 +18974,8 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
18966
18974
|
const r2 = this.db.prepare(
|
|
18967
18975
|
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
18968
18976
|
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
18969
|
-
FROM nodes INDEXED BY
|
|
18970
|
-
WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
|
|
18977
|
+
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
18978
|
+
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
18971
18979
|
).get();
|
|
18972
18980
|
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
18973
18981
|
}
|
|
@@ -20263,7 +20271,7 @@ function mergeContextEdge(store, from, to, type, ts) {
|
|
|
20263
20271
|
function deriveContextFromAnchors(store, problemId, ts, pkgIndex) {
|
|
20264
20272
|
const report = { languages: [], packages: [] };
|
|
20265
20273
|
const p = store.getNode(problemId);
|
|
20266
|
-
if (!p || p.label !== "Problem") return report;
|
|
20274
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
20267
20275
|
const files = store.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
20268
20276
|
if (files.length === 0) return report;
|
|
20269
20277
|
const idx = pkgIndex ?? buildPackageIndex(store);
|
|
@@ -20901,6 +20909,81 @@ function ingestDesignProblem(store, flag, opts) {
|
|
|
20901
20909
|
...reopenedFromRegression ? { reopenedFromRegression } : {}
|
|
20902
20910
|
};
|
|
20903
20911
|
}
|
|
20912
|
+
function ingestWitnessedVulnerability(store, flag, opts) {
|
|
20913
|
+
const statement = flag.statement.trim();
|
|
20914
|
+
if (isPlaceholderStatement(statement)) return { id: "", created: false, corroborated: false, upgraded: false, rejected: "statement" };
|
|
20915
|
+
const target = flag.target?.trim().replace(/\\/g, "/").replace(/^\.\//, "") ?? "";
|
|
20916
|
+
const file2 = target ? resolveFileNode(store, target, opts.workspaceId) : null;
|
|
20917
|
+
const symbol2 = file2 && flag.line !== void 0 && flag.fileText !== void 0 ? resolveEnclosingSymbol(store, file2.id, flag.line, flag.fileText) : null;
|
|
20918
|
+
const identityTarget = symbol2 ? String(symbol2.attrs["logicalId"] ?? `${target}#${symbol2.attrs["qname"] ?? symbol2.description}`) : target;
|
|
20919
|
+
const compatibleTarget = (n) => !symbol2 || !n.attrs["identityTarget"] || n.attrs["identityTarget"] === target || n.attrs["identityTarget"] === identityTarget;
|
|
20920
|
+
const bound = flag.existingId ? store.getNode(flag.existingId) : null;
|
|
20921
|
+
if (flag.existingId && (!bound || bound.label !== "Vulnerability" || bound.attrs["workspaceId"] !== opts.workspaceId || target && bound.attrs["affectedTarget"] && bound.attrs["affectedTarget"] !== target || !compatibleTarget(bound))) {
|
|
20922
|
+
return { id: "", created: false, corroborated: false, upgraded: false, rejected: "binding-conflict" };
|
|
20923
|
+
}
|
|
20924
|
+
const statementKey = identityId({ kind: "DesignProblem", statement });
|
|
20925
|
+
const restated = bound ? null : store.findNodesByLabel("Vulnerability").find((n) => n.attrs["workspaceId"] === opts.workspaceId && n.attrs["affectedTarget"] === target && compatibleTarget(n) && [n.description, ...n.attrs["statementAliases"] ?? []].some((s) => identityId({ kind: "DesignProblem", statement: s }) === statementKey));
|
|
20926
|
+
const id = bound?.id ?? restated?.id ?? identityId({ kind: "WitnessedVulnerability", workspaceId: opts.workspaceId, target: identityTarget, statement });
|
|
20927
|
+
const existing = bound ?? store.getNode(id);
|
|
20928
|
+
const sources = existing?.attrs["sources"] ?? [];
|
|
20929
|
+
const corroborated = !!existing && !sources.includes(opts.source);
|
|
20930
|
+
const upgraded = !!existing && existing.attrs["assessment"] !== "asserted" && flag.assessment === "asserted";
|
|
20931
|
+
const assessment = existing?.attrs["assessment"] === "asserted" ? "asserted" : flag.assessment;
|
|
20932
|
+
if (!existing) {
|
|
20933
|
+
store.mergeNode(buildNode(store, id, "Vulnerability", statement, opts.ts, {
|
|
20934
|
+
source: "convo",
|
|
20935
|
+
sourceClass: "agent-observation",
|
|
20936
|
+
privateFinding: id,
|
|
20937
|
+
workspaceId: opts.workspaceId,
|
|
20938
|
+
affectedTarget: target,
|
|
20939
|
+
identityTarget,
|
|
20940
|
+
assessment,
|
|
20941
|
+
anchorDisposition: target ? "unresolved" : "absent",
|
|
20942
|
+
provisional: true,
|
|
20943
|
+
sources: [opts.source],
|
|
20944
|
+
corroborations: 0,
|
|
20945
|
+
...opts.hostHarness ? { hostHarness: opts.hostHarness } : {}
|
|
20946
|
+
}));
|
|
20947
|
+
} else if (corroborated || upgraded || !existing.attrs["affectedTarget"] && target || symbol2 && existing.attrs["identityTarget"] !== identityTarget) {
|
|
20948
|
+
store.updateNode(id, {
|
|
20949
|
+
attrs: {
|
|
20950
|
+
...existing.attrs,
|
|
20951
|
+
assessment,
|
|
20952
|
+
...upgraded && existing.description !== statement ? { statementAliases: [...existing.attrs["statementAliases"] ?? [], existing.description].slice(-5) } : {},
|
|
20953
|
+
affectedTarget: existing.attrs["affectedTarget"] || target,
|
|
20954
|
+
...symbol2 ? { identityTarget } : {},
|
|
20955
|
+
sources: corroborated ? [...sources, opts.source] : sources,
|
|
20956
|
+
corroborations: (Number(existing.attrs["corroborations"]) || 0) + (corroborated ? 1 : 0)
|
|
20957
|
+
},
|
|
20958
|
+
// Assessment strengthens on evidence, but does not certify exploitability.
|
|
20959
|
+
...upgraded ? { description: statement } : {},
|
|
20960
|
+
...corroborated ? { cumulativeHits: existing.cumulativeHits + 1 } : {},
|
|
20961
|
+
...reinforcedSeqAttrs(store, "Vulnerability"),
|
|
20962
|
+
lastUpdatedAt: opts.ts
|
|
20963
|
+
});
|
|
20964
|
+
}
|
|
20965
|
+
return { id, created: !existing, corroborated, upgraded };
|
|
20966
|
+
}
|
|
20967
|
+
function recordVulnerabilityCause(store, findingId, causes, ts) {
|
|
20968
|
+
const finding = store.getNode(findingId);
|
|
20969
|
+
if (finding?.label !== "Vulnerability") return 0;
|
|
20970
|
+
let from = findingId;
|
|
20971
|
+
let count = 0;
|
|
20972
|
+
for (const description of causes) {
|
|
20973
|
+
if (isPlaceholderStatement(description)) continue;
|
|
20974
|
+
const id = genericCanonicalId("dcause", { findingId, description: description.trim() });
|
|
20975
|
+
if (!store.getNode(id)) store.mergeNode(buildNode(store, id, "RootCause", description.trim(), ts, {
|
|
20976
|
+
source: "convo",
|
|
20977
|
+
privateFinding: findingId,
|
|
20978
|
+
sources: finding.attrs["sources"],
|
|
20979
|
+
assessment: finding.attrs["assessment"]
|
|
20980
|
+
}));
|
|
20981
|
+
mergeEdge(store, from, id, "CAUSED_BY", ts);
|
|
20982
|
+
from = id;
|
|
20983
|
+
count++;
|
|
20984
|
+
}
|
|
20985
|
+
return count;
|
|
20986
|
+
}
|
|
20904
20987
|
function canonicalizePatternText(name2) {
|
|
20905
20988
|
const stripped = name2.replace(/^\s*pattern(?:\s*[×x]\s*\d+)?\s*(?:\(exemplar\))?\s*[:—–-]\s*/i, "").replace(/\s+/g, " ").trim();
|
|
20906
20989
|
return stripped.length > 0 ? stripped : name2.replace(/\s+/g, " ").trim();
|
|
@@ -21211,6 +21294,7 @@ function findReusableSolution(store, fixText) {
|
|
|
21211
21294
|
const key = solutionTextKey(fixText);
|
|
21212
21295
|
if (!key) return null;
|
|
21213
21296
|
for (const s of store.findNodesByLabel("Solution")) {
|
|
21297
|
+
if (s.attrs["privateFinding"]) continue;
|
|
21214
21298
|
if (s.validTo != null) continue;
|
|
21215
21299
|
if (isAutoMinted(s)) continue;
|
|
21216
21300
|
if (solutionTextKey(s.description ?? "") === key) return s;
|
|
@@ -21235,7 +21319,7 @@ function priorsForFile(store, relPath) {
|
|
|
21235
21319
|
for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
21236
21320
|
const n = store.getNode(e.from);
|
|
21237
21321
|
if (!n) continue;
|
|
21238
|
-
if (n.label === "Problem") {
|
|
21322
|
+
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
21239
21323
|
if (seenProblem.has(n.id)) continue;
|
|
21240
21324
|
seenProblem.add(n.id);
|
|
21241
21325
|
if (!n.attrs["resolvedAt"]) {
|
|
@@ -21305,7 +21389,8 @@ function solutionForProblem(store, problemId) {
|
|
|
21305
21389
|
}
|
|
21306
21390
|
function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts, provenance = "edited", opts = {}) {
|
|
21307
21391
|
const p = store.getNode(problemId);
|
|
21308
|
-
if (!p || p.label !== "Problem") return false;
|
|
21392
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
21393
|
+
if (p.label === "Vulnerability" && provenance !== "witnessed") return false;
|
|
21309
21394
|
if (!isAnchorableCodePath(relPath)) return false;
|
|
21310
21395
|
const file2 = resolveFileNode(store, relPath, workspaceId2);
|
|
21311
21396
|
if (!file2) return false;
|
|
@@ -21484,18 +21569,22 @@ function resolveDesignProblemById(store, id, fixNote, t) {
|
|
|
21484
21569
|
}
|
|
21485
21570
|
function closeDesignProblem(store, problemId, fixNote, t) {
|
|
21486
21571
|
const p = store.getNode(problemId);
|
|
21487
|
-
if (!p || p.label !== "Problem") return false;
|
|
21572
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
21488
21573
|
if (p.attrs["resolvedAs"]) return false;
|
|
21489
21574
|
if (fixNote?.trim() && !isFragmentFixNote(fixNote)) {
|
|
21490
21575
|
const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
|
|
21491
21576
|
if (sols.every((s) => isAutoMinted(s))) {
|
|
21492
|
-
const reuse = findReusableSolution(store, fixNote);
|
|
21577
|
+
const reuse = p.label === "Vulnerability" ? null : findReusableSolution(store, fixNote);
|
|
21493
21578
|
if (reuse) {
|
|
21494
21579
|
mergeEdge(store, problemId, reuse.id, "SOLVED_BY", t);
|
|
21495
21580
|
} else {
|
|
21496
21581
|
const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
|
|
21497
21582
|
store.mergeNode(
|
|
21498
|
-
buildNode(store, solId, "Solution", fixNote.trim(), t, {
|
|
21583
|
+
buildNode(store, solId, "Solution", fixNote.trim(), t, {
|
|
21584
|
+
source: "convo",
|
|
21585
|
+
provisional: false,
|
|
21586
|
+
...p.label === "Vulnerability" ? { privateFinding: problemId, sources: p.attrs["sources"] } : {}
|
|
21587
|
+
})
|
|
21499
21588
|
);
|
|
21500
21589
|
mergeEdge(store, problemId, solId, "SOLVED_BY", t);
|
|
21501
21590
|
}
|
|
@@ -21509,6 +21598,7 @@ function closeDesignProblem(store, problemId, fixNote, t) {
|
|
|
21509
21598
|
// mints no Solution, and a legacy placeholder beside it reads as
|
|
21510
21599
|
// all-heuristic either way.
|
|
21511
21600
|
attrs: { ...p.attrs, provisional: false, resolvedAt: t, resolutionWitnessed: true },
|
|
21601
|
+
...reinforcedSeqAttrs(store, p.label),
|
|
21512
21602
|
lastUpdatedAt: t
|
|
21513
21603
|
});
|
|
21514
21604
|
return true;
|
|
@@ -23860,7 +23950,7 @@ function detectLocalCommunities(store, opts = {}) {
|
|
|
23860
23950
|
const ids = /* @__PURE__ */ new Set();
|
|
23861
23951
|
for (const label of JOINT_COMMUNITY_LABELS) {
|
|
23862
23952
|
for (const n of store.findNodesByLabel(label)) {
|
|
23863
|
-
if (
|
|
23953
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
|
|
23864
23954
|
ids.add(n.id);
|
|
23865
23955
|
}
|
|
23866
23956
|
}
|
|
@@ -23997,6 +24087,7 @@ var init_community2 = __esm({
|
|
|
23997
24087
|
init_src4();
|
|
23998
24088
|
JOINT_COMMUNITY_LABELS = [
|
|
23999
24089
|
"Problem",
|
|
24090
|
+
"Vulnerability",
|
|
24000
24091
|
"Solution",
|
|
24001
24092
|
"RootCause",
|
|
24002
24093
|
"Pattern"
|
|
@@ -24858,6 +24949,7 @@ __export(src_exports2, {
|
|
|
24858
24949
|
induceAbstractions: () => induceAbstractions,
|
|
24859
24950
|
induceTriage: () => induceTriage,
|
|
24860
24951
|
ingestDesignProblem: () => ingestDesignProblem,
|
|
24952
|
+
ingestWitnessedVulnerability: () => ingestWitnessedVulnerability,
|
|
24861
24953
|
isConstraintProblem: () => isConstraintProblem,
|
|
24862
24954
|
isFragmentFixNote: () => isFragmentFixNote,
|
|
24863
24955
|
isPlaceholderStatement: () => isPlaceholderStatement,
|
|
@@ -24905,6 +24997,7 @@ __export(src_exports2, {
|
|
|
24905
24997
|
recordMisreadPrior: () => recordMisreadPrior,
|
|
24906
24998
|
recordRenderLedger: () => recordRenderLedger,
|
|
24907
24999
|
recordTriageObservation: () => recordTriageObservation,
|
|
25000
|
+
recordVulnerabilityCause: () => recordVulnerabilityCause,
|
|
24908
25001
|
reinforceSameAnchorProblem: () => reinforceSameAnchorProblem,
|
|
24909
25002
|
relateClaims: () => relateClaims,
|
|
24910
25003
|
reopenAutoClosedProblems: () => reopenAutoClosedProblems,
|
|
@@ -24974,7 +25067,7 @@ function isPassiveInjectable(node2, atCommit) {
|
|
|
24974
25067
|
return false;
|
|
24975
25068
|
}
|
|
24976
25069
|
function buildSnapshot(opts) {
|
|
24977
|
-
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
|
|
25070
|
+
const problems = [...opts.store.findNodesByLabel("Problem"), ...opts.store.findNodesByLabel("Vulnerability")].filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
|
|
24978
25071
|
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
24979
25072
|
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
24980
25073
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
@@ -25129,11 +25222,11 @@ function sliceForFile(store, relPath, atCommit) {
|
|
|
25129
25222
|
const priors = fp ? {
|
|
25130
25223
|
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
25131
25224
|
id: p.id,
|
|
25132
|
-
description: p.description,
|
|
25225
|
+
description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description,
|
|
25133
25226
|
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
25134
25227
|
})),
|
|
25135
25228
|
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
25136
|
-
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
25229
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description })),
|
|
25137
25230
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
25138
25231
|
solutionsByProblem: Object.fromEntries(
|
|
25139
25232
|
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
@@ -25251,7 +25344,8 @@ function renderSnapshot(s) {
|
|
|
25251
25344
|
if (s.recentProblems.length > 0) {
|
|
25252
25345
|
lines.push("_Open \u2014 recently observed here, derived from your work (a recency slice, not an inventory \u2014 burst an id for its full neighborhood):_");
|
|
25253
25346
|
for (const r of s.recentProblems) {
|
|
25254
|
-
|
|
25347
|
+
const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
|
|
25348
|
+
lines.push(`- **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
|
|
25255
25349
|
const hits = r.node.cumulativeHits ?? 0;
|
|
25256
25350
|
const surprise = (r.node.cumulativeSurprise ?? 0).toFixed(2);
|
|
25257
25351
|
lines.push(` - observed ${hits}\xD7 \xB7 cumulativeSurprise ${surprise}`);
|
|
@@ -25292,7 +25386,8 @@ function renderSnapshot(s) {
|
|
|
25292
25386
|
if (s.recentResolved.length > 0) {
|
|
25293
25387
|
lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
|
|
25294
25388
|
for (const r of s.recentResolved) {
|
|
25295
|
-
|
|
25389
|
+
const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
|
|
25390
|
+
lines.push(`- \u2713 **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
|
|
25296
25391
|
if (r.solution) {
|
|
25297
25392
|
lines.push(` - solved: ${r.solution.description.slice(0, 160)} \u2014 \`${r.solution.id}\`${tagOf(r.solution)}`);
|
|
25298
25393
|
}
|
|
@@ -27152,8 +27247,8 @@ var init_config = __esm({
|
|
|
27152
27247
|
"use strict";
|
|
27153
27248
|
init_paths();
|
|
27154
27249
|
LEGACY_CLOUD_URL = "https://inerrata-gateway.onrender.com";
|
|
27155
|
-
DEFAULT_CLOUD_URL = true ? "https://inerrata.dev" : DEV_CLOUD_URL;
|
|
27156
|
-
DEFAULT_UPDATE_CHANNEL =
|
|
27250
|
+
DEFAULT_CLOUD_URL = true ? "https://dev.inerrata.dev" : DEV_CLOUD_URL;
|
|
27251
|
+
DEFAULT_UPDATE_CHANNEL = false ? "latest" : "dev";
|
|
27157
27252
|
}
|
|
27158
27253
|
});
|
|
27159
27254
|
|
|
@@ -31713,7 +31808,7 @@ var init_src8 = __esm({
|
|
|
31713
31808
|
|
|
31714
31809
|
// src/agent-signals.ts
|
|
31715
31810
|
function buildFileRecallInstruction(referent = "\u2026") {
|
|
31716
|
-
return `Touching one? paste its token: ${TAG_EXAMPLE.fix(referent)} resolved \xB7 ${TAG_EXAMPLE.prior(referent)} leaned-on \xB7 ${TAG_EXAMPLE.problem()} new. We harvest it.`;
|
|
31811
|
+
return `Touching one? paste its token: ${TAG_EXAMPLE.fix(referent)} resolved \xB7 ${TAG_EXAMPLE.prior(referent)} leaned-on \xB7 ${TAG_EXAMPLE.problem()} new \xB7 ${TAG_EXAMPLE.vulnerability()} security. We harvest it.`;
|
|
31717
31812
|
}
|
|
31718
31813
|
function triageBullet(ref) {
|
|
31719
31814
|
return [
|
|
@@ -31749,15 +31844,15 @@ function attemptBullet() {
|
|
|
31749
31844
|
].join("\n");
|
|
31750
31845
|
}
|
|
31751
31846
|
function buildAgentInstruction(opts = {}) {
|
|
31752
|
-
const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
|
|
31847
|
+
const signals = opts.signals ?? ["prior", "problem", "vulnerability", "domain", "fix", "constraint", "triage", "attempt", "link"];
|
|
31753
31848
|
const ref = opts.referent ?? "its-handle";
|
|
31754
31849
|
const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : s === "verdict" ? TAG_EXAMPLE.confirm(ref) : TAG_EXAMPLE[s](ref);
|
|
31755
|
-
const head2 =
|
|
31756
|
-
const tail = "
|
|
31850
|
+
const head2 = "Tag inline (no fences or backticks). Untagged findings are silently lost. Cite short handles; tag everything, we filter downstream. Requested errata-* answer blocks are the exception: keep their fences in your turn-final reply:";
|
|
31851
|
+
const tail = "Restate mid-turn tags in your final reply; the harness can drop mid-turn text. EXCEPT one already shown with an id: cite [its-handle] instead of retyping (drift mints a duplicate).";
|
|
31757
31852
|
return [
|
|
31758
31853
|
head2,
|
|
31759
31854
|
...signals.map(
|
|
31760
|
-
(s) => s === "triage" ? triageBullet(ref) : s === "attempt" ? attemptBullet() : s === "link" ? linkBullet(ref) : s === "verdict" ? verdictBullet(ref) : ` \u2022 ${GLOSS[s]} \u2192 ${token(s)}`
|
|
31855
|
+
(s) => s === "triage" ? triageBullet(ref) : s === "vulnerability" ? ` \u2022 ${GLOSS.vulnerability} \u2192 ${TAG_EXAMPLE.vulnerability()}. Uncertain? ${TAG_EXAMPLE.suspectedVulnerability()}. Code review counts, even if immediately fixed. No CVE/exploit needed. For fixes: ${TAG_EXAMPLE.vulnerabilityThread()} ${TAG_EXAMPLE.fixThread("v")}.` : s === "attempt" ? attemptBullet() : s === "link" ? linkBullet(ref) : s === "verdict" ? verdictBullet(ref) : ` \u2022 ${GLOSS[s]} \u2192 ${token(s)}`
|
|
31761
31856
|
),
|
|
31762
31857
|
tail
|
|
31763
31858
|
].join("\n");
|
|
@@ -31769,6 +31864,9 @@ var init_agent_signals = __esm({
|
|
|
31769
31864
|
TAG_EXAMPLE = {
|
|
31770
31865
|
prior: (ref) => `([${ref}])`,
|
|
31771
31866
|
problem: () => `[!one line on what's wrong]`,
|
|
31867
|
+
vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
|
|
31868
|
+
suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
|
|
31869
|
+
vulnerabilityThread: () => `[!#v vuln: finding here]`,
|
|
31772
31870
|
fix: (ref) => `(fix:[${ref}])`,
|
|
31773
31871
|
// CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
|
|
31774
31872
|
// conflict that shaped a decision). Free text, attaches to the decision sentence the
|
|
@@ -31789,7 +31887,7 @@ var init_agent_signals = __esm({
|
|
|
31789
31887
|
// tags never need one — the id is for diagnoses that unfold over distance.
|
|
31790
31888
|
problemThread: () => `[!#slug one line on what's wrong]`,
|
|
31791
31889
|
causeThread: () => `(cause:#slug the mechanism)`,
|
|
31792
|
-
fixThread: () => `(fix
|
|
31890
|
+
fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
|
|
31793
31891
|
// ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
|
|
31794
31892
|
// witnessed NEGATIVE result: it saves the next agent the same dead end.
|
|
31795
31893
|
tried: () => `(tried: the approach you're attempting)`,
|
|
@@ -31831,8 +31929,9 @@ var init_agent_signals = __esm({
|
|
|
31831
31929
|
// problems, and narrated them as progress (a pick-best-before-scoring bug lost).
|
|
31832
31930
|
// Every example here was diagnosis-framed, so nothing contradicted that sorting.
|
|
31833
31931
|
// Kept to 29 chars: the instruction sits at the 2600 passive-budget ceiling.
|
|
31834
|
-
problem: "
|
|
31835
|
-
|
|
31932
|
+
problem: "any bug or finding, even in code you just wrote; name affected code: [!what's wrong @ path:42]",
|
|
31933
|
+
vulnerability: "unauthorized actions, data exposure, or execution/disruption an attacker can trigger",
|
|
31934
|
+
fix: "you fixed a shown finding; for your own non-security finding: (fix: what changed) \u2014 a fix resolves a stated problem, completing routine work is not a fix",
|
|
31836
31935
|
constraint: "a design decision forced by conflicting requirements or constraints \u2014 tag the tension, especially when your fix makes it 'only look' resolved",
|
|
31837
31936
|
triage: "you diagnosed a cause \u2014 fold the chain into the flag with <- (caused-by), deepest sure mechanism rightmost",
|
|
31838
31937
|
attempt: "attempting an approach / an attempt failed \u2014 a failed attempt is knowledge, it rules out a path",
|
|
@@ -31854,7 +31953,7 @@ var init_agent_signals = __esm({
|
|
|
31854
31953
|
// Sim note (2026-08-02 shrink): domain emission held 4/4 with just the trigger
|
|
31855
31954
|
// clause; the pattern convergence hook now rides here too (it fell 0/4 when it
|
|
31856
31955
|
// only appeared in the link bullet's tail).
|
|
31857
|
-
domain: "
|
|
31956
|
+
domain: "no single file? name the area (Title Case), and (pattern: the shape). No code anchor needed; two agents naming the same shape converge on one node",
|
|
31858
31957
|
link: "the problem you flagged is an instance of priors/abstractions you can name \u2014 (instance:[h1],[h2],\u2026) / (pattern: \u2026) / (aids:[h]); rendered whole via linkBullet",
|
|
31859
31958
|
verdict: "you can judge a prior we SHOWED you \u2014 confirm it if it held, refute it (with why) if it didn't; rendered whole via verdictBullet"
|
|
31860
31959
|
};
|
|
@@ -32095,7 +32194,7 @@ function readContextCard(root, hash2) {
|
|
|
32095
32194
|
function openProblems(store, now = Date.now()) {
|
|
32096
32195
|
const hit = openProblemsCache.get(store);
|
|
32097
32196
|
if (hit && now - hit.at < OPEN_PROBLEMS_TTL_MS) return hit.nodes;
|
|
32098
|
-
const nodes = store.findNodesByLabel("Problem").filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
|
|
32197
|
+
const nodes = [...store.findNodesByLabel("Problem"), ...store.findNodesByLabel("Vulnerability")].filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
|
|
32099
32198
|
openProblemsCache.set(store, { at: now, nodes });
|
|
32100
32199
|
return nodes;
|
|
32101
32200
|
}
|
|
@@ -43482,47 +43581,9 @@ var init_mcp = __esm({
|
|
|
43482
43581
|
init_reconcile();
|
|
43483
43582
|
init_agent_signals();
|
|
43484
43583
|
INSTRUCTIONS = [
|
|
43485
|
-
|
|
43486
|
-
|
|
43487
|
-
|
|
43488
|
-
// from your WORK deterministically, and the one thing it asks of the agent — a
|
|
43489
|
-
// one-line root cause at a fix — rides INLINE on the next tool response (the ⚡
|
|
43490
|
-
// errata line; see the dispatch's enrichment piggyback) instead of a fenced
|
|
43491
|
-
// protocol the agent has to remember. So this stays short: tool-routing steering
|
|
43492
|
-
// plus a single line about the inline nudge. Per-tool routing lives in each tool's
|
|
43493
|
-
// own `description` (delivered via tools/list). Don't re-add a tool table here.
|
|
43494
|
-
"errata is a LIVE, bi-temporal code graph of this workspace, kept current by a",
|
|
43495
|
-
"file watcher \u2014 a SEPARATE DIAGNOSTIC AXIS to run ALONGSIDE grep/glob/read, not",
|
|
43496
|
-
"instead of them. For STRUCTURAL questions (callers of callers, blast-radius,",
|
|
43497
|
-
"rename lineage) prefer it: faster, more complete, and it reaches relationships",
|
|
43498
|
-
"grep can't. Each tool's description says when to reach for it. Fall back to",
|
|
43499
|
-
'grep/read when a tool returns found:false (ESPECIALLY reason:"unresolved" \u2014 the',
|
|
43500
|
-
"name was not recognized, NOT an empty answer; an empty list like callers:[] is a",
|
|
43501
|
-
'confident "none" \u2014 trust it), when you need EXACT bytes before a high-stakes',
|
|
43502
|
-
"edit, or for an unindexed file/language. Always confirm exact bytes with a read",
|
|
43503
|
-
"before editing.",
|
|
43504
|
-
"",
|
|
43505
|
-
"errata is a cloud knowledge substrate \u2014 AND ALSO this workspace's code graph.",
|
|
43506
|
-
"The problems you hit and the fixes you make become shared knowledge other agents",
|
|
43507
|
-
"recall. That capture is automatic: derived from your edits/commands/commits \u2014 no",
|
|
43508
|
-
"emit needed. Just after you resolve something a tool result may carry a one-line",
|
|
43509
|
-
"\u26A1 errata note asking the ROOT CAUSE; answer via errata.annotate_resolution when",
|
|
43510
|
-
"you know it, skip it otherwise.",
|
|
43511
|
-
"",
|
|
43512
|
-
"Two things it CAN'T derive \u2014 only you witness them, and this is a CAPTURE",
|
|
43513
|
-
"PROTOCOL, not optional formatting: a problem or fix you state only in prose, a",
|
|
43514
|
-
"list, or your own numbering is SILENTLY LOST \u2014 the tag is the ONLY thing recorded.",
|
|
43515
|
-
"So the moment you state one, wrap it inline (no fence, no extra call):",
|
|
43516
|
-
// Signal tokens come from the single source (agent-signals.ts) so this always-on
|
|
43517
|
-
// block can't teach a grammar the parser rejects. Referent is "its-id" here — the
|
|
43518
|
-
// raw MCP channel has no primed handles, so the agent cites a tool-result id.
|
|
43519
|
-
` \u2022 ${TAG_EXAMPLE.problem()} \u2014 EVERY problem you notice or report ([?\u2026] = TODO)`,
|
|
43520
|
-
` \u2022 ${TAG_EXAMPLE.fix("its-id")} \u2014 your edit fixed a problem errata showed you`,
|
|
43521
|
-
"Tag every one you state \u2014 we filter downstream, so don't self-censor or batch.",
|
|
43522
|
-
"",
|
|
43523
|
-
"Graph-as-files: the graph is ALSO a virtual FS \u2014 Read (or `cat`)",
|
|
43524
|
-
".errata/g/<verb>/<seed> to run a query as a file read (e.g. .errata/g/burst/",
|
|
43525
|
-
"<symbol>, /why/<sym>, /trace/<a>..<b>). Read .errata/g/index for the vocabulary."
|
|
43584
|
+
"errata is a live code and knowledge graph, a SEPARATE DIAGNOSTIC AXIS alongside grep/read for callers, blast radius, rename lineage and prior fixes. Fall back to read/search on found:false (unresolved is not an empty answer), or for unindexed code. Confirm exact bytes before editing.",
|
|
43585
|
+
buildAgentInstruction({ signals: ["prior", "problem", "vulnerability", "fix"], referent: "its-id" }),
|
|
43586
|
+
"Read .errata/g/index for graph-as-files query paths. A tool result may ask for a root cause; answer via errata.annotate_resolution when known."
|
|
43526
43587
|
].join("\n");
|
|
43527
43588
|
AUDIT_RUN_THRESHOLD = 5;
|
|
43528
43589
|
AUDIT_NUDGE_COOLDOWN = 12;
|
|
@@ -44114,7 +44175,7 @@ var init_mcp = __esm({
|
|
|
44114
44175
|
},
|
|
44115
44176
|
{
|
|
44116
44177
|
name: "errata.problems",
|
|
44117
|
-
description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
|
|
44178
|
+
description: "List the workspace's Problem and Vulnerability backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
|
|
44118
44179
|
inputSchema: {
|
|
44119
44180
|
type: "object",
|
|
44120
44181
|
properties: {
|
|
@@ -44126,13 +44187,15 @@ var init_mcp = __esm({
|
|
|
44126
44187
|
const want = String(args2["status"] ?? "open");
|
|
44127
44188
|
const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 50)));
|
|
44128
44189
|
const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
|
|
44129
|
-
const rows = store.findAllVersionsByLabel("Problem").map((p) => {
|
|
44190
|
+
const rows = [...store.findAllVersionsByLabel("Problem"), ...store.findAllVersionsByLabel("Vulnerability")].map((p) => {
|
|
44130
44191
|
const ra = p.attrs["resolvedAs"];
|
|
44131
44192
|
const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
|
|
44132
44193
|
const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
|
|
44133
44194
|
const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
|
|
44134
44195
|
return {
|
|
44135
44196
|
id: p.id,
|
|
44197
|
+
label: p.label,
|
|
44198
|
+
...p.label === "Vulnerability" ? { assessment: p.attrs["assessment"] } : {},
|
|
44136
44199
|
problem: p.description,
|
|
44137
44200
|
status,
|
|
44138
44201
|
createdAt: p.createdAt,
|
|
@@ -52697,7 +52760,8 @@ function errorRecallTerms(errorText) {
|
|
|
52697
52760
|
}
|
|
52698
52761
|
function recallLine(n, token) {
|
|
52699
52762
|
const desc = (n.description ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
52700
|
-
|
|
52763
|
+
const label = n.label === "Vulnerability" ? `Vulnerability; ${n.attrs["assessment"] ?? "unassessed"}` : n.label;
|
|
52764
|
+
return `- [${label}] ${desc} \u2192 ${token}`;
|
|
52701
52765
|
}
|
|
52702
52766
|
function recallForFile(store, relPath) {
|
|
52703
52767
|
return fileRecall(store, relPath)?.context ?? null;
|
|
@@ -52718,7 +52782,7 @@ function fileRecall(store, relPath) {
|
|
|
52718
52782
|
const shownRelated = priors.related.slice(0, 4);
|
|
52719
52783
|
for (const p of shownProblems) {
|
|
52720
52784
|
const hasCause = store.outEdges(p.id, ["CAUSED_BY"]).some((e) => store.getNode(e.to)?.label === "RootCause");
|
|
52721
|
-
const causeCue = hasCause ? "" : ` \xB7 ${TAG_EXAMPLE.triageMint()} to diagnose its cause`;
|
|
52785
|
+
const causeCue = hasCause ? "" : ` \xB7 ${p.label === "Vulnerability" ? `(cause:#${p.id} the mechanism)` : TAG_EXAMPLE.triageMint()} to diagnose its cause`;
|
|
52722
52786
|
lines.push(
|
|
52723
52787
|
recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
|
|
52724
52788
|
);
|
|
@@ -75988,7 +76052,7 @@ init_src5();
|
|
|
75988
76052
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
75989
76053
|
var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
|
|
75990
76054
|
var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
|
|
75991
|
-
var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
|
|
76055
|
+
var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})/gi;
|
|
75992
76056
|
var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
75993
76057
|
"handle",
|
|
75994
76058
|
"its-handle",
|
|
@@ -76003,7 +76067,7 @@ var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
|
76003
76067
|
"b"
|
|
76004
76068
|
]);
|
|
76005
76069
|
var isPlaceholderHandle = (h) => PLACEHOLDER_HANDLES.has(h.toLowerCase());
|
|
76006
|
-
var FLAG_BODY_HANDLE_RE = /^(?:[a-z0-9]+(?:-[a-z0-9]+){2,}-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})$/i;
|
|
76070
|
+
var FLAG_BODY_HANDLE_RE = /^(?:[a-z0-9]+(?:-[a-z0-9]+){2,}-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})$/i;
|
|
76007
76071
|
var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
76008
76072
|
var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
|
|
76009
76073
|
var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
@@ -76028,7 +76092,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
|
|
|
76028
76092
|
var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
76029
76093
|
var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
76030
76094
|
var CAUSE_TEXT_MIN = 8;
|
|
76031
|
-
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n])
|
|
76095
|
+
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n])*?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
|
|
76032
76096
|
var CONSTRAINT_MIN = 8;
|
|
76033
76097
|
function clauseBefore(sentence, idx) {
|
|
76034
76098
|
const win = sentence.slice(Math.max(0, idx - 80), idx);
|
|
@@ -76172,6 +76236,7 @@ function cleanEmittedTags(tags) {
|
|
|
76172
76236
|
}
|
|
76173
76237
|
function emptyTagDispositions() {
|
|
76174
76238
|
return {
|
|
76239
|
+
invalidVulnerability: 0,
|
|
76175
76240
|
oversizeSegments: 0,
|
|
76176
76241
|
codeSpanNeutralized: 0,
|
|
76177
76242
|
refuteTooShort: 0,
|
|
@@ -76184,6 +76249,7 @@ function emptyTagDispositions() {
|
|
|
76184
76249
|
};
|
|
76185
76250
|
}
|
|
76186
76251
|
function addTagDispositions(a, b) {
|
|
76252
|
+
a.invalidVulnerability += b.invalidVulnerability;
|
|
76187
76253
|
a.oversizeSegments += b.oversizeSegments;
|
|
76188
76254
|
a.codeSpanNeutralized += b.codeSpanNeutralized;
|
|
76189
76255
|
a.refuteTooShort += b.refuteTooShort;
|
|
@@ -76197,12 +76263,31 @@ function addTagDispositions(a, b) {
|
|
|
76197
76263
|
function parseInlineTags(text) {
|
|
76198
76264
|
return parseInlineTagsWithDispositions(text).tags;
|
|
76199
76265
|
}
|
|
76266
|
+
function* captureSentences(text) {
|
|
76267
|
+
for (const line of text.split(/\n+/)) {
|
|
76268
|
+
if (line.length > 2e4) {
|
|
76269
|
+
yield line;
|
|
76270
|
+
continue;
|
|
76271
|
+
}
|
|
76272
|
+
const flags2 = [...line.matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => ({ start: m.index, end: m.index + m[0].length }));
|
|
76273
|
+
let flag = 0;
|
|
76274
|
+
let start2 = 0;
|
|
76275
|
+
for (const boundary of line.matchAll(/(?<=[.!?])\s+(?!\(fix:)/g)) {
|
|
76276
|
+
const index = boundary.index;
|
|
76277
|
+
while (flags2[flag] && flags2[flag].end <= index) flag++;
|
|
76278
|
+
if (flags2[flag] && flags2[flag].start < index) continue;
|
|
76279
|
+
yield line.slice(start2, index);
|
|
76280
|
+
start2 = index + boundary[0].length;
|
|
76281
|
+
}
|
|
76282
|
+
yield line.slice(start2);
|
|
76283
|
+
}
|
|
76284
|
+
}
|
|
76200
76285
|
function parseInlineTagsWithDispositions(text) {
|
|
76201
76286
|
const out2 = [];
|
|
76202
76287
|
const dispositions = emptyTagDispositions();
|
|
76203
76288
|
const deFenced = text.replace(/```[\s\S]*?```/g, " ");
|
|
76204
76289
|
let seqNo = -1;
|
|
76205
|
-
for (const raw2 of deFenced
|
|
76290
|
+
for (const raw2 of captureSentences(deFenced)) {
|
|
76206
76291
|
seqNo++;
|
|
76207
76292
|
if (raw2.length > 2e4) {
|
|
76208
76293
|
dispositions.oversizeSegments++;
|
|
@@ -76428,6 +76513,29 @@ function parseInlineTagsWithDispositions(text) {
|
|
|
76428
76513
|
while ((f = FLAG_RE.exec(sentence)) !== null) {
|
|
76429
76514
|
const flagThread = f[2];
|
|
76430
76515
|
const raw3 = f[3].trim();
|
|
76516
|
+
if (/^vuln\b/i.test(raw3)) {
|
|
76517
|
+
const typed = /^vuln:\s*([\s\S]*)$/i.exec(raw3);
|
|
76518
|
+
const body2 = typed?.[1]?.trim() ?? "";
|
|
76519
|
+
const parts2 = body2.split(CAUSE_ARROW).map((s) => s.trim());
|
|
76520
|
+
const symptom = f[1] === "!" && parts2.length > 1 && !f[4] ? new RegExp(FLAG_RE.source, "i").exec(`[!${parts2[0]}]`) : null;
|
|
76521
|
+
const statement = f[1] === "?" ? body2 : symptom?.[3]?.trim() ?? parts2[0];
|
|
76522
|
+
if (statement.length < 8 || /^<[^>]*>$/.test(statement)) {
|
|
76523
|
+
dispositions.invalidVulnerability++;
|
|
76524
|
+
continue;
|
|
76525
|
+
}
|
|
76526
|
+
const path2 = (f[4] ?? symptom?.[4])?.trim();
|
|
76527
|
+
const line = f[5] ?? symptom?.[5];
|
|
76528
|
+
out2.push({
|
|
76529
|
+
kind: "vulnerability",
|
|
76530
|
+
statement,
|
|
76531
|
+
assessment: f[1] === "!" ? "asserted" : "suspected",
|
|
76532
|
+
...f[1] === "!" && parts2.length > 1 ? { vulnerabilityCauses: parts2.slice(1).filter((s) => s.length >= CAUSE_TEXT_MIN) } : {},
|
|
76533
|
+
...path2 ? { location: { path: path2, ...line ? { line: Number(line) } : {} } } : {},
|
|
76534
|
+
...flagThread ? { threadId: flagThread } : {},
|
|
76535
|
+
sentence: sfield
|
|
76536
|
+
});
|
|
76537
|
+
continue;
|
|
76538
|
+
}
|
|
76431
76539
|
if (FLAG_BODY_HANDLE_RE.test(raw3) && !f[4]) {
|
|
76432
76540
|
dispositions.flagAsHandle++;
|
|
76433
76541
|
out2.push({ kind: "prior", handle: raw3, sentence: sfield });
|
|
@@ -76798,7 +76906,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76798
76906
|
const parsed = parseInlineTagsWithDispositions(text);
|
|
76799
76907
|
const tags = parsed.tags;
|
|
76800
76908
|
plan.dispositions = parsed.dispositions;
|
|
76801
|
-
const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
|
|
76909
|
+
const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo" || t.kind === "vulnerability") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId, kind: t.kind })).sort((a, b) => a.seq - b.seq);
|
|
76802
76910
|
const bindSymptom = (seq, threadId) => {
|
|
76803
76911
|
if (threadId) {
|
|
76804
76912
|
const hit = symptomSeqs.find((s) => s.threadId === threadId);
|
|
@@ -76811,6 +76919,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76811
76919
|
if (seq == null) return { evidence: "inferred" };
|
|
76812
76920
|
let best;
|
|
76813
76921
|
for (const s of symptomSeqs) {
|
|
76922
|
+
if (s.kind === "vulnerability") continue;
|
|
76814
76923
|
if (s.seq <= seq) best = s;
|
|
76815
76924
|
else break;
|
|
76816
76925
|
}
|
|
@@ -76825,10 +76934,13 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76825
76934
|
return id;
|
|
76826
76935
|
};
|
|
76827
76936
|
for (const tag of tags) {
|
|
76828
|
-
if (tag.kind === "problem" || tag.kind === "todo") {
|
|
76937
|
+
if (tag.kind === "problem" || tag.kind === "todo" || tag.kind === "vulnerability") {
|
|
76829
76938
|
plan.problems.push({
|
|
76830
76939
|
statement: tag.statement,
|
|
76831
76940
|
kind: tag.kind,
|
|
76941
|
+
...tag.assessment ? { assessment: tag.assessment } : {},
|
|
76942
|
+
...tag.vulnerabilityCauses ? { vulnerabilityCauses: tag.vulnerabilityCauses } : {},
|
|
76943
|
+
...tag.kind === "vulnerability" && tag.seq !== void 0 ? { seq: tag.seq } : {},
|
|
76832
76944
|
...tag.location ? { location: tag.location } : {},
|
|
76833
76945
|
...tag.threadId ? { threadId: tag.threadId } : {}
|
|
76834
76946
|
});
|
|
@@ -76851,7 +76963,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76851
76963
|
} else if (tag.threadId && !b.statement) {
|
|
76852
76964
|
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, threadId: tag.threadId });
|
|
76853
76965
|
} else if (b.statement && b.evidence === "witnessed") {
|
|
76854
|
-
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement });
|
|
76966
|
+
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement, ...tag.threadId ? { threadId: tag.threadId } : {} });
|
|
76855
76967
|
} else {
|
|
76856
76968
|
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, ambiguous: true });
|
|
76857
76969
|
}
|
|
@@ -77054,6 +77166,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
77054
77166
|
}
|
|
77055
77167
|
var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
|
|
77056
77168
|
"Problem",
|
|
77169
|
+
"Vulnerability",
|
|
77057
77170
|
"Solution",
|
|
77058
77171
|
"RootCause",
|
|
77059
77172
|
"Pattern",
|
|
@@ -80832,9 +80945,9 @@ function partitionReplayable(queue, channel, isShipped) {
|
|
|
80832
80945
|
function retireWitnesses(queue, channel, settledKeys) {
|
|
80833
80946
|
return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
|
|
80834
80947
|
}
|
|
80835
|
-
var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
|
|
80948
|
+
var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Vulnerability", "Solution", "RootCause"]);
|
|
80836
80949
|
var MOTIF_ID_PREFIX = "motif_community_";
|
|
80837
|
-
var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,}$/i;
|
|
80950
|
+
var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,}$/i;
|
|
80838
80951
|
function classifyWitnessTarget(nodeId, getNode) {
|
|
80839
80952
|
if (nodeId.startsWith(MOTIF_ID_PREFIX)) {
|
|
80840
80953
|
const exemplar = nodeId.slice(MOTIF_ID_PREFIX.length);
|
|
@@ -81404,7 +81517,7 @@ function createWatchBreaker(deps) {
|
|
|
81404
81517
|
}
|
|
81405
81518
|
|
|
81406
81519
|
// src/engine.ts
|
|
81407
|
-
var DAEMON_VERSION = true ? "2.0.
|
|
81520
|
+
var DAEMON_VERSION = true ? "2.0.17-dev.3369" : "2.0.0-alpha.0";
|
|
81408
81521
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
81409
81522
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
81410
81523
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -82296,6 +82409,9 @@ function createWorkspaceEngine(opts) {
|
|
|
82296
82409
|
const harvestTextsInner = async (sessionId, items) => {
|
|
82297
82410
|
let minted = 0;
|
|
82298
82411
|
let tagsSeen = 0;
|
|
82412
|
+
let vulnerabilitiesSeen = 0;
|
|
82413
|
+
let vulnerabilitiesMinted = 0;
|
|
82414
|
+
let vulnerabilitiesSuspected = 0;
|
|
82299
82415
|
let tagsRejected = 0;
|
|
82300
82416
|
let tagsRestated = 0;
|
|
82301
82417
|
let resolved = 0;
|
|
@@ -82526,10 +82642,73 @@ function createWorkspaceEngine(opts) {
|
|
|
82526
82642
|
if ((toolRuns.get(sessionId)?.size ?? 0) > 0) touchedToolTurns++;
|
|
82527
82643
|
const wf = workingFiles.get(sessionId);
|
|
82528
82644
|
let inScopeProblemId;
|
|
82645
|
+
const findingIds = /* @__PURE__ */ new Map();
|
|
82646
|
+
const conflictingThreads = /* @__PURE__ */ new Set();
|
|
82529
82647
|
const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
|
|
82530
82648
|
sessionThreads.set(sessionId, threads);
|
|
82531
82649
|
for (const p of plan.problems) {
|
|
82532
82650
|
tagsSeen++;
|
|
82651
|
+
if (p.kind === "vulnerability") {
|
|
82652
|
+
vulnerabilitiesSeen++;
|
|
82653
|
+
if (p.assessment === "suspected") vulnerabilitiesSuspected++;
|
|
82654
|
+
let fileText;
|
|
82655
|
+
let anchorReadFailed = false;
|
|
82656
|
+
if (p.location) {
|
|
82657
|
+
try {
|
|
82658
|
+
fileText = readFileSync31(join35(opts.workspaceRoot, p.location.path), "utf8");
|
|
82659
|
+
} catch {
|
|
82660
|
+
anchorReadFailed = true;
|
|
82661
|
+
}
|
|
82662
|
+
}
|
|
82663
|
+
const result = ingestWitnessedVulnerability(store, {
|
|
82664
|
+
statement: p.statement,
|
|
82665
|
+
assessment: p.assessment,
|
|
82666
|
+
target: p.location?.path,
|
|
82667
|
+
line: p.location?.line,
|
|
82668
|
+
fileText,
|
|
82669
|
+
existingId: p.threadId ? threads.get(p.threadId) ?? handleMap[p.threadId]?.id ?? store.getNode(p.threadId)?.id : void 0
|
|
82670
|
+
}, { workspaceId: profile.id, source: sessionId, ts: t, hostHarness: turn.hostHarness });
|
|
82671
|
+
let anchorDisposition = p.location ? "unresolved" : "absent";
|
|
82672
|
+
if (result.rejected) {
|
|
82673
|
+
tagsRejected++;
|
|
82674
|
+
if (p.threadId) conflictingThreads.add(p.threadId);
|
|
82675
|
+
} else {
|
|
82676
|
+
findingIds.set(p.statement, result.id);
|
|
82677
|
+
if (p.threadId) threads.set(p.threadId, result.id);
|
|
82678
|
+
if (result.created || result.corroborated || result.upgraded) minted++;
|
|
82679
|
+
else tagsRestated++;
|
|
82680
|
+
if (result.created) vulnerabilitiesMinted++;
|
|
82681
|
+
if (p.location) {
|
|
82682
|
+
try {
|
|
82683
|
+
const anchored = anchorProblemToWorkingFile(store, result.id, p.location.path, profile.id, t, "witnessed", { line: p.location.line, fileText });
|
|
82684
|
+
anchorDisposition = anchored ? "resolved" : anchorReadFailed ? "error" : "unresolved";
|
|
82685
|
+
if (anchored) deriveContextFromAnchors(store, result.id, t);
|
|
82686
|
+
} catch {
|
|
82687
|
+
anchorDisposition = "error";
|
|
82688
|
+
}
|
|
82689
|
+
}
|
|
82690
|
+
const node2 = store.getNode(result.id);
|
|
82691
|
+
store.updateNode(result.id, { attrs: { ...node2.attrs, anchorDisposition } });
|
|
82692
|
+
if (p.vulnerabilityCauses) triaged += recordVulnerabilityCause(store, result.id, p.vulnerabilityCauses, t);
|
|
82693
|
+
}
|
|
82694
|
+
log.append({
|
|
82695
|
+
kind: "vulnerability.witness",
|
|
82696
|
+
workspaceId: profile.id,
|
|
82697
|
+
ts: t,
|
|
82698
|
+
digest: digest({ kind: "vulnerability.witness", workspaceId: profile.id, sessionId, turnId: turn.uuid, statement: p.statement, assessment: p.assessment, location: p.location }),
|
|
82699
|
+
sessionId,
|
|
82700
|
+
turnId: turn.uuid,
|
|
82701
|
+
harness: turn.hostHarness,
|
|
82702
|
+
findingId: result.id,
|
|
82703
|
+
statement: p.statement,
|
|
82704
|
+
assessment: p.assessment,
|
|
82705
|
+
location: p.location,
|
|
82706
|
+
seq: p.seq,
|
|
82707
|
+
anchorDisposition,
|
|
82708
|
+
disposition: result.rejected ?? (result.created ? "created" : result.upgraded ? "upgraded" : result.corroborated ? "corroborated" : "restated")
|
|
82709
|
+
});
|
|
82710
|
+
continue;
|
|
82711
|
+
}
|
|
82533
82712
|
const anchorPath = p.location?.path ?? editedTurnFile;
|
|
82534
82713
|
const anchorProvenance = p.location?.path ? "witnessed" : "edited";
|
|
82535
82714
|
const hintPath = anchorPath ? void 0 : turnFile ?? wf;
|
|
@@ -82624,10 +82803,10 @@ function createWorkspaceEngine(opts) {
|
|
|
82624
82803
|
if (r.created || r.corroborated) {
|
|
82625
82804
|
minted++;
|
|
82626
82805
|
if (p.kind !== "constraint") {
|
|
82627
|
-
sessionLastProblem.set(sessionId,
|
|
82628
|
-
inScopeProblemId =
|
|
82806
|
+
sessionLastProblem.set(sessionId, r.problemId);
|
|
82807
|
+
inScopeProblemId = r.problemId;
|
|
82629
82808
|
}
|
|
82630
|
-
if (p.threadId) threads.set(p.threadId,
|
|
82809
|
+
if (p.threadId) threads.set(p.threadId, r.problemId);
|
|
82631
82810
|
try {
|
|
82632
82811
|
if (anchorPath) {
|
|
82633
82812
|
let enclosing = {};
|
|
@@ -82664,7 +82843,8 @@ function createWorkspaceEngine(opts) {
|
|
|
82664
82843
|
);
|
|
82665
82844
|
}
|
|
82666
82845
|
for (const fx of plan.fixes) {
|
|
82667
|
-
|
|
82846
|
+
if (fx.threadId && conflictingThreads.has(fx.threadId)) continue;
|
|
82847
|
+
const pid = fx.problemId ?? (fx.threadId ? threads.get(fx.threadId) : void 0) ?? (fx.boundStatement ? findingIds.get(fx.boundStatement) ?? designProblemId(fx.boundStatement) : void 0);
|
|
82668
82848
|
if (!pid) {
|
|
82669
82849
|
if (fx.inScope) {
|
|
82670
82850
|
console.warn(
|
|
@@ -82676,7 +82856,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82676
82856
|
const closed = resolveDesignProblemById(store, pid, fx.fixNote, t);
|
|
82677
82857
|
if (!closed) {
|
|
82678
82858
|
const n = store.getNode(pid);
|
|
82679
|
-
if (!n || n.label !== "Problem" || n.attrs["resolvedAs"] != null) {
|
|
82859
|
+
if (!n || n.label !== "Problem" && n.label !== "Vulnerability" || n.attrs["resolvedAs"] != null) {
|
|
82680
82860
|
console.warn(
|
|
82681
82861
|
`[errata] fix tag went nowhere: ${!n ? `id ${pid.slice(0, 32)} unknown` : n.label !== "Problem" ? `${pid.slice(0, 32)} is a ${n.label}, not a Problem` : "the problem was retracted \u2014 a retraction is not fixable"} \u2014 the fix was NOT recorded`
|
|
82682
82862
|
);
|
|
@@ -82695,10 +82875,23 @@ function createWorkspaceEngine(opts) {
|
|
|
82695
82875
|
}
|
|
82696
82876
|
}
|
|
82697
82877
|
}
|
|
82878
|
+
const vulnerabilityTriages = /* @__PURE__ */ new Set();
|
|
82879
|
+
for (const tr of plan.triages) {
|
|
82880
|
+
if (tr.threadId && conflictingThreads.has(tr.threadId)) {
|
|
82881
|
+
vulnerabilityTriages.add(tr);
|
|
82882
|
+
continue;
|
|
82883
|
+
}
|
|
82884
|
+
const id = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? (tr.symptomStatement ? findingIds.get(tr.symptomStatement) : void 0);
|
|
82885
|
+
if (id && store.getNode(id)?.label === "Vulnerability") {
|
|
82886
|
+
vulnerabilityTriages.add(tr);
|
|
82887
|
+
if (tr.evidence === "witnessed" && tr.causeDescription) triaged += recordVulnerabilityCause(store, id, [tr.causeDescription], t);
|
|
82888
|
+
}
|
|
82889
|
+
}
|
|
82698
82890
|
if (opts.sharedStore && plan.triages.length > 0) {
|
|
82699
82891
|
const fallbackId = sessionLastProblem.get(sessionId);
|
|
82700
82892
|
const fallbackStatement = fallbackId ? store.getNode(fallbackId)?.description : void 0;
|
|
82701
82893
|
for (const tr of plan.triages) {
|
|
82894
|
+
if (vulnerabilityTriages.has(tr)) continue;
|
|
82702
82895
|
let presentingStatement = tr.symptomStatement;
|
|
82703
82896
|
let evidence = tr.evidence;
|
|
82704
82897
|
if (!presentingStatement && tr.threadId) {
|
|
@@ -82716,7 +82909,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82716
82909
|
evidence = "inferred";
|
|
82717
82910
|
}
|
|
82718
82911
|
if (!presentingStatement) continue;
|
|
82719
|
-
const presentingId = tr.symptomProblemId ?? designProblemId(presentingStatement);
|
|
82912
|
+
const presentingId = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? designProblemId(presentingStatement);
|
|
82720
82913
|
if (tr.causeId === presentingId) continue;
|
|
82721
82914
|
try {
|
|
82722
82915
|
recordTriageObservation(
|
|
@@ -82774,7 +82967,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82774
82967
|
}
|
|
82775
82968
|
}
|
|
82776
82969
|
for (const at of plan.attempts) {
|
|
82777
|
-
const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? designProblemId(at.boundStatement) : void 0);
|
|
82970
|
+
const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? findingIds.get(at.boundStatement) ?? designProblemId(at.boundStatement) : void 0);
|
|
82778
82971
|
const node2 = pid ? store.getNode(pid) : void 0;
|
|
82779
82972
|
if (!pid || !node2) continue;
|
|
82780
82973
|
try {
|
|
@@ -82790,7 +82983,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82790
82983
|
}
|
|
82791
82984
|
}
|
|
82792
82985
|
const bindPid = (tag) => {
|
|
82793
|
-
const pid = tag.problemId ?? (tag.threadId ? threads.get(tag.threadId) : void 0) ?? (tag.boundStatement ? designProblemId(tag.boundStatement) : void 0) ?? inScopeProblemId ?? sessionLastProblem.get(sessionId);
|
|
82986
|
+
const pid = tag.problemId ?? (tag.threadId ? threads.get(tag.threadId) : void 0) ?? (tag.boundStatement ? findingIds.get(tag.boundStatement) ?? designProblemId(tag.boundStatement) : void 0) ?? inScopeProblemId ?? sessionLastProblem.get(sessionId);
|
|
82794
82987
|
return pid && store.getNode(pid) ? pid : void 0;
|
|
82795
82988
|
};
|
|
82796
82989
|
const mintCiteEdge = (from, to, type, confidence, extraAttrs) => {
|
|
@@ -82944,7 +83137,12 @@ function createWorkspaceEngine(opts) {
|
|
|
82944
83137
|
for (const [session, items2] of groups) {
|
|
82945
83138
|
if (items2.length === 0) continue;
|
|
82946
83139
|
try {
|
|
82947
|
-
const
|
|
83140
|
+
const wireItems = items2.map(({ nodeId, witnessKey }) => {
|
|
83141
|
+
const node2 = store.getNode(nodeId);
|
|
83142
|
+
const cloudId = node2?.attrs["privateFinding"] ? node2.attrs["cloudNodeId"] : void 0;
|
|
83143
|
+
return { nodeId: typeof cloudId === "string" ? cloudId : nodeId, witnessKey };
|
|
83144
|
+
});
|
|
83145
|
+
const raw2 = await send(wireItems, session);
|
|
82948
83146
|
const d = {
|
|
82949
83147
|
recorded: raw2?.recorded ?? 0,
|
|
82950
83148
|
unmatched: raw2?.unmatched ?? 0,
|
|
@@ -82957,8 +83155,8 @@ function createWorkspaceEngine(opts) {
|
|
|
82957
83155
|
duplicate += d.duplicate;
|
|
82958
83156
|
selfGated += d.selfGated;
|
|
82959
83157
|
const missing = new Set(d.unmatchedIds);
|
|
82960
|
-
for (const it of items2) {
|
|
82961
|
-
if (missing.has(
|
|
83158
|
+
for (const [index, it] of items2.entries()) {
|
|
83159
|
+
if (missing.has(wireItems[index].nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: isShipped(it.nodeId) });
|
|
82962
83160
|
else settled.add(it.witnessKey);
|
|
82963
83161
|
}
|
|
82964
83162
|
} catch (err2) {
|
|
@@ -83128,6 +83326,10 @@ function createWorkspaceEngine(opts) {
|
|
|
83128
83326
|
// but nothing judgeable was cited.
|
|
83129
83327
|
tagsVerdictNoHandle: dispositions.verdictNoHandle,
|
|
83130
83328
|
tagsFlagAsHandle: dispositions.flagAsHandle,
|
|
83329
|
+
tagsInvalidVulnerability: dispositions.invalidVulnerability,
|
|
83330
|
+
vulnerabilitiesSeen,
|
|
83331
|
+
vulnerabilitiesMinted,
|
|
83332
|
+
vulnerabilitiesSuspected,
|
|
83131
83333
|
unresolvedHandles,
|
|
83132
83334
|
priorEdgesFlagOff,
|
|
83133
83335
|
priorEdgesNoSource,
|
|
@@ -84465,7 +84667,7 @@ function internalSymbolsFor(description, lexiconHas, publicIndex) {
|
|
|
84465
84667
|
|
|
84466
84668
|
// src/instance-ingest.ts
|
|
84467
84669
|
init_generalize_graph();
|
|
84468
|
-
var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
|
|
84670
|
+
var INSTANCE_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
84469
84671
|
var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
|
|
84470
84672
|
var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
|
|
84471
84673
|
var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
|
|
@@ -84600,6 +84802,10 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84600
84802
|
attrs: {
|
|
84601
84803
|
scope: stripCodebaseScope(n.attrs["scope"]),
|
|
84602
84804
|
...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
|
|
84805
|
+
...n.attrs["privateFinding"] ? { privateFinding: true } : {},
|
|
84806
|
+
...n.attrs["assessment"] ? { assessment: n.attrs["assessment"] } : {},
|
|
84807
|
+
...n.attrs["sourceClass"] ? { sourceClass: n.attrs["sourceClass"] } : {},
|
|
84808
|
+
...n.attrs["resolutionWitnessed"] ? { resolutionWitnessed: true, resolvedAt: n.attrs["resolvedAt"] } : {},
|
|
84603
84809
|
...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {},
|
|
84604
84810
|
...preserved.length > 0 ? { preservedSymbols: preserved } : {},
|
|
84605
84811
|
...internal.length > 0 ? { internalSymbols: internal } : {}
|
|
@@ -84614,6 +84820,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84614
84820
|
const semanticEndpoints = /* @__PURE__ */ new Map();
|
|
84615
84821
|
const includedByLabel = /* @__PURE__ */ new Map();
|
|
84616
84822
|
const composition = {
|
|
84823
|
+
privateTargetMissing: 0,
|
|
84617
84824
|
candidates: 0,
|
|
84618
84825
|
shipped: 0,
|
|
84619
84826
|
cloudSourced: 0,
|
|
@@ -84659,11 +84866,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84659
84866
|
if (walked.has(n.id)) continue;
|
|
84660
84867
|
walked.add(n.id);
|
|
84661
84868
|
composition.candidates++;
|
|
84869
|
+
if ((n.attrs["privateFinding"] || n.label === "Vulnerability") && !opts.project) {
|
|
84870
|
+
composition.privateTargetMissing++;
|
|
84871
|
+
continue;
|
|
84872
|
+
}
|
|
84662
84873
|
if (n.attrs["source"] === "cloud") {
|
|
84663
84874
|
composition.cloudSourced++;
|
|
84664
84875
|
continue;
|
|
84665
84876
|
}
|
|
84666
|
-
if (
|
|
84877
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
|
|
84667
84878
|
composition.falsePositive++;
|
|
84668
84879
|
continue;
|
|
84669
84880
|
}
|
|
@@ -84686,7 +84897,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84686
84897
|
composition.terminalRefused++;
|
|
84687
84898
|
continue;
|
|
84688
84899
|
}
|
|
84689
|
-
if (!noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
84900
|
+
if (!n.attrs["privateFinding"] && !noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
84690
84901
|
composition.notNovel++;
|
|
84691
84902
|
continue;
|
|
84692
84903
|
}
|
|
@@ -84729,7 +84940,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84729
84940
|
}
|
|
84730
84941
|
if (targets.length === 0) continue;
|
|
84731
84942
|
const shipped = shippedById.get(sourceId);
|
|
84732
|
-
if (shipped) {
|
|
84943
|
+
if (shipped && !shipped.attrs["privateFinding"]) {
|
|
84733
84944
|
const anchors = (label) => targets.filter((x) => x.t.label === label).map(
|
|
84734
84945
|
(x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
|
|
84735
84946
|
).filter((a) => a != null).sort();
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -15122,7 +15122,7 @@ var init_edge_rules = __esm({
|
|
|
15122
15122
|
// anchor anything at anything, e.g. at a public `Package`, quietly re-creating
|
|
15123
15123
|
// the code↔collective bridge the membrane exists to prevent.
|
|
15124
15124
|
ANCHORED_AT: {
|
|
15125
|
-
from: ["Problem", "Solution", "RootCause", "Claim"],
|
|
15125
|
+
from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim"],
|
|
15126
15126
|
to: ["Symbol"]
|
|
15127
15127
|
},
|
|
15128
15128
|
// ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
|
|
@@ -15167,7 +15167,7 @@ var init_edge_rules = __esm({
|
|
|
15167
15167
|
// `Component` joined the target set with OM-agent-anchors: an agent-named
|
|
15168
15168
|
// component ("React Router") is the same knowledge→named-unit anchor shape as
|
|
15169
15169
|
// a Tool — the knowledge is ABOUT it, not dependent on it.
|
|
15170
|
-
CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15170
|
+
CONCERNS: { from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15171
15171
|
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15172
15172
|
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15173
15173
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
@@ -24827,7 +24827,8 @@ CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
|
24827
24827
|
-- its table row. Partial, so it maintains a few thousand rows instead of adding
|
|
24828
24828
|
-- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
|
|
24829
24829
|
DROP INDEX IF EXISTS nodes_live_fingerprint;
|
|
24830
|
-
|
|
24830
|
+
DROP INDEX IF EXISTS nodes_instance_fingerprint;
|
|
24831
|
+
CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
|
|
24831
24832
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
24832
24833
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
24833
24834
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
@@ -24898,7 +24899,7 @@ function rowToEdge(r) {
|
|
|
24898
24899
|
attrs: JSON.parse(r.attrs_json)
|
|
24899
24900
|
};
|
|
24900
24901
|
}
|
|
24901
|
-
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
|
|
24902
|
+
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
24902
24903
|
var SqliteGraphStore = class {
|
|
24903
24904
|
db;
|
|
24904
24905
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
@@ -25576,8 +25577,8 @@ var SqliteGraphStore = class {
|
|
|
25576
25577
|
const r2 = this.db.prepare(
|
|
25577
25578
|
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
25578
25579
|
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
25579
|
-
FROM nodes INDEXED BY
|
|
25580
|
-
WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
|
|
25580
|
+
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
25581
|
+
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
25581
25582
|
).get();
|
|
25582
25583
|
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
25583
25584
|
}
|
|
@@ -26181,7 +26182,7 @@ function mergeContextEdge(store2, from, to, type, ts) {
|
|
|
26181
26182
|
function deriveContextFromAnchors(store2, problemId, ts, pkgIndex) {
|
|
26182
26183
|
const report = { languages: [], packages: [] };
|
|
26183
26184
|
const p = store2.getNode(problemId);
|
|
26184
|
-
if (!p || p.label !== "Problem") return report;
|
|
26185
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
26185
26186
|
const files = store2.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store2.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
26186
26187
|
if (files.length === 0) return report;
|
|
26187
26188
|
const idx = pkgIndex ?? buildPackageIndex(store2);
|
|
@@ -26386,7 +26387,7 @@ function priorsForFile(store2, relPath) {
|
|
|
26386
26387
|
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
26387
26388
|
const n = store2.getNode(e.from);
|
|
26388
26389
|
if (!n) continue;
|
|
26389
|
-
if (n.label === "Problem") {
|
|
26390
|
+
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
26390
26391
|
if (seenProblem.has(n.id)) continue;
|
|
26391
26392
|
seenProblem.add(n.id);
|
|
26392
26393
|
if (!n.attrs["resolvedAt"]) {
|
|
@@ -27654,7 +27655,7 @@ var AGENTS_POINTER_BODY = [
|
|
|
27654
27655
|
init_src2();
|
|
27655
27656
|
init_src();
|
|
27656
27657
|
function buildSnapshot(opts) {
|
|
27657
|
-
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
|
|
27658
|
+
const problems = [...opts.store.findNodesByLabel("Problem"), ...opts.store.findNodesByLabel("Vulnerability")].filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
|
|
27658
27659
|
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
27659
27660
|
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
27660
27661
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
@@ -27809,11 +27810,11 @@ function sliceForFile(store2, relPath, atCommit) {
|
|
|
27809
27810
|
const priors = fp ? {
|
|
27810
27811
|
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27811
27812
|
id: p.id,
|
|
27812
|
-
description: p.description,
|
|
27813
|
+
description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description,
|
|
27813
27814
|
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
27814
27815
|
})),
|
|
27815
27816
|
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
27816
|
-
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
27817
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description })),
|
|
27817
27818
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
27818
27819
|
solutionsByProblem: Object.fromEntries(
|
|
27819
27820
|
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|