@event4u/agent-config 17.2.1 → 17.4.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.
Files changed (225) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/.claude-plugin/skills/install-agent-config/SKILL.md +9 -9
  5. package/CHANGELOG.md +167 -193
  6. package/CREDITS.md +2 -2
  7. package/README.md +1 -1
  8. package/dist/agent-src/skills/code-intelligence/SKILL.md +16 -12
  9. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  10. package/dist/agent-src/templates/scripts/check_memory.ts +1 -1
  11. package/dist/cli/commands/recordTriggerEval.js +1 -1
  12. package/dist/cli/registry.js +2 -2
  13. package/dist/cli-delegate/{chunk-KR5ZIFPO.js → chunk-5AYJGUA3.js} +1 -3
  14. package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
  15. package/dist/cli-delegate/chunk-E7ACTJTJ.js +6792 -0
  16. package/dist/cli-delegate/{chunk-VYZGJLCA.js → chunk-RKNT4LIK.js} +22 -1286
  17. package/dist/cli-delegate/{chunk-BZJ4Y3CI.js → chunk-WY5ZZPGL.js} +2 -2
  18. package/dist/cli-delegate/{chunk-CMZEKSNN.js → chunk-X6LZ7W5A.js} +73 -10
  19. package/dist/cli-delegate/cmd_conformance.js +190 -192
  20. package/dist/cli-delegate/cmd_doctor.js +5 -5
  21. package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
  22. package/dist/cli-delegate/cmd_explain.js +1 -1
  23. package/dist/cli-delegate/cmd_export.js +2 -2
  24. package/dist/cli-delegate/cmd_handoff.js +2 -2
  25. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  26. package/dist/cli-delegate/cmd_prune.js +2 -2
  27. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  28. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  29. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  30. package/dist/cli-delegate/cmd_settings_set.js +8 -4
  31. package/dist/cli-delegate/cmd_sync.js +2 -2
  32. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  33. package/dist/cli-delegate/cmd_update.js +24 -13
  34. package/dist/cli-delegate/cmd_upgrade.js +2 -2
  35. package/dist/cli-delegate/cmd_validate.js +2 -2
  36. package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
  37. package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
  38. package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
  39. package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
  40. package/dist/discovery/deprecation-report.md +1 -1
  41. package/dist/discovery/discovery-manifest.json +3 -3
  42. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  43. package/dist/discovery/discovery-manifest.summary.md +1 -1
  44. package/dist/discovery/dormancy-report.md +1 -1
  45. package/dist/discovery/orphan-report.md +1 -1
  46. package/dist/discovery/packs.json +2 -2
  47. package/dist/discovery/trust-report.md +1 -1
  48. package/dist/discovery/workspaces.json +2 -2
  49. package/dist/hooks/code_graph_refresh.chunk.cjs +443 -0
  50. package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -0
  51. package/dist/hooks/dispatch.js +1001 -938
  52. package/dist/hooks/dispatch.sha256 +1 -1
  53. package/dist/hooks/egress_engine.chunk.cjs +57 -7
  54. package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
  55. package/dist/hooks/update_notice.chunk.cjs +914 -0
  56. package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
  57. package/dist/install/install.mjs +6974 -6037
  58. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -0
  59. package/dist/mcp/registry-manifest.json +3 -2
  60. package/dist/mcp/server.json +13 -4
  61. package/dist/mcp/server.mjs +71202 -63282
  62. package/dist/scripts/_lib/egress_rules.js +8 -2
  63. package/dist/scripts/condense.js +18 -15
  64. package/dist/server/routes/discovery.js +1 -1
  65. package/dist/server/routes/install.js +1 -1
  66. package/dist/server/routes/ping.js +1 -1
  67. package/dist/server/routes/schema.js +4 -4
  68. package/dist/server/routes/settings.js +2 -6
  69. package/dist/server/routes/wizard.js +6 -6
  70. package/dist/server/schemas/settings.js +42 -42
  71. package/dist/shared/settingsSurface.js +1 -1
  72. package/dist/shared/userMd/schema.js +1 -1
  73. package/dist/shared/zodJsonSchema.js +47 -0
  74. package/dist/ui/assets/index-B0bcIgi0.js +77 -0
  75. package/dist/ui/index.html +1 -1
  76. package/package.json +6 -3
  77. package/src/agent-src/templates/scripts/check_memory.ts +1 -1
  78. package/src/config/ci-local-parity.yml +11 -0
  79. package/src/config/continuity-surface.json +9 -2
  80. package/src/config/evaluator-budgets.json +9 -8
  81. package/src/config/gate-violation-baselines.json +3 -3
  82. package/src/config/gitignore-block.txt +6 -5
  83. package/src/config/hook-bundle-budget.json +46 -6
  84. package/src/config/pack-size-budget.json +12 -1
  85. package/src/config/packed-binary-manifest.json +18 -0
  86. package/src/config/sigstore/npm_keys.json +50 -0
  87. package/src/config/sigstore/trust-material.json +9 -0
  88. package/src/config/sigstore/trusted_root.json +126 -0
  89. package/src/config/skill-body-budget.json +7 -1
  90. package/src/scripts/_cli/cmd_settings_set.ts +3 -2
  91. package/src/scripts/_cli/cmd_update.ts +48 -20
  92. package/src/scripts/_cli/cmd_upgrade.ts +7 -1
  93. package/src/scripts/_cli/upgrade_unattended.ts +90 -76
  94. package/src/scripts/_dispatch.bash +4 -3
  95. package/src/scripts/_lib/confusables.ts +0 -19
  96. package/src/scripts/_lib/egress_normalize.ts +53 -23
  97. package/src/scripts/_lib/egress_rules.ts +8 -2
  98. package/src/scripts/_lib/egress_text.ts +5 -3
  99. package/src/scripts/_lib/egress_token.ts +7 -5
  100. package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
  101. package/src/scripts/_lib/git_common_dir.ts +10 -0
  102. package/src/scripts/_lib/graph_feeder_record.ts +3 -2
  103. package/src/scripts/_lib/install_reach_checks.ts +32 -2
  104. package/src/scripts/_lib/lineage_backfill.ts +136 -0
  105. package/src/scripts/_lib/lineage_ledger.ts +211 -0
  106. package/src/scripts/_lib/lineage_map.ts +55 -0
  107. package/src/scripts/_lib/lineage_query.ts +140 -0
  108. package/src/scripts/_lib/lineage_seed.ts +116 -0
  109. package/src/scripts/_lib/lineage_watch.ts +249 -0
  110. package/src/scripts/_lib/link_crypto.ts +8 -4
  111. package/src/scripts/_lib/listing_manifests.ts +85 -0
  112. package/src/scripts/_lib/neighbour_census.ts +1 -0
  113. package/src/scripts/_lib/production_reach.ts +3 -3
  114. package/src/scripts/_lib/project_registry.ts +227 -0
  115. package/src/scripts/_lib/provenance_verify.ts +14 -242
  116. package/src/scripts/_lib/release_settings_surface.ts +2 -6
  117. package/src/scripts/_lib/sigstore_provenance.ts +177 -0
  118. package/src/scripts/_lib/source_id.ts +48 -0
  119. package/src/scripts/_lib/source_shape.ts +4 -0
  120. package/src/scripts/_lib/unattended_gates.ts +6 -1
  121. package/src/scripts/adoption_status.ts +231 -27
  122. package/src/scripts/advisories.ts +26 -10
  123. package/src/scripts/ai_council/council_egress.ts +5 -6
  124. package/src/scripts/bench_graph_feeder_latency.ts +10 -3
  125. package/src/scripts/build_mcp_registry_manifest.ts +23 -12
  126. package/src/scripts/check_dependency_floors.ts +14 -0
  127. package/src/scripts/check_egress_mutations.ts +1 -0
  128. package/src/scripts/check_release_manifest_content.ts +17 -2
  129. package/src/scripts/check_release_pr_shape.ts +5 -0
  130. package/src/scripts/code_graph/build.ts +145 -45
  131. package/src/scripts/code_graph/cli.ts +382 -35
  132. package/src/scripts/code_graph/conventions_default.ts +247 -0
  133. package/src/scripts/code_graph/detect.ts +5 -5
  134. package/src/scripts/code_graph/estate_cli.ts +135 -0
  135. package/src/scripts/code_graph/estate_index.ts +394 -0
  136. package/src/scripts/code_graph/estate_manifests.ts +223 -0
  137. package/src/scripts/code_graph/estate_map.ts +141 -0
  138. package/src/scripts/code_graph/extract.ts +8 -2
  139. package/src/scripts/code_graph/extract_more.ts +241 -2
  140. package/src/scripts/code_graph/graph_budget.ts +158 -0
  141. package/src/scripts/code_graph/graph_home.ts +80 -0
  142. package/src/scripts/code_graph/lineage_cli.ts +315 -0
  143. package/src/scripts/code_graph/loader.ts +10 -2
  144. package/src/scripts/code_graph/map.ts +195 -53
  145. package/src/scripts/code_graph/map_model.ts +113 -20
  146. package/src/scripts/code_graph/map_verify.ts +80 -5
  147. package/src/scripts/code_graph/map_viewer/viewer.css +18 -0
  148. package/src/scripts/code_graph/map_viewer/viewer.js +146 -15
  149. package/src/scripts/code_graph/passes.ts +173 -0
  150. package/src/scripts/code_graph/project_ref.ts +25 -0
  151. package/src/scripts/code_graph/projects_cli.ts +254 -0
  152. package/src/scripts/code_graph/providers/angular.ts +55 -0
  153. package/src/scripts/code_graph/providers/aspnetcore.ts +182 -0
  154. package/src/scripts/code_graph/providers/catalog.ts +299 -0
  155. package/src/scripts/code_graph/providers/django.ts +206 -0
  156. package/src/scripts/code_graph/providers/express.ts +25 -0
  157. package/src/scripts/code_graph/providers/fastapi.ts +49 -0
  158. package/src/scripts/code_graph/providers/flask.ts +99 -0
  159. package/src/scripts/code_graph/providers/go_http.ts +121 -0
  160. package/src/scripts/code_graph/providers/index.ts +103 -0
  161. package/src/scripts/code_graph/{laravel_overlay.ts → providers/laravel.ts} +113 -7
  162. package/src/scripts/code_graph/providers/nestjs.ts +34 -0
  163. package/src/scripts/code_graph/providers/nextjs.ts +91 -0
  164. package/src/scripts/code_graph/providers/nuxt.ts +55 -0
  165. package/src/scripts/code_graph/providers/rails.ts +204 -0
  166. package/src/scripts/code_graph/providers/react-router.ts +58 -0
  167. package/src/scripts/code_graph/providers/shared/detect.ts +78 -0
  168. package/src/scripts/code_graph/providers/shared/extractors.ts +249 -0
  169. package/src/scripts/code_graph/providers/shared/js.ts +252 -0
  170. package/src/scripts/code_graph/providers/shared/kit.ts +210 -0
  171. package/src/scripts/code_graph/providers/shared/php.ts +64 -0
  172. package/src/scripts/code_graph/providers/shared/python.ts +223 -0
  173. package/src/scripts/code_graph/providers/spring.ts +175 -0
  174. package/src/scripts/code_graph/providers/sqlalchemy.ts +62 -0
  175. package/src/scripts/code_graph/providers/sveltekit.ts +59 -0
  176. package/src/scripts/code_graph/providers/symfony.ts +142 -0
  177. package/src/scripts/code_graph/providers/typeorm.ts +58 -0
  178. package/src/scripts/code_graph/providers/types.ts +56 -0
  179. package/src/scripts/code_graph/providers/vue-router.ts +21 -0
  180. package/src/scripts/code_graph/providers/wordpress.ts +128 -0
  181. package/src/scripts/code_graph/query.ts +9 -9
  182. package/src/scripts/code_graph/refresh_cli.ts +81 -0
  183. package/src/scripts/code_graph/refresh_lock.ts +197 -0
  184. package/src/scripts/code_graph/repo_identity.ts +169 -0
  185. package/src/scripts/code_graph/resolution_tiers.ts +19 -7
  186. package/src/scripts/code_graph/scip_reader.ts +2 -2
  187. package/src/scripts/code_graph/sqlite_store.ts +31 -28
  188. package/src/scripts/code_graph/types.ts +79 -41
  189. package/src/scripts/code_graph/validate.ts +6 -8
  190. package/src/scripts/condense.ts +15 -15
  191. package/src/scripts/generate_settings_reference.ts +2 -6
  192. package/src/scripts/hook_chunks.mjs +11 -0
  193. package/src/scripts/hook_manifest.json +1 -1
  194. package/src/scripts/hook_manifest.yaml +43 -23
  195. package/src/scripts/hooks/code_graph_refresh_hook.ts +163 -0
  196. package/src/scripts/hooks/dispatch_hook.ts +6 -7
  197. package/src/scripts/hooks/host_semantics.ts +72 -0
  198. package/src/scripts/hooks/lazy_concern.ts +8 -0
  199. package/src/scripts/hooks/user_notice.ts +105 -0
  200. package/src/scripts/install-hooks.sh +54 -33
  201. package/src/scripts/install.ts +2 -6
  202. package/src/scripts/lint_harvest_provenance.ts +137 -11
  203. package/src/scripts/lint_hook_manifest.ts +2 -0
  204. package/src/scripts/lint_marketplace.ts +33 -0
  205. package/src/scripts/lint_mcp_registry_manifest.ts +36 -0
  206. package/src/scripts/mcp_server/consumer_tool_catalog.json +70 -15
  207. package/src/scripts/mcp_server/graph_tools.ts +165 -41
  208. package/src/scripts/network_sources.ts +24 -6
  209. package/src/scripts/release.ts +2 -5
  210. package/src/scripts/release_drill.ts +4 -1
  211. package/src/scripts/release_env.ts +17 -0
  212. package/src/scripts/release_mix_taxonomy.json +3 -0
  213. package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
  214. package/src/scripts/update_notice_hook.ts +75 -7
  215. package/src/server/schemas/settings.ts +42 -42
  216. package/src/shared/settingsSurface.ts +1 -1
  217. package/src/shared/userMd/schema.ts +1 -1
  218. package/src/shared/zodJsonSchema.ts +41 -0
  219. package/src/vendor/grammars/README.md +8 -6
  220. package/src/vendor/grammars/tree-sitter-c_sharp.wasm +0 -0
  221. package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
  222. package/dist/cli-delegate/chunk-XIAPY44N.js +0 -4587
  223. package/dist/ui/assets/index-w70Ado09.js +0 -23
  224. /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
  225. /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
@@ -42,7 +42,14 @@ import * as fs from 'node:fs';
42
42
  import * as path from 'node:path';
43
43
 
44
44
  import { changedFiles } from '../code_graph/cli.js';
45
- import { detectSources, graphState, NATIVE_CACHE_REL, pickSource } from '../code_graph/detect.js';
45
+ import { detectSources, graphState, pickSource } from '../code_graph/detect.js';
46
+ import { graphHome } from '../code_graph/graph_home.js';
47
+ import { advisoryImpact, whereUsed } from '../code_graph/estate_cli.js';
48
+ import { openEstate } from '../code_graph/estate_index.js';
49
+ import { touchHome } from '../code_graph/graph_budget.js';
50
+ import { resolveProjectRef } from '../code_graph/project_ref.js';
51
+ import { repoIdentity } from '../code_graph/repo_identity.js';
52
+ import { readRegistry, repoIdOf } from '../_lib/project_registry.js';
46
53
  import { resolvePath } from './path_util.js';
47
54
  import { isTestFile } from '../code_graph/build.js';
48
55
  import {
@@ -62,11 +69,52 @@ import {
62
69
  testsFor,
63
70
  untested,
64
71
  } from '../code_graph/verbs.js';
65
- import { areaReport, readMap } from '../code_graph/map.js';
72
+ import { areaReport, graphGeneration, readMap } from '../code_graph/map.js';
66
73
  import type { BuiltinTool } from './tools.js';
67
74
 
68
- /** The `no graph here` answer, in one place so all five agree on its shape. */
75
+ /**
76
+ * Registered projects whose graph exists, for a root that has none (5.2). Read
77
+ * from the local registry only; the graph files themselves are not opened.
78
+ *
79
+ * Any MCP caller can reach this answer, so it never names another repository's
80
+ * path: only another checkout of the server root's own repository is listed
81
+ * with its root, and other projects appear — by id and name — only while the
82
+ * owner has switched the estate index on (O1).
83
+ */
84
+ function projectsWithGraph(root: string): Array<{ id: string; name: string; root?: string }> {
85
+ const reg = readRegistry();
86
+ const self = repoIdentity(root).repoId;
87
+ let real: string;
88
+ try {
89
+ real = fs.realpathSync(root);
90
+ } catch {
91
+ real = path.resolve(root);
92
+ }
93
+ const out: Array<{ id: string; name: string; root?: string }> = [];
94
+ for (const [id, e] of Object.entries(reg.projects)) {
95
+ const same = repoIdOf(id) === self;
96
+ if (!same && !reg.index) continue;
97
+ if (e.root === real || !fs.existsSync(e.root) || !fs.existsSync(graphHome(e.root).cachePath)) continue;
98
+ out.push(same ? { id, name: path.basename(e.root), root: e.root } : { id, name: path.basename(e.root) });
99
+ if (out.length === 20) break;
100
+ }
101
+ return out;
102
+ }
103
+
104
+ /** The `no graph here` answer, in one place so all the tools agree on its shape. */
69
105
  function noGraph(root: string): Record<string, unknown> {
106
+ const registered = projectsWithGraph(root);
107
+ if (registered.length) {
108
+ return {
109
+ status: 'unavailable',
110
+ staleness: 'absent',
111
+ error:
112
+ 'no code-graph source under this root — pass `project` to ask a registered project below, ' +
113
+ 'or run `agent-config code-graph build` here',
114
+ root,
115
+ registered_projects: registered,
116
+ };
117
+ }
70
118
  return {
71
119
  status: 'unavailable',
72
120
  staleness: 'absent',
@@ -88,9 +136,10 @@ function withGraph<T extends Record<string, unknown>>(
88
136
  root: string,
89
137
  fn: (g: LoadedGraph) => T,
90
138
  ): Record<string, unknown> {
91
- const picked = pickSource(detectSources(root, path.join(root, NATIVE_CACHE_REL)));
139
+ const picked = pickSource(detectSources(root, graphHome(root).cachePath));
92
140
  if (!picked || picked.kind === 'scip') return noGraph(root);
93
- const g = loadGraph(picked.path, `${picked.kind}:${path.relative(root, picked.path)}`);
141
+ touchHome(root);
142
+ const g = loadGraph(picked.path, `${picked.kind}:${shown(root, picked.path)}`);
94
143
  try {
95
144
  const out = fn(g);
96
145
  // `status` is DERIVED from the answer, never prefixed onto it. An
@@ -107,6 +156,12 @@ function withGraph<T extends Record<string, unknown>>(
107
156
  }
108
157
  }
109
158
 
159
+ /** Relative inside the root, absolute for a graph home outside it (ADR-299). */
160
+ function shown(root: string, p: string): string {
161
+ const rel = path.relative(root, p);
162
+ return rel.startsWith('..') || path.isAbsolute(rel) ? p : rel;
163
+ }
164
+
110
165
  function str(args: Record<string, unknown>, key: string): string {
111
166
  const v = args[key];
112
167
  return typeof v === 'string' ? v.trim() : '';
@@ -134,6 +189,7 @@ const RELATIONS = [
134
189
  'migrates',
135
190
  'persists',
136
191
  'relates',
192
+ 'injects',
137
193
  'documents',
138
194
  ] as const;
139
195
 
@@ -142,14 +198,49 @@ function num(args: Record<string, unknown>, key: string, dflt: number): number {
142
198
  return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : dflt;
143
199
  }
144
200
 
145
- export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
201
+ /**
202
+ * Every graph tool takes an optional `project` (5.1): a registered repo id,
203
+ * name or path, resolved through the registry and its graph home. Without it
204
+ * the tool answers about the server root, exactly as before.
205
+ */
206
+ function withProjectParam(tools: Record<string, BuiltinTool>): Record<string, BuiltinTool> {
207
+ const out: Record<string, BuiltinTool> = {};
208
+ for (const [name, t] of Object.entries(tools)) {
209
+ // The estate tool answers across every project already.
210
+ if (name === 'graph_estate') {
211
+ out[name] = t;
212
+ continue;
213
+ }
214
+ const schema = t.input_schema as { properties?: Record<string, unknown> };
215
+ out[name] = {
216
+ ...t,
217
+ input_schema: {
218
+ ...t.input_schema,
219
+ properties: {
220
+ ...(schema.properties ?? {}),
221
+ project: { type: 'string' },
222
+ },
223
+ },
224
+ handler: async (args, root) => {
225
+ const ref = str(args, 'project');
226
+ if (ref === '') return t.handler(args, root);
227
+ const r = resolveProjectRef(ref);
228
+ if ('error' in r) return { status: 'error', error: r.error, candidates: r.candidates };
229
+ return t.handler(args, r.root);
230
+ },
231
+ };
232
+ }
233
+ return out;
234
+ }
235
+
236
+ export const GRAPH_TOOLS: Record<string, BuiltinTool> = withProjectParam({
146
237
  graph_impact: {
147
238
  name: 'graph_impact',
148
239
  side_effect: 'shell',
149
240
  description:
150
241
  'What a diff can break: callers, dependents and test files reaching the ' +
151
- 'changed symbols. Walks only stated-fact edges, never a same-name guess, ' +
152
- 'and reports the guesses refused. Runs `git diff` for the rev.',
242
+ 'changed symbols, over stated-fact edges only (refused guesses reported). ' +
243
+ 'Runs `git diff` for the rev.',
153
244
  input_schema: {
154
245
  type: 'object',
155
246
  required: ['diff'],
@@ -169,7 +260,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
169
260
  if (rev === '') return { status: 'error', error: 'diff must be a non-empty git rev' };
170
261
  const files = changedFiles(root, rev);
171
262
  if (files === null) return { status: 'error', error: `cannot resolve rev '${rev}' in ${root}` };
172
- const state = graphState(root);
263
+ const state = graphState(root, graphHome(root).cachePath);
173
264
  return withGraph(root, (g) =>
174
265
  args['untested_only'] === true
175
266
  ? { ...untested(g, files, state), changed_files: files.length }
@@ -182,20 +273,20 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
182
273
  name: 'graph_tests_for',
183
274
  side_effect: 'ro',
184
275
  description:
185
- 'Which test files exercise a symbol, derived from a test file importing ' +
186
- 'it or its module. Empty means no test imports it — a finding, not an error.',
276
+ 'Test files that exercise a symbol: a test file imports it or its module. ' +
277
+ 'Empty means none does — a finding, not an error.',
187
278
  input_schema: {
188
279
  type: 'object',
189
280
  required: ['symbol'],
190
281
  properties: {
191
- symbol: { type: 'string', description: 'Node id `<relpath>#<sym>`, label, or free text.' },
282
+ symbol: { type: 'string', description: 'Node id, label, or free text.' },
192
283
  },
193
284
  additionalProperties: false,
194
285
  },
195
286
  handler: async (args, root) => {
196
287
  const symbol = str(args, 'symbol');
197
288
  if (symbol === '') return { status: 'error', error: 'symbol must be a non-empty string' };
198
- const state = graphState(root);
289
+ const state = graphState(root, graphHome(root).cachePath);
199
290
  return withGraph(root, (g) => ({ ...testsFor(g, symbol, state) }));
200
291
  },
201
292
  },
@@ -204,16 +295,15 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
204
295
  name: 'graph_dead',
205
296
  side_effect: 'ro',
206
297
  description:
207
- 'Symbols with no accepted reference that no declared entry point names. ' +
208
- 'REFUSES (status `refused`) while a source is unreadable: with no ' +
209
- 'exportedness recorded, an exported-but-unimported symbol is ' +
210
- 'indistinguishable from a dead one. Never a deletion recommendation.',
298
+ 'Symbols no accepted reference and no declared entry point names. REFUSES ' +
299
+ '(status `refused`) while a source is unreadable: without recorded exportedness ' +
300
+ 'an exported-but-unimported symbol looks dead. Never a deletion recommendation.',
211
301
  input_schema: {
212
302
  type: 'object',
213
303
  properties: {
214
304
  entry_points: {
215
305
  type: 'string',
216
- description: 'Path under this root to a newline-separated id/label list; `#` comments ok.',
306
+ description: 'Path under this root: newline-separated ids or labels, `#` comments ok.',
217
307
  },
218
308
  accept_missing_exports: {
219
309
  type: 'boolean',
@@ -224,7 +314,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
224
314
  additionalProperties: false,
225
315
  },
226
316
  handler: async (args, root) => {
227
- const state = graphState(root);
317
+ const state = graphState(root, graphHome(root).cachePath);
228
318
  const listArg = str(args, 'entry_points');
229
319
  let sources = repoEntryPoints(root);
230
320
  if (listArg !== '') {
@@ -280,22 +370,21 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
280
370
  name: 'graph_query',
281
371
  side_effect: 'ro',
282
372
  description:
283
- "A symbol's direct outgoing relations — calls, imports, uses, inherits — " +
284
- 'each line carrying its confidence and resolution mechanism. For "what ' +
285
- 'does this touch" before reading a file.',
373
+ "A symbol's direct outgoing relations (calls, imports, uses, inherits), each " +
374
+ 'with its confidence and resolution mechanism: "what does this touch" before reading a file.',
286
375
  input_schema: {
287
376
  type: 'object',
288
377
  required: ['symbol'],
289
378
  properties: {
290
379
  symbol: { type: 'string', description: 'Node id, label, or free text.' },
291
- budget: { type: 'integer', minimum: 1, default: 1500, description: 'Token budget for lines.' },
380
+ budget: { type: 'integer', minimum: 1, default: 1500 },
292
381
  },
293
382
  additionalProperties: false,
294
383
  },
295
384
  handler: async (args, root) => {
296
385
  const symbol = str(args, 'symbol');
297
386
  if (symbol === '') return { status: 'error', error: 'symbol must be a non-empty string' };
298
- const staleness = graphState(root);
387
+ const staleness = graphState(root, graphHome(root).cachePath);
299
388
  return withGraph(root, (g) => ({
300
389
  staleness,
301
390
  ...graphQueryVerb(g, symbol, num(args, 'budget', 1500)),
@@ -307,10 +396,9 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
307
396
  name: 'graph_node',
308
397
  side_effect: 'ro',
309
398
  description:
310
- 'One symbol in full: where it is declared, what reaches it, what it ' +
311
- 'reaches, and how connected it is. Resolves EXACTLY (node id, then ' +
312
- 'label) and refuses anything it cannot — it never scores free text, ' +
313
- 'so an answer is always about the node you named.',
399
+ 'One symbol in full: where it is declared, what reaches it, what it reaches, ' +
400
+ 'how connected it is. Resolves EXACTLY (node id, then label) and refuses ' +
401
+ 'anything else — never scores free text.',
314
402
  input_schema: {
315
403
  type: 'object',
316
404
  required: ['id'],
@@ -320,13 +408,13 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
320
408
  type: 'string',
321
409
  enum: ['in', 'out', 'both'],
322
410
  default: 'both',
323
- description: 'Which edges to walk. `in` = who reaches it, `out` = what it reaches.',
411
+ description: '`in` = who reaches it, `out` = what it reaches.',
324
412
  },
325
413
  depth: { type: 'integer', minimum: 1, maximum: 3, default: 1, description: 'Hop limit.' },
326
414
  relation: {
327
415
  type: 'string',
328
416
  enum: RELATIONS,
329
- description: 'Restrict to one relation. Omit for all of them.',
417
+ description: 'One relation only; omit for all.',
330
418
  },
331
419
  },
332
420
  additionalProperties: false,
@@ -334,7 +422,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
334
422
  handler: async (args, root) => {
335
423
  const id = str(args, 'id');
336
424
  if (id === '') return { status: 'error', error: 'id must be a non-empty string' };
337
- const staleness = graphState(root);
425
+ const staleness = graphState(root, graphHome(root).cachePath);
338
426
  const opts: NodeOptions = {};
339
427
  const dir = str(args, 'direction');
340
428
  if (dir === 'in' || dir === 'out' || dir === 'both') opts.direction = dir;
@@ -352,16 +440,15 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
352
440
  name: 'graph_path',
353
441
  side_effect: 'ro',
354
442
  description:
355
- 'The shortest relation chain between two symbols, direction preserved. ' +
356
- 'Answers "how does A reach B" without reading the files between. ' +
357
- '`(no path found)` when unconnected is an answer.',
443
+ 'The shortest relation chain between two symbols, direction kept: how A reaches ' +
444
+ 'B without reading the files between. `(no path found)` is an answer.',
358
445
  input_schema: {
359
446
  type: 'object',
360
447
  required: ['from', 'to'],
361
448
  properties: {
362
449
  from: { type: 'string', description: 'Start node id, label, or free text.' },
363
450
  to: { type: 'string', description: 'Goal node id, label, or free text.' },
364
- budget: { type: 'integer', minimum: 1, default: 1500, description: 'Token budget for lines.' },
451
+ budget: { type: 'integer', minimum: 1, default: 1500 },
365
452
  },
366
453
  additionalProperties: false,
367
454
  },
@@ -369,7 +456,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
369
456
  const from = str(args, 'from');
370
457
  const to = str(args, 'to');
371
458
  if (from === '' || to === '') return { status: 'error', error: 'from and to must be non-empty strings' };
372
- const staleness = graphState(root);
459
+ const staleness = graphState(root, graphHome(root).cachePath);
373
460
  return withGraph(root, (g) => ({
374
461
  staleness,
375
462
  ...graphPathVerb(g, from, to, num(args, 'budget', 1500)),
@@ -382,7 +469,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
382
469
  description: 'Is there a code map (agents/map/)? Its freshness, counts and MAP.md path.',
383
470
  input_schema: { type: 'object', properties: {}, additionalProperties: false },
384
471
  handler: async (_args, root) => {
385
- const staleness = graphState(root);
472
+ const staleness = graphState(root, graphHome(root).cachePath);
386
473
  const map = readMap(root);
387
474
  if (!map) {
388
475
  return {
@@ -393,20 +480,25 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
393
480
  };
394
481
  }
395
482
  const m = map.model;
483
+ const home = graphHome(root);
484
+ const mapRel = home.kind === 'project' ? 'agents/map' : home.mapDir;
396
485
  return {
397
486
  status: 'ok',
398
487
  staleness,
399
488
  map: 'present',
400
489
  drawn_at_commit: map.meta.commit,
401
490
  graph_state_when_drawn: map.meta.state,
491
+ // Same generation → the map shows exactly the graph the queries read.
492
+ map_matches_graph: map.meta.graph_generation !== undefined && map.meta.graph_generation === graphGeneration(home.cachePath),
402
493
  files: m.files.length,
403
494
  areas: m.areas.length,
404
495
  trusted_links: m.analyses.trustedEdges,
405
496
  guess_share: m.analyses.guessShare,
406
497
  request_paths: m.analyses.requestPathsTotal,
498
+ providers: (m.providers ?? []).map((p) => `${p.id} (${p.status})`),
407
499
  largest_areas: m.areas.slice(0, 5).map((a) => `${a.id} ${a.name} (${a.files.length})`),
408
- map_md: 'agents/map/MAP.md',
409
- index_html: 'agents/map/index.html',
500
+ map_md: `${mapRel}/MAP.md`,
501
+ index_html: `${mapRel}/index.html`,
410
502
  };
411
503
  },
412
504
  },
@@ -427,9 +519,41 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
427
519
  if (q === '') return { status: 'error', error: 'area must be a non-empty string' };
428
520
  const map = readMap(root);
429
521
  if (!map) return { status: 'unavailable', error: 'no map — run `agent-config code-graph map`' };
430
- const r = areaReport(map.model, q);
522
+ const home = graphHome(root);
523
+ const r = areaReport(map.model, q, home.kind === 'project' ? undefined : home.mapDir);
431
524
  if ('refusal' in r) return { status: 'refused', refusal: r.refusal, candidates: r.candidates };
432
525
  return { status: 'ok', drawn_at_commit: map.meta.commit, ...r };
433
526
  },
434
527
  },
435
- };
528
+ graph_estate: {
529
+ name: 'graph_estate',
530
+ side_effect: 'ro',
531
+ description:
532
+ 'Estate index across registered projects: package users, where a name is used, advisory ' +
533
+ 'reach. Other graph tools take `project`.',
534
+ input_schema: {
535
+ type: 'object',
536
+ required: ['query'],
537
+ properties: {
538
+ query: { type: 'string', enum: ['where-used', 'projects-using', 'advisory-impact'] },
539
+ name: { type: 'string' },
540
+ },
541
+ additionalProperties: false,
542
+ },
543
+ handler: async (args) => {
544
+ const q = str(args, 'query');
545
+ const name = str(args, 'name');
546
+ if (q !== 'advisory-impact' && name === '') return { status: 'error', error: `${q || 'query'} needs a name` };
547
+ const db = openEstate();
548
+ if (!db) return { status: 'unavailable', error: 'no estate index — `agent-config code-graph projects index on`, then refresh each project' };
549
+ try {
550
+ if (q === 'projects-using') return { status: 'ok', projects: db.projectsUsing(name) };
551
+ if (q === 'where-used') return { status: 'ok', ...whereUsed(db, name) };
552
+ if (q === 'advisory-impact') return { status: 'ok', impact: advisoryImpact(db, name === '' ? null : name) };
553
+ return { status: 'error', error: `unknown query: ${q}` };
554
+ } finally {
555
+ db.close();
556
+ }
557
+ },
558
+ },
559
+ });
@@ -24,6 +24,7 @@ import { isIP } from 'node:net';
24
24
  import * as path from 'node:path';
25
25
 
26
26
  import { encrypt } from './_lib/link_crypto.js';
27
+ import { sourceId } from './_lib/source_id.js';
27
28
  import { sanitize_text } from './_lib/retrieval_sanitize.js';
28
29
  import { SourceError, resolveSource, type SourceRef } from './source_adapters.js';
29
30
 
@@ -207,26 +208,43 @@ export function quotedAnchor(text: string, needle: string): { line: number; quot
207
208
  return { line: i + 1, quote: words.slice(start, start + MAX_QUOTE_WORDS).join(' ') };
208
209
  }
209
210
 
210
- /** Codes source identities S1, S2, …; the identities leave only as ENC1 tokens in the round's intake.md. */
211
+ /**
212
+ * Codes source identities; the identities leave only as ENC1 tokens in the round's intake.md.
213
+ * With an id key the codes are stable `src_…` ids (`_lib/source_id.ts`), equal across rounds;
214
+ * without one they are `S1, S2, …`, meaningful inside this round only.
215
+ */
211
216
  export class SourceCodebook {
212
217
  private readonly ids: string[] = [];
218
+ private readonly codes: string[] = [];
213
219
  private readonly tokens: string[] = [];
220
+ private readonly idKey: string | null;
214
221
 
215
- constructor(private readonly key: string) {}
222
+ constructor(
223
+ private readonly key: string,
224
+ opts: { idKey?: string | null } = {},
225
+ ) {
226
+ this.idKey = opts.idKey ? opts.idKey : null;
227
+ }
216
228
 
217
229
  code(identity: string): string {
218
- let i = this.ids.indexOf(identity);
230
+ const stable = this.idKey === null ? null : sourceId(identity, this.idKey);
231
+ let i = stable === null ? this.ids.indexOf(identity) : this.codes.indexOf(stable);
219
232
  if (i === -1) {
220
233
  this.ids.push(identity);
234
+ this.codes.push(stable ?? `S${String(this.ids.length)}`);
221
235
  this.tokens.push(encrypt(identity, this.key));
222
236
  i = this.ids.length - 1;
223
237
  }
224
- return `S${String(i + 1)}`;
238
+ return this.codes[i] as string;
225
239
  }
226
240
 
227
241
  intakeMarkdown(): string {
228
- const rows = this.tokens.map((token, i) => `- S${String(i + 1)}: ${token}`);
229
- return ['# Sources', '', 'Identities are encrypted (`link_crypto`); tracked output names only the codes.', '', ...rows, ''].join('\n');
242
+ const rows = this.tokens.map((token, i) => `- ${this.codes[i] as string}: ${token}`);
243
+ const scope =
244
+ this.idKey === null
245
+ ? 'Codes are per-round (S1, S2, …) — no id key was available, so they do not match another round.'
246
+ : 'Codes are stable source ids (src_…), equal across rounds.';
247
+ return ['# Sources', '', 'Identities are encrypted (`link_crypto`); tracked output names only the codes.', scope, '', ...rows, ''].join('\n');
230
248
  }
231
249
 
232
250
  writeIntake(roundDir: string): void {
@@ -152,8 +152,6 @@ const _HERE = fileURLToPath(import.meta.url);
152
152
 
153
153
  import {
154
154
  ArgparseExit,
155
- AUGMENT_MARKETPLACE_JSON,
156
- AUGMENT_PLUGIN_JSON,
157
155
  CHANGELOG,
158
156
  _set_changelog_reader,
159
157
  read_changelog_text,
@@ -169,7 +167,7 @@ import {
169
167
  REPO_ROOT,
170
168
  REPO_SLUG,
171
169
  SystemExitError,
172
- set_augment_manifest_version,
170
+ set_plugin_manifest_versions,
173
171
  set_marketplace_version,
174
172
  set_template_pin,
175
173
  _rstrip,
@@ -939,8 +937,7 @@ function execute(
939
937
  set_package_version(PACKAGE_JSON, plan.target, plan.commits);
940
938
  set_lockfile_version(PACKAGE_LOCK_JSON, plan.target);
941
939
  set_marketplace_version(MARKETPLACE_JSON, plan.target);
942
- set_augment_manifest_version(AUGMENT_PLUGIN_JSON, plan.target);
943
- set_augment_manifest_version(AUGMENT_MARKETPLACE_JSON, plan.target);
940
+ set_plugin_manifest_versions(plan.target);
944
941
  set_template_pin(PROJECT_TEMPLATE, plan.target);
945
942
  writeReleasedSurface(plan.target);
946
943
  prepend_changelog(CHANGELOG, plan.changelog_entry);
@@ -44,6 +44,7 @@ import { releaseHoldPreflight } from './_lib/release_holds.js';
44
44
  import { SETTINGS_SURFACE_FIXTURE } from './_lib/release_settings_surface.js';
45
45
  import { die } from './release_publication.js';
46
46
  import { DERIVED_MARKER } from './_lib/release_highlights.js';
47
+ import { listingManifestPaths } from './_lib/listing_manifests.js';
47
48
  import {
48
49
  AUGMENT_MARKETPLACE_JSON,
49
50
  AUGMENT_PLUGIN_JSON,
@@ -52,13 +53,14 @@ import {
52
53
  PACKAGE_JSON,
53
54
  PACKAGE_LOCK_JSON,
54
55
  PROJECT_TEMPLATE,
56
+ REPO_ROOT as ENV_REPO_ROOT,
55
57
  } from './release_env.js';
56
58
 
57
59
  /**
58
60
  * The files step 2 mutates on disk, diverted into memory for every scenario.
59
61
  *
60
62
  * The FakeWorld intercepts COMMANDS, not `fs` writes, and step 2's bump is
61
- * seven direct writes. Every scenario before 2026-09-03 passed `--resume` with
63
+ * ten direct writes. Every scenario before 2026-09-03 passed `--resume` with
62
64
  * `package.json` already at the target, so step 2 short-circuited and nothing
63
65
  * was ever written — the drill looked side-effect-free because no scenario had
64
66
  * reached the writing arm. The first `resume: false` scenario appended twelve
@@ -83,6 +85,7 @@ const _MUTATED_BY_STEP_2: readonly string[] = [
83
85
  MARKETPLACE_JSON,
84
86
  AUGMENT_PLUGIN_JSON,
85
87
  AUGMENT_MARKETPLACE_JSON,
88
+ ...listingManifestPaths(ENV_REPO_ROOT),
86
89
  PROJECT_TEMPLATE,
87
90
  SETTINGS_SURFACE_FIXTURE,
88
91
  ];
@@ -24,6 +24,8 @@ import * as fs from 'node:fs';
24
24
  import * as path from 'node:path';
25
25
  import { fileURLToPath } from 'node:url';
26
26
 
27
+ import { listingManifestPaths, setListingManifestVersion } from './_lib/listing_manifests.js';
28
+
27
29
  const _HERE = fileURLToPath(import.meta.url);
28
30
 
29
31
  // ---------------------------------------------------------------------------
@@ -293,6 +295,20 @@ function set_augment_manifest_version(p: string, version: string): void {
293
295
  fs.writeFileSync(p, jsonDumpsIndent(data, 2) + '\n', 'utf-8');
294
296
  }
295
297
 
298
+ /**
299
+ * Every plugin and extension manifest the release keeps at the package
300
+ * version: the two `.augment-plugin/` twins and the two listing manifests that
301
+ * also pin the version inside their `npx` arguments. One entry point so
302
+ * `release.ts`, which sits past the source-size cap, spends one line on all four.
303
+ */
304
+ function set_plugin_manifest_versions(version: string, root: string = REPO_ROOT): void {
305
+ set_augment_manifest_version(path.join(root, '.augment-plugin', 'plugin.json'), version);
306
+ set_augment_manifest_version(path.join(root, '.augment-plugin', 'marketplace.json'), version);
307
+ for (const p of listingManifestPaths(root)) {
308
+ setListingManifestVersion(p, version, (v) => jsonDumpsIndent(v, 2));
309
+ }
310
+ }
311
+
296
312
  /** Update `metadata.version`; preserve 2-space indentation + UTF-8. */
297
313
  function set_marketplace_version(p: string, version: string): void {
298
314
  const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
@@ -374,6 +390,7 @@ export {
374
390
  commaGroup,
375
391
  jsonDumpsIndent,
376
392
  set_augment_manifest_version,
393
+ set_plugin_manifest_versions,
377
394
  set_marketplace_version,
378
395
  set_template_pin,
379
396
  pyLen,
@@ -117,6 +117,9 @@
117
117
  { "prefix": "LICENSE", "category": "consumer" },
118
118
  { "prefix": "NOTICE", "category": "consumer" },
119
119
  { "prefix": "llms.txt", "category": "consumer" },
120
+ { "prefix": "gemini-extension.json", "category": "consumer" },
121
+ { "prefix": "gemini-extension-context.md", "category": "consumer" },
122
+ { "prefix": "assets/", "category": "consumer" },
120
123
 
121
124
  { "prefix": "AGENTS.md", "category": "governance" },
122
125
  { "prefix": "CLAUDE.md", "category": "governance" },
@@ -0,0 +1,46 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "provenance-mechanism.schema.json",
4
+ "title": "Provenance ledger mechanism decision",
5
+ "description": "One line of provenance/mechanisms.jsonl — a decision about one harvested mechanism: adopted, adapted, rejected, already present, missing, or deferred. Source-free by construction (council O5): no source id, no pin, no locator. What links a source to a mechanism lives in the local ledger under the user-global runtime/lineage/ and never in the tracked tree. Append-only; a later row for the same mechanism_id supersedes an earlier one. Contract: provenance/README.md. Enforced by src/scripts/lint_harvest_provenance.ts.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["mechanism_id", "verdict", "decided_at"],
9
+ "properties": {
10
+ "mechanism_id": {
11
+ "type": "string",
12
+ "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
13
+ "description": "Kebab-case slug naming the mechanism. A harvest citation marker may cite it; for a mechanism seeded from harvests.jsonl it equals the harvest_id."
14
+ },
15
+ "verdict": {
16
+ "type": "string",
17
+ "enum": ["adopt", "adapt", "reject", "already", "gap", "deferred"],
18
+ "description": "adopt|adapt — realised in this repo; reject — decided against; already — present before the harvest; gap — wanted and missing; deferred — wanted, parked."
19
+ },
20
+ "realised_in": {
21
+ "type": "array",
22
+ "minItems": 1,
23
+ "items": { "type": "string" },
24
+ "description": "Repo-relative paths where the mechanism lives, each optionally suffixed ':<line>'. Required for adopt and adapt; every path must exist."
25
+ },
26
+ "roadmap": {
27
+ "type": "string",
28
+ "description": "Repo-relative path of the roadmap that owns a gap or deferred mechanism, or the literal 'open' when nothing owns it yet."
29
+ },
30
+ "decided_at": {
31
+ "type": "string",
32
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
33
+ "description": "ISO-8601 date (YYYY-MM-DD) of the decision."
34
+ },
35
+ "reason": {
36
+ "type": "string",
37
+ "minLength": 3,
38
+ "description": "Why. Required for reject."
39
+ },
40
+ "reopen_if": {
41
+ "type": "string",
42
+ "minLength": 3,
43
+ "description": "The condition under which a rejection or deferral is reconsidered."
44
+ }
45
+ }
46
+ }