@agentskit/doc-bridge 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -161,6 +161,7 @@ var GatesConfigSchema = z.object({
161
161
  include: z.array(
162
162
  z.enum([
163
163
  "index-freshness",
164
+ "index-reproducible",
164
165
  "human-guide-links",
165
166
  "link-rot",
166
167
  "okf-type",
@@ -173,6 +174,7 @@ var GatesConfigSchema = z.object({
173
174
  exclude: z.array(
174
175
  z.enum([
175
176
  "index-freshness",
177
+ "index-reproducible",
176
178
  "human-guide-links",
177
179
  "link-rot",
178
180
  "okf-type",
@@ -1331,8 +1333,13 @@ var RetrievalIndexV1Schema = z5.object({
1331
1333
  schemaVersion: z5.literal(RETRIEVAL_INDEX_SCHEMA_VERSION),
1332
1334
  contentHash: hash2,
1333
1335
  contentHashAlgo: z5.literal("sha256-normalized-v1"),
1334
- /** The three inputs the projection is a function of. Same three hashes, same projection. */
1336
+ /**
1337
+ * Which snapshot this was projected from. Provenance, not a seal input: it carries the
1338
+ * snapshot's `sourceRevision`, and the projection is a function of what the snapshot observed
1339
+ * rather than of the revision it was observed at.
1340
+ */
1335
1341
  snapshotHash: hash2,
1342
+ /** The inputs the projection is a function of. Same hashes, same projection. */
1336
1343
  overlayHash: hash2,
1337
1344
  configurationHash: hash2,
1338
1345
  lexiconVersion: z5.number().int().nonnegative().max(1e3),
@@ -4393,7 +4400,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
4393
4400
  };
4394
4401
 
4395
4402
  // src/version.ts
4396
- var PACKAGE_VERSION = "1.10.0";
4403
+ var PACKAGE_VERSION = "1.11.0";
4397
4404
 
4398
4405
  // src/index-builder/capabilities.ts
4399
4406
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -5973,7 +5980,7 @@ var resolveSearchParams = (configured) => ({
5973
5980
  });
5974
5981
 
5975
5982
  // src/retrieval/project.ts
5976
- var RETRIEVAL_PROJECTION_VERSION = 1;
5983
+ var RETRIEVAL_PROJECTION_VERSION = 2;
5977
5984
  var DOCUMENT_BODY_LIMIT = 4e3;
5978
5985
  var MAX_EDGES = 64;
5979
5986
  var MAX_ALIASES = 32;
@@ -5981,6 +5988,12 @@ var MAX_TAGS = 32;
5981
5988
  var MAX_SYMBOLS = 256;
5982
5989
  var MAX_SUMMARY = 400;
5983
5990
  var EMPTY_OVERLAY_HASH = sha256NormalizedV1({ accepted: [] });
5991
+ var snapshotObservationHash = (snapshot) => sha256NormalizedV1({
5992
+ pipelineVersion: snapshot.pipelineVersion,
5993
+ analyzerVersions: snapshot.analyzerVersions,
5994
+ entities: snapshot.entities,
5995
+ relations: snapshot.relations
5996
+ });
5984
5997
  var CONFIDENCE_RANK = { observed: 0, declared: 1, fuzzy: 2, proposed: 3 };
5985
5998
  var weakerConfidence = (a, b) => CONFIDENCE_RANK[a] >= CONFIDENCE_RANK[b] ? a : b;
5986
5999
  var relationConfidence = (relation) => relation.metadata?.confidence === "fuzzy" ? "fuzzy" : relation.provenance;
@@ -6241,7 +6254,7 @@ var projectRetrievalIndex = (options) => {
6241
6254
  };
6242
6255
  const contentHash = sha256NormalizedV1({
6243
6256
  projectionVersion: RETRIEVAL_PROJECTION_VERSION,
6244
- snapshotHash: base.snapshotHash,
6257
+ observationHash: snapshotObservationHash(snapshot),
6245
6258
  overlayHash: base.overlayHash,
6246
6259
  configurationHash: base.configurationHash,
6247
6260
  lexiconVersion: base.lexiconVersion,
@@ -7070,6 +7083,50 @@ var buildDocBridgeIndex = (opts) => {
7070
7083
  };
7071
7084
  };
7072
7085
 
7086
+ // src/discovery/reproducibility.ts
7087
+ import { execFileSync as execFileSync2 } from "child_process";
7088
+ var NOT_CHECKED = (skipped) => ({
7089
+ checked: false,
7090
+ skipped,
7091
+ ignored: []
7092
+ });
7093
+ var git = (root, args, options = {}) => {
7094
+ try {
7095
+ return execFileSync2("git", args, {
7096
+ cwd: root,
7097
+ encoding: "utf8",
7098
+ stdio: ["pipe", "pipe", "ignore"],
7099
+ maxBuffer: 64 * 1024 * 1024,
7100
+ ...options.input === void 0 ? {} : { input: options.input }
7101
+ });
7102
+ } catch (error) {
7103
+ const status = error.status;
7104
+ if (options.allowExit1 && status === 1) return error.stdout ?? "";
7105
+ return void 0;
7106
+ }
7107
+ };
7108
+ var checkIndexReproducibility = (root, indexPath, paths) => {
7109
+ if (git(root, ["rev-parse", "--is-inside-work-tree"]) === void 0) return NOT_CHECKED("no-git");
7110
+ if (git(root, ["ls-files", "--error-unmatch", "--", indexPath]) === void 0) {
7111
+ return NOT_CHECKED("index-untracked");
7112
+ }
7113
+ if (paths.length === 0) return { checked: true, ignored: [] };
7114
+ const unique3 = [...new Set(paths)].sort();
7115
+ const output = git(root, ["check-ignore", "--verbose", "-z", "--stdin"], {
7116
+ input: `${unique3.join("\0")}\0`,
7117
+ allowExit1: true
7118
+ });
7119
+ if (output === void 0) return NOT_CHECKED("no-git");
7120
+ const fields = output.split("\0");
7121
+ const ignored = [];
7122
+ for (let index = 0; index + 3 < fields.length; index += 4) {
7123
+ const [source, line, pattern, path] = [fields[index], fields[index + 1], fields[index + 2], fields[index + 3]];
7124
+ if (!path) continue;
7125
+ ignored.push({ path, rule: `${source ?? "?"}:${line ?? "?"}:${pattern ?? "?"}` });
7126
+ }
7127
+ return { checked: true, ignored: ignored.sort((left, right) => left.path.localeCompare(right.path)) };
7128
+ };
7129
+
7073
7130
  // src/retrieval/rank.ts
7074
7131
  var BM25_SCALE = 50;
7075
7132
  var EXACT_ID = 200;
@@ -11036,6 +11093,38 @@ var runGate = (root, config, id) => {
11036
11093
  if (id === "human-guide-links") return runHumanGuideLinksGate(root, config);
11037
11094
  if (id === "okf-type") return runOkfTypeGate(root, config);
11038
11095
  if (id === "docs-style") return runDocsStyleGate(root, config);
11096
+ if (id === "index-reproducible") {
11097
+ let index;
11098
+ try {
11099
+ index = loadDocBridgeIndex(root, config);
11100
+ } catch (error) {
11101
+ if (error instanceof IndexNotFoundError) return { id, ok: false, message: error.message };
11102
+ throw error;
11103
+ }
11104
+ const result = checkIndexReproducibility(
11105
+ root,
11106
+ config.index?.outFile ?? ".doc-bridge/index.json",
11107
+ index.knowledge.map((entry) => entry.path)
11108
+ );
11109
+ if (!result.checked) {
11110
+ return {
11111
+ id,
11112
+ ok: true,
11113
+ message: result.skipped === "index-untracked" ? "Index is not committed, so nothing has to reproduce it" : "Not a Git checkout; reproducibility was not checked"
11114
+ };
11115
+ }
11116
+ if (result.ignored.length === 0) {
11117
+ return { id, ok: true, message: "Every indexed path is committed" };
11118
+ }
11119
+ const sample = result.ignored.slice(0, 5).map((entry) => `${entry.path} (${entry.rule})`);
11120
+ return {
11121
+ id,
11122
+ ok: false,
11123
+ message: `${result.ignored.length} indexed path(s) are ignored by Git, so a clean checkout builds a different index. Add them to safety.exclude.`,
11124
+ expected: "every indexed path is committed",
11125
+ actual: sample.join(", ")
11126
+ };
11127
+ }
11039
11128
  if (id !== "index-freshness") throw new Error(`Unsupported gate "${id}"`);
11040
11129
  let current;
11041
11130
  try {
@@ -13279,6 +13368,16 @@ var buildIssues = (coverage) => {
13279
13368
  action: "ak-docs index"
13280
13369
  });
13281
13370
  }
13371
+ const { ignored } = coverage.reproducibility;
13372
+ if (ignored.length > 0) {
13373
+ const sample = ignored.slice(0, 5).map((entry) => `${entry.path} (${entry.rule})`);
13374
+ issues.push({
13375
+ severity: "warn",
13376
+ code: "index-not-reproducible",
13377
+ message: `${ignored.length} indexed path(s) are ignored by Git, so a clean checkout builds a different index: ${sample.join(", ")}${ignored.length > sample.length ? `, and ${ignored.length - sample.length} more` : ""}.`,
13378
+ action: "edit doc-bridge.config.json # add the generated paths to safety.exclude"
13379
+ });
13380
+ }
13282
13381
  for (const id of coverage.packages.missingAgentDoc) {
13283
13382
  issues.push({
13284
13383
  severity: "warn",
@@ -13416,7 +13515,12 @@ var runDoctor = (root, config) => {
13416
13515
  message: freshnessMessage,
13417
13516
  hasIndex
13418
13517
  },
13419
- gates
13518
+ gates,
13519
+ reproducibility: checkIndexReproducibility(
13520
+ root,
13521
+ config.index?.outFile ?? ".doc-bridge/index.json",
13522
+ index.knowledge.map((entry) => entry.path)
13523
+ )
13420
13524
  };
13421
13525
  const issues = buildIssues(coverage);
13422
13526
  const score = computeScore(coverage);
@@ -13613,7 +13717,7 @@ var evaluateQualityScorecard = (input) => {
13613
13717
  };
13614
13718
 
13615
13719
  // src/memory/github-pr.ts
13616
- import { execFileSync as execFileSync2, spawnSync } from "child_process";
13720
+ import { execFileSync as execFileSync3, spawnSync } from "child_process";
13617
13721
  import { existsSync as existsSync21, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
13618
13722
  import { join as join19 } from "path";
13619
13723
  var run = (cmd, args, cwd) => {
@@ -13740,7 +13844,7 @@ ${auth.out}`
13740
13844
  ];
13741
13845
  let prUrl = "";
13742
13846
  try {
13743
- prUrl = execFileSync2("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
13847
+ prUrl = execFileSync3("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
13744
13848
  } catch (error) {
13745
13849
  const message = error instanceof Error ? error.message : String(error);
13746
13850
  return {
@@ -17614,6 +17718,7 @@ export {
17614
17718
  canonicality,
17615
17719
  centrality,
17616
17720
  changeDigestView,
17721
+ checkIndexReproducibility,
17617
17722
  checkPublicParity,
17618
17723
  checkStudyExpectations,
17619
17724
  chunksFromMarkdown,
@@ -17882,6 +17987,7 @@ export {
17882
17987
  selectTaskExecutions,
17883
17988
  sha256NormalizedV1,
17884
17989
  singularizeSearchToken,
17990
+ snapshotObservationHash,
17885
17991
  startInkChat,
17886
17992
  startMcpStdioServer,
17887
17993
  studyRetrievalSuite,