@inerrata-corporation/errata 2.0.15 → 2.0.16-dev.3343
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`) ──
|
|
@@ -18268,7 +18275,8 @@ CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
|
18268
18275
|
-- its table row. Partial, so it maintains a few thousand rows instead of adding
|
|
18269
18276
|
-- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
|
|
18270
18277
|
DROP INDEX IF EXISTS nodes_live_fingerprint;
|
|
18271
|
-
|
|
18278
|
+
DROP INDEX IF EXISTS nodes_instance_fingerprint;
|
|
18279
|
+
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');
|
|
18272
18280
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
18273
18281
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
18274
18282
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
@@ -18282,7 +18290,7 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
|
|
|
18282
18290
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
18283
18291
|
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
|
|
18284
18292
|
`;
|
|
18285
|
-
FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
|
|
18293
|
+
FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
18286
18294
|
SqliteGraphStore = class {
|
|
18287
18295
|
db;
|
|
18288
18296
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
@@ -18960,8 +18968,8 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
18960
18968
|
const r2 = this.db.prepare(
|
|
18961
18969
|
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
18962
18970
|
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
18963
|
-
FROM nodes INDEXED BY
|
|
18964
|
-
WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
|
|
18971
|
+
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
18972
|
+
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
18965
18973
|
).get();
|
|
18966
18974
|
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
18967
18975
|
}
|
|
@@ -20257,7 +20265,7 @@ function mergeContextEdge(store, from, to, type, ts) {
|
|
|
20257
20265
|
function deriveContextFromAnchors(store, problemId, ts, pkgIndex) {
|
|
20258
20266
|
const report = { languages: [], packages: [] };
|
|
20259
20267
|
const p = store.getNode(problemId);
|
|
20260
|
-
if (!p || p.label !== "Problem") return report;
|
|
20268
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
20261
20269
|
const files = store.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
20262
20270
|
if (files.length === 0) return report;
|
|
20263
20271
|
const idx = pkgIndex ?? buildPackageIndex(store);
|
|
@@ -20895,6 +20903,81 @@ function ingestDesignProblem(store, flag, opts) {
|
|
|
20895
20903
|
...reopenedFromRegression ? { reopenedFromRegression } : {}
|
|
20896
20904
|
};
|
|
20897
20905
|
}
|
|
20906
|
+
function ingestWitnessedVulnerability(store, flag, opts) {
|
|
20907
|
+
const statement = flag.statement.trim();
|
|
20908
|
+
if (isPlaceholderStatement(statement)) return { id: "", created: false, corroborated: false, upgraded: false, rejected: "statement" };
|
|
20909
|
+
const target = flag.target?.trim().replace(/\\/g, "/").replace(/^\.\//, "") ?? "";
|
|
20910
|
+
const file2 = target ? resolveFileNode(store, target, opts.workspaceId) : null;
|
|
20911
|
+
const symbol2 = file2 && flag.line !== void 0 && flag.fileText !== void 0 ? resolveEnclosingSymbol(store, file2.id, flag.line, flag.fileText) : null;
|
|
20912
|
+
const identityTarget = symbol2 ? String(symbol2.attrs["logicalId"] ?? `${target}#${symbol2.attrs["qname"] ?? symbol2.description}`) : target;
|
|
20913
|
+
const compatibleTarget = (n) => !symbol2 || !n.attrs["identityTarget"] || n.attrs["identityTarget"] === target || n.attrs["identityTarget"] === identityTarget;
|
|
20914
|
+
const bound = flag.existingId ? store.getNode(flag.existingId) : null;
|
|
20915
|
+
if (flag.existingId && (!bound || bound.label !== "Vulnerability" || bound.attrs["workspaceId"] !== opts.workspaceId || target && bound.attrs["affectedTarget"] && bound.attrs["affectedTarget"] !== target || !compatibleTarget(bound))) {
|
|
20916
|
+
return { id: "", created: false, corroborated: false, upgraded: false, rejected: "binding-conflict" };
|
|
20917
|
+
}
|
|
20918
|
+
const statementKey = identityId({ kind: "DesignProblem", statement });
|
|
20919
|
+
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));
|
|
20920
|
+
const id = bound?.id ?? restated?.id ?? identityId({ kind: "WitnessedVulnerability", workspaceId: opts.workspaceId, target: identityTarget, statement });
|
|
20921
|
+
const existing = bound ?? store.getNode(id);
|
|
20922
|
+
const sources = existing?.attrs["sources"] ?? [];
|
|
20923
|
+
const corroborated = !!existing && !sources.includes(opts.source);
|
|
20924
|
+
const upgraded = !!existing && existing.attrs["assessment"] !== "asserted" && flag.assessment === "asserted";
|
|
20925
|
+
const assessment = existing?.attrs["assessment"] === "asserted" ? "asserted" : flag.assessment;
|
|
20926
|
+
if (!existing) {
|
|
20927
|
+
store.mergeNode(buildNode(store, id, "Vulnerability", statement, opts.ts, {
|
|
20928
|
+
source: "convo",
|
|
20929
|
+
sourceClass: "agent-observation",
|
|
20930
|
+
privateFinding: id,
|
|
20931
|
+
workspaceId: opts.workspaceId,
|
|
20932
|
+
affectedTarget: target,
|
|
20933
|
+
identityTarget,
|
|
20934
|
+
assessment,
|
|
20935
|
+
anchorDisposition: target ? "unresolved" : "absent",
|
|
20936
|
+
provisional: true,
|
|
20937
|
+
sources: [opts.source],
|
|
20938
|
+
corroborations: 0,
|
|
20939
|
+
...opts.hostHarness ? { hostHarness: opts.hostHarness } : {}
|
|
20940
|
+
}));
|
|
20941
|
+
} else if (corroborated || upgraded || !existing.attrs["affectedTarget"] && target || symbol2 && existing.attrs["identityTarget"] !== identityTarget) {
|
|
20942
|
+
store.updateNode(id, {
|
|
20943
|
+
attrs: {
|
|
20944
|
+
...existing.attrs,
|
|
20945
|
+
assessment,
|
|
20946
|
+
...upgraded && existing.description !== statement ? { statementAliases: [...existing.attrs["statementAliases"] ?? [], existing.description].slice(-5) } : {},
|
|
20947
|
+
affectedTarget: existing.attrs["affectedTarget"] || target,
|
|
20948
|
+
...symbol2 ? { identityTarget } : {},
|
|
20949
|
+
sources: corroborated ? [...sources, opts.source] : sources,
|
|
20950
|
+
corroborations: (Number(existing.attrs["corroborations"]) || 0) + (corroborated ? 1 : 0)
|
|
20951
|
+
},
|
|
20952
|
+
// Assessment strengthens on evidence, but does not certify exploitability.
|
|
20953
|
+
...upgraded ? { description: statement } : {},
|
|
20954
|
+
...corroborated ? { cumulativeHits: existing.cumulativeHits + 1 } : {},
|
|
20955
|
+
...reinforcedSeqAttrs(store, "Vulnerability"),
|
|
20956
|
+
lastUpdatedAt: opts.ts
|
|
20957
|
+
});
|
|
20958
|
+
}
|
|
20959
|
+
return { id, created: !existing, corroborated, upgraded };
|
|
20960
|
+
}
|
|
20961
|
+
function recordVulnerabilityCause(store, findingId, causes, ts) {
|
|
20962
|
+
const finding = store.getNode(findingId);
|
|
20963
|
+
if (finding?.label !== "Vulnerability") return 0;
|
|
20964
|
+
let from = findingId;
|
|
20965
|
+
let count = 0;
|
|
20966
|
+
for (const description of causes) {
|
|
20967
|
+
if (isPlaceholderStatement(description)) continue;
|
|
20968
|
+
const id = genericCanonicalId("dcause", { findingId, description: description.trim() });
|
|
20969
|
+
if (!store.getNode(id)) store.mergeNode(buildNode(store, id, "RootCause", description.trim(), ts, {
|
|
20970
|
+
source: "convo",
|
|
20971
|
+
privateFinding: findingId,
|
|
20972
|
+
sources: finding.attrs["sources"],
|
|
20973
|
+
assessment: finding.attrs["assessment"]
|
|
20974
|
+
}));
|
|
20975
|
+
mergeEdge(store, from, id, "CAUSED_BY", ts);
|
|
20976
|
+
from = id;
|
|
20977
|
+
count++;
|
|
20978
|
+
}
|
|
20979
|
+
return count;
|
|
20980
|
+
}
|
|
20898
20981
|
function canonicalizePatternText(name2) {
|
|
20899
20982
|
const stripped = name2.replace(/^\s*pattern(?:\s*[×x]\s*\d+)?\s*(?:\(exemplar\))?\s*[:—–-]\s*/i, "").replace(/\s+/g, " ").trim();
|
|
20900
20983
|
return stripped.length > 0 ? stripped : name2.replace(/\s+/g, " ").trim();
|
|
@@ -21205,6 +21288,7 @@ function findReusableSolution(store, fixText) {
|
|
|
21205
21288
|
const key = solutionTextKey(fixText);
|
|
21206
21289
|
if (!key) return null;
|
|
21207
21290
|
for (const s of store.findNodesByLabel("Solution")) {
|
|
21291
|
+
if (s.attrs["privateFinding"]) continue;
|
|
21208
21292
|
if (s.validTo != null) continue;
|
|
21209
21293
|
if (isAutoMinted(s)) continue;
|
|
21210
21294
|
if (solutionTextKey(s.description ?? "") === key) return s;
|
|
@@ -21229,7 +21313,7 @@ function priorsForFile(store, relPath) {
|
|
|
21229
21313
|
for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
21230
21314
|
const n = store.getNode(e.from);
|
|
21231
21315
|
if (!n) continue;
|
|
21232
|
-
if (n.label === "Problem") {
|
|
21316
|
+
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
21233
21317
|
if (seenProblem.has(n.id)) continue;
|
|
21234
21318
|
seenProblem.add(n.id);
|
|
21235
21319
|
if (!n.attrs["resolvedAt"]) {
|
|
@@ -21299,7 +21383,8 @@ function solutionForProblem(store, problemId) {
|
|
|
21299
21383
|
}
|
|
21300
21384
|
function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts, provenance = "edited", opts = {}) {
|
|
21301
21385
|
const p = store.getNode(problemId);
|
|
21302
|
-
if (!p || p.label !== "Problem") return false;
|
|
21386
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
21387
|
+
if (p.label === "Vulnerability" && provenance !== "witnessed") return false;
|
|
21303
21388
|
if (!isAnchorableCodePath(relPath)) return false;
|
|
21304
21389
|
const file2 = resolveFileNode(store, relPath, workspaceId2);
|
|
21305
21390
|
if (!file2) return false;
|
|
@@ -21478,18 +21563,22 @@ function resolveDesignProblemById(store, id, fixNote, t) {
|
|
|
21478
21563
|
}
|
|
21479
21564
|
function closeDesignProblem(store, problemId, fixNote, t) {
|
|
21480
21565
|
const p = store.getNode(problemId);
|
|
21481
|
-
if (!p || p.label !== "Problem") return false;
|
|
21566
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
21482
21567
|
if (p.attrs["resolvedAs"]) return false;
|
|
21483
21568
|
if (fixNote?.trim() && !isFragmentFixNote(fixNote)) {
|
|
21484
21569
|
const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
|
|
21485
21570
|
if (sols.every((s) => isAutoMinted(s))) {
|
|
21486
|
-
const reuse = findReusableSolution(store, fixNote);
|
|
21571
|
+
const reuse = p.label === "Vulnerability" ? null : findReusableSolution(store, fixNote);
|
|
21487
21572
|
if (reuse) {
|
|
21488
21573
|
mergeEdge(store, problemId, reuse.id, "SOLVED_BY", t);
|
|
21489
21574
|
} else {
|
|
21490
21575
|
const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
|
|
21491
21576
|
store.mergeNode(
|
|
21492
|
-
buildNode(store, solId, "Solution", fixNote.trim(), t, {
|
|
21577
|
+
buildNode(store, solId, "Solution", fixNote.trim(), t, {
|
|
21578
|
+
source: "convo",
|
|
21579
|
+
provisional: false,
|
|
21580
|
+
...p.label === "Vulnerability" ? { privateFinding: problemId, sources: p.attrs["sources"] } : {}
|
|
21581
|
+
})
|
|
21493
21582
|
);
|
|
21494
21583
|
mergeEdge(store, problemId, solId, "SOLVED_BY", t);
|
|
21495
21584
|
}
|
|
@@ -21503,6 +21592,7 @@ function closeDesignProblem(store, problemId, fixNote, t) {
|
|
|
21503
21592
|
// mints no Solution, and a legacy placeholder beside it reads as
|
|
21504
21593
|
// all-heuristic either way.
|
|
21505
21594
|
attrs: { ...p.attrs, provisional: false, resolvedAt: t, resolutionWitnessed: true },
|
|
21595
|
+
...reinforcedSeqAttrs(store, p.label),
|
|
21506
21596
|
lastUpdatedAt: t
|
|
21507
21597
|
});
|
|
21508
21598
|
return true;
|
|
@@ -23854,7 +23944,7 @@ function detectLocalCommunities(store, opts = {}) {
|
|
|
23854
23944
|
const ids = /* @__PURE__ */ new Set();
|
|
23855
23945
|
for (const label of JOINT_COMMUNITY_LABELS) {
|
|
23856
23946
|
for (const n of store.findNodesByLabel(label)) {
|
|
23857
|
-
if (
|
|
23947
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
|
|
23858
23948
|
ids.add(n.id);
|
|
23859
23949
|
}
|
|
23860
23950
|
}
|
|
@@ -23991,6 +24081,7 @@ var init_community2 = __esm({
|
|
|
23991
24081
|
init_src4();
|
|
23992
24082
|
JOINT_COMMUNITY_LABELS = [
|
|
23993
24083
|
"Problem",
|
|
24084
|
+
"Vulnerability",
|
|
23994
24085
|
"Solution",
|
|
23995
24086
|
"RootCause",
|
|
23996
24087
|
"Pattern"
|
|
@@ -24852,6 +24943,7 @@ __export(src_exports2, {
|
|
|
24852
24943
|
induceAbstractions: () => induceAbstractions,
|
|
24853
24944
|
induceTriage: () => induceTriage,
|
|
24854
24945
|
ingestDesignProblem: () => ingestDesignProblem,
|
|
24946
|
+
ingestWitnessedVulnerability: () => ingestWitnessedVulnerability,
|
|
24855
24947
|
isConstraintProblem: () => isConstraintProblem,
|
|
24856
24948
|
isFragmentFixNote: () => isFragmentFixNote,
|
|
24857
24949
|
isPlaceholderStatement: () => isPlaceholderStatement,
|
|
@@ -24899,6 +24991,7 @@ __export(src_exports2, {
|
|
|
24899
24991
|
recordMisreadPrior: () => recordMisreadPrior,
|
|
24900
24992
|
recordRenderLedger: () => recordRenderLedger,
|
|
24901
24993
|
recordTriageObservation: () => recordTriageObservation,
|
|
24994
|
+
recordVulnerabilityCause: () => recordVulnerabilityCause,
|
|
24902
24995
|
reinforceSameAnchorProblem: () => reinforceSameAnchorProblem,
|
|
24903
24996
|
relateClaims: () => relateClaims,
|
|
24904
24997
|
reopenAutoClosedProblems: () => reopenAutoClosedProblems,
|
|
@@ -24968,7 +25061,7 @@ function isPassiveInjectable(node2, atCommit) {
|
|
|
24968
25061
|
return false;
|
|
24969
25062
|
}
|
|
24970
25063
|
function buildSnapshot(opts) {
|
|
24971
|
-
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
|
|
25064
|
+
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);
|
|
24972
25065
|
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
24973
25066
|
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
24974
25067
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
@@ -25123,11 +25216,11 @@ function sliceForFile(store, relPath, atCommit) {
|
|
|
25123
25216
|
const priors = fp ? {
|
|
25124
25217
|
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
25125
25218
|
id: p.id,
|
|
25126
|
-
description: p.description,
|
|
25219
|
+
description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description,
|
|
25127
25220
|
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
25128
25221
|
})),
|
|
25129
25222
|
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
25130
|
-
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
25223
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description })),
|
|
25131
25224
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
25132
25225
|
solutionsByProblem: Object.fromEntries(
|
|
25133
25226
|
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
@@ -25245,7 +25338,8 @@ function renderSnapshot(s) {
|
|
|
25245
25338
|
if (s.recentProblems.length > 0) {
|
|
25246
25339
|
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):_");
|
|
25247
25340
|
for (const r of s.recentProblems) {
|
|
25248
|
-
|
|
25341
|
+
const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
|
|
25342
|
+
lines.push(`- **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
|
|
25249
25343
|
const hits = r.node.cumulativeHits ?? 0;
|
|
25250
25344
|
const surprise = (r.node.cumulativeSurprise ?? 0).toFixed(2);
|
|
25251
25345
|
lines.push(` - observed ${hits}\xD7 \xB7 cumulativeSurprise ${surprise}`);
|
|
@@ -25286,7 +25380,8 @@ function renderSnapshot(s) {
|
|
|
25286
25380
|
if (s.recentResolved.length > 0) {
|
|
25287
25381
|
lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
|
|
25288
25382
|
for (const r of s.recentResolved) {
|
|
25289
|
-
|
|
25383
|
+
const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
|
|
25384
|
+
lines.push(`- \u2713 **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
|
|
25290
25385
|
if (r.solution) {
|
|
25291
25386
|
lines.push(` - solved: ${r.solution.description.slice(0, 160)} \u2014 \`${r.solution.id}\`${tagOf(r.solution)}`);
|
|
25292
25387
|
}
|
|
@@ -27146,8 +27241,8 @@ var init_config = __esm({
|
|
|
27146
27241
|
"use strict";
|
|
27147
27242
|
init_paths();
|
|
27148
27243
|
LEGACY_CLOUD_URL = "https://inerrata-gateway.onrender.com";
|
|
27149
|
-
DEFAULT_CLOUD_URL = true ? "https://inerrata.dev" : DEV_CLOUD_URL;
|
|
27150
|
-
DEFAULT_UPDATE_CHANNEL =
|
|
27244
|
+
DEFAULT_CLOUD_URL = true ? "https://dev.inerrata.dev" : DEV_CLOUD_URL;
|
|
27245
|
+
DEFAULT_UPDATE_CHANNEL = false ? "latest" : "dev";
|
|
27151
27246
|
}
|
|
27152
27247
|
});
|
|
27153
27248
|
|
|
@@ -31707,7 +31802,7 @@ var init_src8 = __esm({
|
|
|
31707
31802
|
|
|
31708
31803
|
// src/agent-signals.ts
|
|
31709
31804
|
function buildFileRecallInstruction(referent = "\u2026") {
|
|
31710
|
-
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.`;
|
|
31805
|
+
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.`;
|
|
31711
31806
|
}
|
|
31712
31807
|
function triageBullet(ref) {
|
|
31713
31808
|
return [
|
|
@@ -31743,15 +31838,15 @@ function attemptBullet() {
|
|
|
31743
31838
|
].join("\n");
|
|
31744
31839
|
}
|
|
31745
31840
|
function buildAgentInstruction(opts = {}) {
|
|
31746
|
-
const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
|
|
31841
|
+
const signals = opts.signals ?? ["prior", "problem", "vulnerability", "domain", "fix", "constraint", "triage", "attempt", "link"];
|
|
31747
31842
|
const ref = opts.referent ?? "its-handle";
|
|
31748
31843
|
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);
|
|
31749
|
-
const head2 =
|
|
31750
|
-
const tail = "
|
|
31844
|
+
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:";
|
|
31845
|
+
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).";
|
|
31751
31846
|
return [
|
|
31752
31847
|
head2,
|
|
31753
31848
|
...signals.map(
|
|
31754
|
-
(s) => s === "triage" ? triageBullet(ref) : s === "attempt" ? attemptBullet() : s === "link" ? linkBullet(ref) : s === "verdict" ? verdictBullet(ref) : ` \u2022 ${GLOSS[s]} \u2192 ${token(s)}`
|
|
31849
|
+
(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)}`
|
|
31755
31850
|
),
|
|
31756
31851
|
tail
|
|
31757
31852
|
].join("\n");
|
|
@@ -31763,6 +31858,9 @@ var init_agent_signals = __esm({
|
|
|
31763
31858
|
TAG_EXAMPLE = {
|
|
31764
31859
|
prior: (ref) => `([${ref}])`,
|
|
31765
31860
|
problem: () => `[!one line on what's wrong]`,
|
|
31861
|
+
vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
|
|
31862
|
+
suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
|
|
31863
|
+
vulnerabilityThread: () => `[!#v vuln: finding here]`,
|
|
31766
31864
|
fix: (ref) => `(fix:[${ref}])`,
|
|
31767
31865
|
// CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
|
|
31768
31866
|
// conflict that shaped a decision). Free text, attaches to the decision sentence the
|
|
@@ -31783,7 +31881,7 @@ var init_agent_signals = __esm({
|
|
|
31783
31881
|
// tags never need one — the id is for diagnoses that unfold over distance.
|
|
31784
31882
|
problemThread: () => `[!#slug one line on what's wrong]`,
|
|
31785
31883
|
causeThread: () => `(cause:#slug the mechanism)`,
|
|
31786
|
-
fixThread: () => `(fix
|
|
31884
|
+
fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
|
|
31787
31885
|
// ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
|
|
31788
31886
|
// witnessed NEGATIVE result: it saves the next agent the same dead end.
|
|
31789
31887
|
tried: () => `(tried: the approach you're attempting)`,
|
|
@@ -31825,8 +31923,9 @@ var init_agent_signals = __esm({
|
|
|
31825
31923
|
// problems, and narrated them as progress (a pick-best-before-scoring bug lost).
|
|
31826
31924
|
// Every example here was diagnosis-framed, so nothing contradicted that sorting.
|
|
31827
31925
|
// Kept to 29 chars: the instruction sits at the 2600 passive-budget ceiling.
|
|
31828
|
-
problem: "
|
|
31829
|
-
|
|
31926
|
+
problem: "any bug or finding, even in code you just wrote; name affected code: [!what's wrong @ path:42]",
|
|
31927
|
+
vulnerability: "unauthorized actions, data exposure, or execution/disruption an attacker can trigger",
|
|
31928
|
+
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",
|
|
31830
31929
|
constraint: "a design decision forced by conflicting requirements or constraints \u2014 tag the tension, especially when your fix makes it 'only look' resolved",
|
|
31831
31930
|
triage: "you diagnosed a cause \u2014 fold the chain into the flag with <- (caused-by), deepest sure mechanism rightmost",
|
|
31832
31931
|
attempt: "attempting an approach / an attempt failed \u2014 a failed attempt is knowledge, it rules out a path",
|
|
@@ -31848,7 +31947,7 @@ var init_agent_signals = __esm({
|
|
|
31848
31947
|
// Sim note (2026-08-02 shrink): domain emission held 4/4 with just the trigger
|
|
31849
31948
|
// clause; the pattern convergence hook now rides here too (it fell 0/4 when it
|
|
31850
31949
|
// only appeared in the link bullet's tail).
|
|
31851
|
-
domain: "
|
|
31950
|
+
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",
|
|
31852
31951
|
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",
|
|
31853
31952
|
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"
|
|
31854
31953
|
};
|
|
@@ -32085,7 +32184,7 @@ function readContextCard(root, hash2) {
|
|
|
32085
32184
|
function openProblems(store, now = Date.now()) {
|
|
32086
32185
|
const hit = openProblemsCache.get(store);
|
|
32087
32186
|
if (hit && now - hit.at < OPEN_PROBLEMS_TTL_MS) return hit.nodes;
|
|
32088
|
-
const nodes = store.findNodesByLabel("Problem").filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
|
|
32187
|
+
const nodes = [...store.findNodesByLabel("Problem"), ...store.findNodesByLabel("Vulnerability")].filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
|
|
32089
32188
|
openProblemsCache.set(store, { at: now, nodes });
|
|
32090
32189
|
return nodes;
|
|
32091
32190
|
}
|
|
@@ -43329,47 +43428,9 @@ var init_mcp = __esm({
|
|
|
43329
43428
|
init_reconcile();
|
|
43330
43429
|
init_agent_signals();
|
|
43331
43430
|
INSTRUCTIONS = [
|
|
43332
|
-
|
|
43333
|
-
|
|
43334
|
-
|
|
43335
|
-
// from your WORK deterministically, and the one thing it asks of the agent — a
|
|
43336
|
-
// one-line root cause at a fix — rides INLINE on the next tool response (the ⚡
|
|
43337
|
-
// errata line; see the dispatch's enrichment piggyback) instead of a fenced
|
|
43338
|
-
// protocol the agent has to remember. So this stays short: tool-routing steering
|
|
43339
|
-
// plus a single line about the inline nudge. Per-tool routing lives in each tool's
|
|
43340
|
-
// own `description` (delivered via tools/list). Don't re-add a tool table here.
|
|
43341
|
-
"errata is a LIVE, bi-temporal code graph of this workspace, kept current by a",
|
|
43342
|
-
"file watcher \u2014 a SEPARATE DIAGNOSTIC AXIS to run ALONGSIDE grep/glob/read, not",
|
|
43343
|
-
"instead of them. For STRUCTURAL questions (callers of callers, blast-radius,",
|
|
43344
|
-
"rename lineage) prefer it: faster, more complete, and it reaches relationships",
|
|
43345
|
-
"grep can't. Each tool's description says when to reach for it. Fall back to",
|
|
43346
|
-
'grep/read when a tool returns found:false (ESPECIALLY reason:"unresolved" \u2014 the',
|
|
43347
|
-
"name was not recognized, NOT an empty answer; an empty list like callers:[] is a",
|
|
43348
|
-
'confident "none" \u2014 trust it), when you need EXACT bytes before a high-stakes',
|
|
43349
|
-
"edit, or for an unindexed file/language. Always confirm exact bytes with a read",
|
|
43350
|
-
"before editing.",
|
|
43351
|
-
"",
|
|
43352
|
-
"errata is a cloud knowledge substrate \u2014 AND ALSO this workspace's code graph.",
|
|
43353
|
-
"The problems you hit and the fixes you make become shared knowledge other agents",
|
|
43354
|
-
"recall. That capture is automatic: derived from your edits/commands/commits \u2014 no",
|
|
43355
|
-
"emit needed. Just after you resolve something a tool result may carry a one-line",
|
|
43356
|
-
"\u26A1 errata note asking the ROOT CAUSE; answer via errata.annotate_resolution when",
|
|
43357
|
-
"you know it, skip it otherwise.",
|
|
43358
|
-
"",
|
|
43359
|
-
"Two things it CAN'T derive \u2014 only you witness them, and this is a CAPTURE",
|
|
43360
|
-
"PROTOCOL, not optional formatting: a problem or fix you state only in prose, a",
|
|
43361
|
-
"list, or your own numbering is SILENTLY LOST \u2014 the tag is the ONLY thing recorded.",
|
|
43362
|
-
"So the moment you state one, wrap it inline (no fence, no extra call):",
|
|
43363
|
-
// Signal tokens come from the single source (agent-signals.ts) so this always-on
|
|
43364
|
-
// block can't teach a grammar the parser rejects. Referent is "its-id" here — the
|
|
43365
|
-
// raw MCP channel has no primed handles, so the agent cites a tool-result id.
|
|
43366
|
-
` \u2022 ${TAG_EXAMPLE.problem()} \u2014 EVERY problem you notice or report ([?\u2026] = TODO)`,
|
|
43367
|
-
` \u2022 ${TAG_EXAMPLE.fix("its-id")} \u2014 your edit fixed a problem errata showed you`,
|
|
43368
|
-
"Tag every one you state \u2014 we filter downstream, so don't self-censor or batch.",
|
|
43369
|
-
"",
|
|
43370
|
-
"Graph-as-files: the graph is ALSO a virtual FS \u2014 Read (or `cat`)",
|
|
43371
|
-
".errata/g/<verb>/<seed> to run a query as a file read (e.g. .errata/g/burst/",
|
|
43372
|
-
"<symbol>, /why/<sym>, /trace/<a>..<b>). Read .errata/g/index for the vocabulary."
|
|
43431
|
+
"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.",
|
|
43432
|
+
buildAgentInstruction({ signals: ["prior", "problem", "vulnerability", "fix"], referent: "its-id" }),
|
|
43433
|
+
"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."
|
|
43373
43434
|
].join("\n");
|
|
43374
43435
|
AUDIT_RUN_THRESHOLD = 5;
|
|
43375
43436
|
AUDIT_NUDGE_COOLDOWN = 12;
|
|
@@ -43961,7 +44022,7 @@ var init_mcp = __esm({
|
|
|
43961
44022
|
},
|
|
43962
44023
|
{
|
|
43963
44024
|
name: "errata.problems",
|
|
43964
|
-
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.",
|
|
44025
|
+
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.",
|
|
43965
44026
|
inputSchema: {
|
|
43966
44027
|
type: "object",
|
|
43967
44028
|
properties: {
|
|
@@ -43973,13 +44034,15 @@ var init_mcp = __esm({
|
|
|
43973
44034
|
const want = String(args2["status"] ?? "open");
|
|
43974
44035
|
const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 50)));
|
|
43975
44036
|
const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
|
|
43976
|
-
const rows = store.findAllVersionsByLabel("Problem").map((p) => {
|
|
44037
|
+
const rows = [...store.findAllVersionsByLabel("Problem"), ...store.findAllVersionsByLabel("Vulnerability")].map((p) => {
|
|
43977
44038
|
const ra = p.attrs["resolvedAs"];
|
|
43978
44039
|
const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
|
|
43979
44040
|
const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
|
|
43980
44041
|
const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
|
|
43981
44042
|
return {
|
|
43982
44043
|
id: p.id,
|
|
44044
|
+
label: p.label,
|
|
44045
|
+
...p.label === "Vulnerability" ? { assessment: p.attrs["assessment"] } : {},
|
|
43983
44046
|
problem: p.description,
|
|
43984
44047
|
status,
|
|
43985
44048
|
createdAt: p.createdAt,
|
|
@@ -52544,7 +52607,8 @@ function errorRecallTerms(errorText) {
|
|
|
52544
52607
|
}
|
|
52545
52608
|
function recallLine(n, token) {
|
|
52546
52609
|
const desc = (n.description ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
52547
|
-
|
|
52610
|
+
const label = n.label === "Vulnerability" ? `Vulnerability; ${n.attrs["assessment"] ?? "unassessed"}` : n.label;
|
|
52611
|
+
return `- [${label}] ${desc} \u2192 ${token}`;
|
|
52548
52612
|
}
|
|
52549
52613
|
function recallForFile(store, relPath) {
|
|
52550
52614
|
return fileRecall(store, relPath)?.context ?? null;
|
|
@@ -52565,7 +52629,7 @@ function fileRecall(store, relPath) {
|
|
|
52565
52629
|
const shownRelated = priors.related.slice(0, 4);
|
|
52566
52630
|
for (const p of shownProblems) {
|
|
52567
52631
|
const hasCause = store.outEdges(p.id, ["CAUSED_BY"]).some((e) => store.getNode(e.to)?.label === "RootCause");
|
|
52568
|
-
const causeCue = hasCause ? "" : ` \xB7 ${TAG_EXAMPLE.triageMint()} to diagnose its cause`;
|
|
52632
|
+
const causeCue = hasCause ? "" : ` \xB7 ${p.label === "Vulnerability" ? `(cause:#${p.id} the mechanism)` : TAG_EXAMPLE.triageMint()} to diagnose its cause`;
|
|
52569
52633
|
lines.push(
|
|
52570
52634
|
recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
|
|
52571
52635
|
);
|
|
@@ -75829,7 +75893,7 @@ init_src5();
|
|
|
75829
75893
|
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
|
|
75830
75894
|
var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
|
|
75831
75895
|
var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
|
|
75832
|
-
var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
|
|
75896
|
+
var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})/gi;
|
|
75833
75897
|
var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
75834
75898
|
"handle",
|
|
75835
75899
|
"its-handle",
|
|
@@ -75844,7 +75908,7 @@ var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
|
75844
75908
|
"b"
|
|
75845
75909
|
]);
|
|
75846
75910
|
var isPlaceholderHandle = (h) => PLACEHOLDER_HANDLES.has(h.toLowerCase());
|
|
75847
|
-
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;
|
|
75911
|
+
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;
|
|
75848
75912
|
var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
75849
75913
|
var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
|
|
75850
75914
|
var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
|
|
@@ -75869,7 +75933,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
|
|
|
75869
75933
|
var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
75870
75934
|
var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
|
|
75871
75935
|
var CAUSE_TEXT_MIN = 8;
|
|
75872
|
-
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n])
|
|
75936
|
+
var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n])*?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
|
|
75873
75937
|
var CONSTRAINT_MIN = 8;
|
|
75874
75938
|
function clauseBefore(sentence, idx) {
|
|
75875
75939
|
const win = sentence.slice(Math.max(0, idx - 80), idx);
|
|
@@ -76013,6 +76077,7 @@ function cleanEmittedTags(tags) {
|
|
|
76013
76077
|
}
|
|
76014
76078
|
function emptyTagDispositions() {
|
|
76015
76079
|
return {
|
|
76080
|
+
invalidVulnerability: 0,
|
|
76016
76081
|
oversizeSegments: 0,
|
|
76017
76082
|
codeSpanNeutralized: 0,
|
|
76018
76083
|
refuteTooShort: 0,
|
|
@@ -76025,6 +76090,7 @@ function emptyTagDispositions() {
|
|
|
76025
76090
|
};
|
|
76026
76091
|
}
|
|
76027
76092
|
function addTagDispositions(a, b) {
|
|
76093
|
+
a.invalidVulnerability += b.invalidVulnerability;
|
|
76028
76094
|
a.oversizeSegments += b.oversizeSegments;
|
|
76029
76095
|
a.codeSpanNeutralized += b.codeSpanNeutralized;
|
|
76030
76096
|
a.refuteTooShort += b.refuteTooShort;
|
|
@@ -76038,12 +76104,31 @@ function addTagDispositions(a, b) {
|
|
|
76038
76104
|
function parseInlineTags(text) {
|
|
76039
76105
|
return parseInlineTagsWithDispositions(text).tags;
|
|
76040
76106
|
}
|
|
76107
|
+
function* captureSentences(text) {
|
|
76108
|
+
for (const line of text.split(/\n+/)) {
|
|
76109
|
+
if (line.length > 2e4) {
|
|
76110
|
+
yield line;
|
|
76111
|
+
continue;
|
|
76112
|
+
}
|
|
76113
|
+
const flags2 = [...line.matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => ({ start: m.index, end: m.index + m[0].length }));
|
|
76114
|
+
let flag = 0;
|
|
76115
|
+
let start2 = 0;
|
|
76116
|
+
for (const boundary of line.matchAll(/(?<=[.!?])\s+(?!\(fix:)/g)) {
|
|
76117
|
+
const index = boundary.index;
|
|
76118
|
+
while (flags2[flag] && flags2[flag].end <= index) flag++;
|
|
76119
|
+
if (flags2[flag] && flags2[flag].start < index) continue;
|
|
76120
|
+
yield line.slice(start2, index);
|
|
76121
|
+
start2 = index + boundary[0].length;
|
|
76122
|
+
}
|
|
76123
|
+
yield line.slice(start2);
|
|
76124
|
+
}
|
|
76125
|
+
}
|
|
76041
76126
|
function parseInlineTagsWithDispositions(text) {
|
|
76042
76127
|
const out2 = [];
|
|
76043
76128
|
const dispositions = emptyTagDispositions();
|
|
76044
76129
|
const deFenced = text.replace(/```[\s\S]*?```/g, " ");
|
|
76045
76130
|
let seqNo = -1;
|
|
76046
|
-
for (const raw2 of deFenced
|
|
76131
|
+
for (const raw2 of captureSentences(deFenced)) {
|
|
76047
76132
|
seqNo++;
|
|
76048
76133
|
if (raw2.length > 2e4) {
|
|
76049
76134
|
dispositions.oversizeSegments++;
|
|
@@ -76269,6 +76354,29 @@ function parseInlineTagsWithDispositions(text) {
|
|
|
76269
76354
|
while ((f = FLAG_RE.exec(sentence)) !== null) {
|
|
76270
76355
|
const flagThread = f[2];
|
|
76271
76356
|
const raw3 = f[3].trim();
|
|
76357
|
+
if (/^vuln\b/i.test(raw3)) {
|
|
76358
|
+
const typed = /^vuln:\s*([\s\S]*)$/i.exec(raw3);
|
|
76359
|
+
const body2 = typed?.[1]?.trim() ?? "";
|
|
76360
|
+
const parts2 = body2.split(CAUSE_ARROW).map((s) => s.trim());
|
|
76361
|
+
const symptom = f[1] === "!" && parts2.length > 1 && !f[4] ? new RegExp(FLAG_RE.source, "i").exec(`[!${parts2[0]}]`) : null;
|
|
76362
|
+
const statement = f[1] === "?" ? body2 : symptom?.[3]?.trim() ?? parts2[0];
|
|
76363
|
+
if (statement.length < 8 || /^<[^>]*>$/.test(statement)) {
|
|
76364
|
+
dispositions.invalidVulnerability++;
|
|
76365
|
+
continue;
|
|
76366
|
+
}
|
|
76367
|
+
const path2 = (f[4] ?? symptom?.[4])?.trim();
|
|
76368
|
+
const line = f[5] ?? symptom?.[5];
|
|
76369
|
+
out2.push({
|
|
76370
|
+
kind: "vulnerability",
|
|
76371
|
+
statement,
|
|
76372
|
+
assessment: f[1] === "!" ? "asserted" : "suspected",
|
|
76373
|
+
...f[1] === "!" && parts2.length > 1 ? { vulnerabilityCauses: parts2.slice(1).filter((s) => s.length >= CAUSE_TEXT_MIN) } : {},
|
|
76374
|
+
...path2 ? { location: { path: path2, ...line ? { line: Number(line) } : {} } } : {},
|
|
76375
|
+
...flagThread ? { threadId: flagThread } : {},
|
|
76376
|
+
sentence: sfield
|
|
76377
|
+
});
|
|
76378
|
+
continue;
|
|
76379
|
+
}
|
|
76272
76380
|
if (FLAG_BODY_HANDLE_RE.test(raw3) && !f[4]) {
|
|
76273
76381
|
dispositions.flagAsHandle++;
|
|
76274
76382
|
out2.push({ kind: "prior", handle: raw3, sentence: sfield });
|
|
@@ -76639,7 +76747,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76639
76747
|
const parsed = parseInlineTagsWithDispositions(text);
|
|
76640
76748
|
const tags = parsed.tags;
|
|
76641
76749
|
plan.dispositions = parsed.dispositions;
|
|
76642
|
-
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);
|
|
76750
|
+
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);
|
|
76643
76751
|
const bindSymptom = (seq, threadId) => {
|
|
76644
76752
|
if (threadId) {
|
|
76645
76753
|
const hit = symptomSeqs.find((s) => s.threadId === threadId);
|
|
@@ -76652,6 +76760,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76652
76760
|
if (seq == null) return { evidence: "inferred" };
|
|
76653
76761
|
let best;
|
|
76654
76762
|
for (const s of symptomSeqs) {
|
|
76763
|
+
if (s.kind === "vulnerability") continue;
|
|
76655
76764
|
if (s.seq <= seq) best = s;
|
|
76656
76765
|
else break;
|
|
76657
76766
|
}
|
|
@@ -76666,10 +76775,13 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76666
76775
|
return id;
|
|
76667
76776
|
};
|
|
76668
76777
|
for (const tag of tags) {
|
|
76669
|
-
if (tag.kind === "problem" || tag.kind === "todo") {
|
|
76778
|
+
if (tag.kind === "problem" || tag.kind === "todo" || tag.kind === "vulnerability") {
|
|
76670
76779
|
plan.problems.push({
|
|
76671
76780
|
statement: tag.statement,
|
|
76672
76781
|
kind: tag.kind,
|
|
76782
|
+
...tag.assessment ? { assessment: tag.assessment } : {},
|
|
76783
|
+
...tag.vulnerabilityCauses ? { vulnerabilityCauses: tag.vulnerabilityCauses } : {},
|
|
76784
|
+
...tag.kind === "vulnerability" && tag.seq !== void 0 ? { seq: tag.seq } : {},
|
|
76673
76785
|
...tag.location ? { location: tag.location } : {},
|
|
76674
76786
|
...tag.threadId ? { threadId: tag.threadId } : {}
|
|
76675
76787
|
});
|
|
@@ -76692,7 +76804,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76692
76804
|
} else if (tag.threadId && !b.statement) {
|
|
76693
76805
|
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, threadId: tag.threadId });
|
|
76694
76806
|
} else if (b.statement && b.evidence === "witnessed") {
|
|
76695
|
-
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement });
|
|
76807
|
+
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement, ...tag.threadId ? { threadId: tag.threadId } : {} });
|
|
76696
76808
|
} else {
|
|
76697
76809
|
plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, ambiguous: true });
|
|
76698
76810
|
}
|
|
@@ -76895,6 +77007,7 @@ function harvestInlineTags(store, text, opts) {
|
|
|
76895
77007
|
}
|
|
76896
77008
|
var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
|
|
76897
77009
|
"Problem",
|
|
77010
|
+
"Vulnerability",
|
|
76898
77011
|
"Solution",
|
|
76899
77012
|
"RootCause",
|
|
76900
77013
|
"Pattern",
|
|
@@ -80673,9 +80786,9 @@ function partitionReplayable(queue, channel, isShipped) {
|
|
|
80673
80786
|
function retireWitnesses(queue, channel, settledKeys) {
|
|
80674
80787
|
return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
|
|
80675
80788
|
}
|
|
80676
|
-
var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
|
|
80789
|
+
var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Vulnerability", "Solution", "RootCause"]);
|
|
80677
80790
|
var MOTIF_ID_PREFIX = "motif_community_";
|
|
80678
|
-
var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,}$/i;
|
|
80791
|
+
var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,}$/i;
|
|
80679
80792
|
function classifyWitnessTarget(nodeId, getNode) {
|
|
80680
80793
|
if (nodeId.startsWith(MOTIF_ID_PREFIX)) {
|
|
80681
80794
|
const exemplar = nodeId.slice(MOTIF_ID_PREFIX.length);
|
|
@@ -81245,7 +81358,7 @@ function createWatchBreaker(deps) {
|
|
|
81245
81358
|
}
|
|
81246
81359
|
|
|
81247
81360
|
// src/engine.ts
|
|
81248
|
-
var DAEMON_VERSION = true ? "2.0.
|
|
81361
|
+
var DAEMON_VERSION = true ? "2.0.16-dev.3343" : "2.0.0-alpha.0";
|
|
81249
81362
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
81250
81363
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
81251
81364
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -82137,6 +82250,9 @@ function createWorkspaceEngine(opts) {
|
|
|
82137
82250
|
const harvestTextsInner = async (sessionId, items) => {
|
|
82138
82251
|
let minted = 0;
|
|
82139
82252
|
let tagsSeen = 0;
|
|
82253
|
+
let vulnerabilitiesSeen = 0;
|
|
82254
|
+
let vulnerabilitiesMinted = 0;
|
|
82255
|
+
let vulnerabilitiesSuspected = 0;
|
|
82140
82256
|
let tagsRejected = 0;
|
|
82141
82257
|
let tagsRestated = 0;
|
|
82142
82258
|
let resolved = 0;
|
|
@@ -82367,10 +82483,73 @@ function createWorkspaceEngine(opts) {
|
|
|
82367
82483
|
if ((toolRuns.get(sessionId)?.size ?? 0) > 0) touchedToolTurns++;
|
|
82368
82484
|
const wf = workingFiles.get(sessionId);
|
|
82369
82485
|
let inScopeProblemId;
|
|
82486
|
+
const findingIds = /* @__PURE__ */ new Map();
|
|
82487
|
+
const conflictingThreads = /* @__PURE__ */ new Set();
|
|
82370
82488
|
const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
|
|
82371
82489
|
sessionThreads.set(sessionId, threads);
|
|
82372
82490
|
for (const p of plan.problems) {
|
|
82373
82491
|
tagsSeen++;
|
|
82492
|
+
if (p.kind === "vulnerability") {
|
|
82493
|
+
vulnerabilitiesSeen++;
|
|
82494
|
+
if (p.assessment === "suspected") vulnerabilitiesSuspected++;
|
|
82495
|
+
let fileText;
|
|
82496
|
+
let anchorReadFailed = false;
|
|
82497
|
+
if (p.location) {
|
|
82498
|
+
try {
|
|
82499
|
+
fileText = readFileSync29(join35(opts.workspaceRoot, p.location.path), "utf8");
|
|
82500
|
+
} catch {
|
|
82501
|
+
anchorReadFailed = true;
|
|
82502
|
+
}
|
|
82503
|
+
}
|
|
82504
|
+
const result = ingestWitnessedVulnerability(store, {
|
|
82505
|
+
statement: p.statement,
|
|
82506
|
+
assessment: p.assessment,
|
|
82507
|
+
target: p.location?.path,
|
|
82508
|
+
line: p.location?.line,
|
|
82509
|
+
fileText,
|
|
82510
|
+
existingId: p.threadId ? threads.get(p.threadId) ?? handleMap[p.threadId]?.id ?? store.getNode(p.threadId)?.id : void 0
|
|
82511
|
+
}, { workspaceId: profile.id, source: sessionId, ts: t, hostHarness: turn.hostHarness });
|
|
82512
|
+
let anchorDisposition = p.location ? "unresolved" : "absent";
|
|
82513
|
+
if (result.rejected) {
|
|
82514
|
+
tagsRejected++;
|
|
82515
|
+
if (p.threadId) conflictingThreads.add(p.threadId);
|
|
82516
|
+
} else {
|
|
82517
|
+
findingIds.set(p.statement, result.id);
|
|
82518
|
+
if (p.threadId) threads.set(p.threadId, result.id);
|
|
82519
|
+
if (result.created || result.corroborated || result.upgraded) minted++;
|
|
82520
|
+
else tagsRestated++;
|
|
82521
|
+
if (result.created) vulnerabilitiesMinted++;
|
|
82522
|
+
if (p.location) {
|
|
82523
|
+
try {
|
|
82524
|
+
const anchored = anchorProblemToWorkingFile(store, result.id, p.location.path, profile.id, t, "witnessed", { line: p.location.line, fileText });
|
|
82525
|
+
anchorDisposition = anchored ? "resolved" : anchorReadFailed ? "error" : "unresolved";
|
|
82526
|
+
if (anchored) deriveContextFromAnchors(store, result.id, t);
|
|
82527
|
+
} catch {
|
|
82528
|
+
anchorDisposition = "error";
|
|
82529
|
+
}
|
|
82530
|
+
}
|
|
82531
|
+
const node2 = store.getNode(result.id);
|
|
82532
|
+
store.updateNode(result.id, { attrs: { ...node2.attrs, anchorDisposition } });
|
|
82533
|
+
if (p.vulnerabilityCauses) triaged += recordVulnerabilityCause(store, result.id, p.vulnerabilityCauses, t);
|
|
82534
|
+
}
|
|
82535
|
+
log.append({
|
|
82536
|
+
kind: "vulnerability.witness",
|
|
82537
|
+
workspaceId: profile.id,
|
|
82538
|
+
ts: t,
|
|
82539
|
+
digest: digest({ kind: "vulnerability.witness", workspaceId: profile.id, sessionId, turnId: turn.uuid, statement: p.statement, assessment: p.assessment, location: p.location }),
|
|
82540
|
+
sessionId,
|
|
82541
|
+
turnId: turn.uuid,
|
|
82542
|
+
harness: turn.hostHarness,
|
|
82543
|
+
findingId: result.id,
|
|
82544
|
+
statement: p.statement,
|
|
82545
|
+
assessment: p.assessment,
|
|
82546
|
+
location: p.location,
|
|
82547
|
+
seq: p.seq,
|
|
82548
|
+
anchorDisposition,
|
|
82549
|
+
disposition: result.rejected ?? (result.created ? "created" : result.upgraded ? "upgraded" : result.corroborated ? "corroborated" : "restated")
|
|
82550
|
+
});
|
|
82551
|
+
continue;
|
|
82552
|
+
}
|
|
82374
82553
|
const anchorPath = p.location?.path ?? editedTurnFile;
|
|
82375
82554
|
const anchorProvenance = p.location?.path ? "witnessed" : "edited";
|
|
82376
82555
|
const hintPath = anchorPath ? void 0 : turnFile ?? wf;
|
|
@@ -82465,10 +82644,10 @@ function createWorkspaceEngine(opts) {
|
|
|
82465
82644
|
if (r.created || r.corroborated) {
|
|
82466
82645
|
minted++;
|
|
82467
82646
|
if (p.kind !== "constraint") {
|
|
82468
|
-
sessionLastProblem.set(sessionId,
|
|
82469
|
-
inScopeProblemId =
|
|
82647
|
+
sessionLastProblem.set(sessionId, r.problemId);
|
|
82648
|
+
inScopeProblemId = r.problemId;
|
|
82470
82649
|
}
|
|
82471
|
-
if (p.threadId) threads.set(p.threadId,
|
|
82650
|
+
if (p.threadId) threads.set(p.threadId, r.problemId);
|
|
82472
82651
|
try {
|
|
82473
82652
|
if (anchorPath) {
|
|
82474
82653
|
let enclosing = {};
|
|
@@ -82505,7 +82684,8 @@ function createWorkspaceEngine(opts) {
|
|
|
82505
82684
|
);
|
|
82506
82685
|
}
|
|
82507
82686
|
for (const fx of plan.fixes) {
|
|
82508
|
-
|
|
82687
|
+
if (fx.threadId && conflictingThreads.has(fx.threadId)) continue;
|
|
82688
|
+
const pid = fx.problemId ?? (fx.threadId ? threads.get(fx.threadId) : void 0) ?? (fx.boundStatement ? findingIds.get(fx.boundStatement) ?? designProblemId(fx.boundStatement) : void 0);
|
|
82509
82689
|
if (!pid) {
|
|
82510
82690
|
if (fx.inScope) {
|
|
82511
82691
|
console.warn(
|
|
@@ -82517,7 +82697,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82517
82697
|
const closed = resolveDesignProblemById(store, pid, fx.fixNote, t);
|
|
82518
82698
|
if (!closed) {
|
|
82519
82699
|
const n = store.getNode(pid);
|
|
82520
|
-
if (!n || n.label !== "Problem" || n.attrs["resolvedAs"] != null) {
|
|
82700
|
+
if (!n || n.label !== "Problem" && n.label !== "Vulnerability" || n.attrs["resolvedAs"] != null) {
|
|
82521
82701
|
console.warn(
|
|
82522
82702
|
`[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`
|
|
82523
82703
|
);
|
|
@@ -82536,10 +82716,23 @@ function createWorkspaceEngine(opts) {
|
|
|
82536
82716
|
}
|
|
82537
82717
|
}
|
|
82538
82718
|
}
|
|
82719
|
+
const vulnerabilityTriages = /* @__PURE__ */ new Set();
|
|
82720
|
+
for (const tr of plan.triages) {
|
|
82721
|
+
if (tr.threadId && conflictingThreads.has(tr.threadId)) {
|
|
82722
|
+
vulnerabilityTriages.add(tr);
|
|
82723
|
+
continue;
|
|
82724
|
+
}
|
|
82725
|
+
const id = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? (tr.symptomStatement ? findingIds.get(tr.symptomStatement) : void 0);
|
|
82726
|
+
if (id && store.getNode(id)?.label === "Vulnerability") {
|
|
82727
|
+
vulnerabilityTriages.add(tr);
|
|
82728
|
+
if (tr.evidence === "witnessed" && tr.causeDescription) triaged += recordVulnerabilityCause(store, id, [tr.causeDescription], t);
|
|
82729
|
+
}
|
|
82730
|
+
}
|
|
82539
82731
|
if (opts.sharedStore && plan.triages.length > 0) {
|
|
82540
82732
|
const fallbackId = sessionLastProblem.get(sessionId);
|
|
82541
82733
|
const fallbackStatement = fallbackId ? store.getNode(fallbackId)?.description : void 0;
|
|
82542
82734
|
for (const tr of plan.triages) {
|
|
82735
|
+
if (vulnerabilityTriages.has(tr)) continue;
|
|
82543
82736
|
let presentingStatement = tr.symptomStatement;
|
|
82544
82737
|
let evidence = tr.evidence;
|
|
82545
82738
|
if (!presentingStatement && tr.threadId) {
|
|
@@ -82557,7 +82750,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82557
82750
|
evidence = "inferred";
|
|
82558
82751
|
}
|
|
82559
82752
|
if (!presentingStatement) continue;
|
|
82560
|
-
const presentingId = tr.symptomProblemId ?? designProblemId(presentingStatement);
|
|
82753
|
+
const presentingId = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? designProblemId(presentingStatement);
|
|
82561
82754
|
if (tr.causeId === presentingId) continue;
|
|
82562
82755
|
try {
|
|
82563
82756
|
recordTriageObservation(
|
|
@@ -82615,7 +82808,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82615
82808
|
}
|
|
82616
82809
|
}
|
|
82617
82810
|
for (const at of plan.attempts) {
|
|
82618
|
-
const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? designProblemId(at.boundStatement) : void 0);
|
|
82811
|
+
const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? findingIds.get(at.boundStatement) ?? designProblemId(at.boundStatement) : void 0);
|
|
82619
82812
|
const node2 = pid ? store.getNode(pid) : void 0;
|
|
82620
82813
|
if (!pid || !node2) continue;
|
|
82621
82814
|
try {
|
|
@@ -82631,7 +82824,7 @@ function createWorkspaceEngine(opts) {
|
|
|
82631
82824
|
}
|
|
82632
82825
|
}
|
|
82633
82826
|
const bindPid = (tag) => {
|
|
82634
|
-
const pid = tag.problemId ?? (tag.threadId ? threads.get(tag.threadId) : void 0) ?? (tag.boundStatement ? designProblemId(tag.boundStatement) : void 0) ?? inScopeProblemId ?? sessionLastProblem.get(sessionId);
|
|
82827
|
+
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);
|
|
82635
82828
|
return pid && store.getNode(pid) ? pid : void 0;
|
|
82636
82829
|
};
|
|
82637
82830
|
const mintCiteEdge = (from, to, type, confidence, extraAttrs) => {
|
|
@@ -82785,7 +82978,12 @@ function createWorkspaceEngine(opts) {
|
|
|
82785
82978
|
for (const [session, items2] of groups) {
|
|
82786
82979
|
if (items2.length === 0) continue;
|
|
82787
82980
|
try {
|
|
82788
|
-
const
|
|
82981
|
+
const wireItems = items2.map(({ nodeId, witnessKey }) => {
|
|
82982
|
+
const node2 = store.getNode(nodeId);
|
|
82983
|
+
const cloudId = node2?.attrs["privateFinding"] ? node2.attrs["cloudNodeId"] : void 0;
|
|
82984
|
+
return { nodeId: typeof cloudId === "string" ? cloudId : nodeId, witnessKey };
|
|
82985
|
+
});
|
|
82986
|
+
const raw2 = await send(wireItems, session);
|
|
82789
82987
|
const d = {
|
|
82790
82988
|
recorded: raw2?.recorded ?? 0,
|
|
82791
82989
|
unmatched: raw2?.unmatched ?? 0,
|
|
@@ -82798,8 +82996,8 @@ function createWorkspaceEngine(opts) {
|
|
|
82798
82996
|
duplicate += d.duplicate;
|
|
82799
82997
|
selfGated += d.selfGated;
|
|
82800
82998
|
const missing = new Set(d.unmatchedIds);
|
|
82801
|
-
for (const it of items2) {
|
|
82802
|
-
if (missing.has(
|
|
82999
|
+
for (const [index, it] of items2.entries()) {
|
|
83000
|
+
if (missing.has(wireItems[index].nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: isShipped(it.nodeId) });
|
|
82803
83001
|
else settled.add(it.witnessKey);
|
|
82804
83002
|
}
|
|
82805
83003
|
} catch (err2) {
|
|
@@ -82969,6 +83167,10 @@ function createWorkspaceEngine(opts) {
|
|
|
82969
83167
|
// but nothing judgeable was cited.
|
|
82970
83168
|
tagsVerdictNoHandle: dispositions.verdictNoHandle,
|
|
82971
83169
|
tagsFlagAsHandle: dispositions.flagAsHandle,
|
|
83170
|
+
tagsInvalidVulnerability: dispositions.invalidVulnerability,
|
|
83171
|
+
vulnerabilitiesSeen,
|
|
83172
|
+
vulnerabilitiesMinted,
|
|
83173
|
+
vulnerabilitiesSuspected,
|
|
82972
83174
|
unresolvedHandles,
|
|
82973
83175
|
priorEdgesFlagOff,
|
|
82974
83176
|
priorEdgesNoSource,
|
|
@@ -84305,7 +84507,7 @@ function internalSymbolsFor(description, lexiconHas, publicIndex) {
|
|
|
84305
84507
|
|
|
84306
84508
|
// src/instance-ingest.ts
|
|
84307
84509
|
init_generalize_graph();
|
|
84308
|
-
var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
|
|
84510
|
+
var INSTANCE_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
84309
84511
|
var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
|
|
84310
84512
|
var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
|
|
84311
84513
|
var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
|
|
@@ -84440,6 +84642,10 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84440
84642
|
attrs: {
|
|
84441
84643
|
scope: stripCodebaseScope(n.attrs["scope"]),
|
|
84442
84644
|
...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
|
|
84645
|
+
...n.attrs["privateFinding"] ? { privateFinding: true } : {},
|
|
84646
|
+
...n.attrs["assessment"] ? { assessment: n.attrs["assessment"] } : {},
|
|
84647
|
+
...n.attrs["sourceClass"] ? { sourceClass: n.attrs["sourceClass"] } : {},
|
|
84648
|
+
...n.attrs["resolutionWitnessed"] ? { resolutionWitnessed: true, resolvedAt: n.attrs["resolvedAt"] } : {},
|
|
84443
84649
|
...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {},
|
|
84444
84650
|
...preserved.length > 0 ? { preservedSymbols: preserved } : {},
|
|
84445
84651
|
...internal.length > 0 ? { internalSymbols: internal } : {}
|
|
@@ -84454,6 +84660,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84454
84660
|
const semanticEndpoints = /* @__PURE__ */ new Map();
|
|
84455
84661
|
const includedByLabel = /* @__PURE__ */ new Map();
|
|
84456
84662
|
const composition = {
|
|
84663
|
+
privateTargetMissing: 0,
|
|
84457
84664
|
candidates: 0,
|
|
84458
84665
|
shipped: 0,
|
|
84459
84666
|
cloudSourced: 0,
|
|
@@ -84499,11 +84706,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84499
84706
|
if (walked.has(n.id)) continue;
|
|
84500
84707
|
walked.add(n.id);
|
|
84501
84708
|
composition.candidates++;
|
|
84709
|
+
if ((n.attrs["privateFinding"] || n.label === "Vulnerability") && !opts.project) {
|
|
84710
|
+
composition.privateTargetMissing++;
|
|
84711
|
+
continue;
|
|
84712
|
+
}
|
|
84502
84713
|
if (n.attrs["source"] === "cloud") {
|
|
84503
84714
|
composition.cloudSourced++;
|
|
84504
84715
|
continue;
|
|
84505
84716
|
}
|
|
84506
|
-
if (
|
|
84717
|
+
if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
|
|
84507
84718
|
composition.falsePositive++;
|
|
84508
84719
|
continue;
|
|
84509
84720
|
}
|
|
@@ -84526,7 +84737,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84526
84737
|
composition.terminalRefused++;
|
|
84527
84738
|
continue;
|
|
84528
84739
|
}
|
|
84529
|
-
if (!noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
84740
|
+
if (!n.attrs["privateFinding"] && !noveltyAgainst(n, ref, noveltyOpts).ready) {
|
|
84530
84741
|
composition.notNovel++;
|
|
84531
84742
|
continue;
|
|
84532
84743
|
}
|
|
@@ -84569,7 +84780,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
84569
84780
|
}
|
|
84570
84781
|
if (targets.length === 0) continue;
|
|
84571
84782
|
const shipped = shippedById.get(sourceId);
|
|
84572
|
-
if (shipped) {
|
|
84783
|
+
if (shipped && !shipped.attrs["privateFinding"]) {
|
|
84573
84784
|
const anchors = (label) => targets.filter((x) => x.t.label === label).map(
|
|
84574
84785
|
(x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
|
|
84575
84786
|
).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) => [
|