@event4u/agent-config 17.3.0 → 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.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +164 -342
- package/CREDITS.md +2 -2
- package/README.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +3 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +2 -2
- package/dist/cli-delegate/{chunk-DHSSP7V3.js → chunk-5AYJGUA3.js} +0 -2
- package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
- package/dist/cli-delegate/{chunk-MT2WOOCY.js → chunk-WY5ZZPGL.js} +2 -2
- package/dist/cli-delegate/{chunk-ORJPBER2.js → chunk-X6LZ7W5A.js} +73 -10
- package/dist/cli-delegate/cmd_conformance.js +175 -183
- package/dist/cli-delegate/cmd_doctor.js +5 -5
- package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_update.js +24 -13
- package/dist/cli-delegate/cmd_upgrade.js +2 -2
- package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
- package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
- package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
- package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +3 -3
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/code_graph_refresh.chunk.cjs +56 -27
- package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -1
- package/dist/hooks/dispatch.js +982 -929
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +56 -6
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
- package/dist/hooks/update_notice.chunk.cjs +914 -0
- package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +71259 -66437
- package/dist/scripts/condense.js +9 -6
- package/package.json +4 -1
- package/src/config/evaluator-budgets.json +9 -8
- package/src/config/gate-violation-baselines.json +2 -2
- package/src/config/hook-bundle-budget.json +31 -6
- package/src/config/packed-binary-manifest.json +9 -0
- package/src/config/sigstore/npm_keys.json +50 -0
- package/src/config/sigstore/trust-material.json +9 -0
- package/src/config/sigstore/trusted_root.json +126 -0
- package/src/config/skill-body-budget.json +7 -1
- package/src/scripts/_cli/cmd_update.ts +48 -20
- package/src/scripts/_cli/cmd_upgrade.ts +7 -1
- package/src/scripts/_cli/upgrade_unattended.ts +90 -76
- package/src/scripts/_dispatch.bash +3 -2
- package/src/scripts/_lib/confusables.ts +0 -19
- package/src/scripts/_lib/egress_normalize.ts +53 -23
- package/src/scripts/_lib/egress_text.ts +5 -3
- package/src/scripts/_lib/egress_token.ts +7 -5
- package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
- package/src/scripts/_lib/git_common_dir.ts +10 -0
- package/src/scripts/_lib/graph_feeder_record.ts +3 -2
- package/src/scripts/_lib/install_reach_checks.ts +32 -2
- package/src/scripts/_lib/lineage_backfill.ts +136 -0
- package/src/scripts/_lib/lineage_ledger.ts +211 -0
- package/src/scripts/_lib/lineage_map.ts +55 -0
- package/src/scripts/_lib/lineage_query.ts +140 -0
- package/src/scripts/_lib/lineage_seed.ts +116 -0
- package/src/scripts/_lib/lineage_watch.ts +249 -0
- package/src/scripts/_lib/link_crypto.ts +8 -4
- package/src/scripts/_lib/production_reach.ts +3 -3
- package/src/scripts/_lib/project_registry.ts +227 -0
- package/src/scripts/_lib/provenance_verify.ts +14 -242
- package/src/scripts/_lib/sigstore_provenance.ts +177 -0
- package/src/scripts/_lib/source_id.ts +48 -0
- package/src/scripts/_lib/source_shape.ts +4 -0
- package/src/scripts/_lib/unattended_gates.ts +6 -1
- package/src/scripts/advisories.ts +26 -10
- package/src/scripts/ai_council/council_egress.ts +5 -6
- package/src/scripts/bench_graph_feeder_latency.ts +10 -3
- package/src/scripts/check_dependency_floors.ts +7 -0
- package/src/scripts/check_egress_mutations.ts +1 -0
- package/src/scripts/code_graph/build.ts +67 -3
- package/src/scripts/code_graph/cli.ts +131 -28
- package/src/scripts/code_graph/conventions_default.ts +20 -1
- package/src/scripts/code_graph/detect.ts +5 -5
- package/src/scripts/code_graph/estate_cli.ts +135 -0
- package/src/scripts/code_graph/estate_index.ts +394 -0
- package/src/scripts/code_graph/estate_manifests.ts +223 -0
- package/src/scripts/code_graph/estate_map.ts +141 -0
- package/src/scripts/code_graph/extract.ts +4 -1
- package/src/scripts/code_graph/extract_more.ts +137 -2
- package/src/scripts/code_graph/graph_budget.ts +158 -0
- package/src/scripts/code_graph/graph_home.ts +80 -0
- package/src/scripts/code_graph/lineage_cli.ts +315 -0
- package/src/scripts/code_graph/map.ts +22 -13
- package/src/scripts/code_graph/map_model.ts +67 -12
- package/src/scripts/code_graph/map_verify.ts +68 -5
- package/src/scripts/code_graph/map_viewer/viewer.css +12 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +45 -15
- package/src/scripts/code_graph/project_ref.ts +25 -0
- package/src/scripts/code_graph/projects_cli.ts +254 -0
- package/src/scripts/code_graph/providers/catalog.ts +14 -5
- package/src/scripts/code_graph/providers/django.ts +52 -9
- package/src/scripts/code_graph/providers/index.ts +2 -0
- package/src/scripts/code_graph/providers/rails.ts +204 -0
- package/src/scripts/code_graph/providers/spring.ts +38 -1
- package/src/scripts/code_graph/query.ts +9 -9
- package/src/scripts/code_graph/refresh_lock.ts +12 -8
- package/src/scripts/code_graph/repo_identity.ts +169 -0
- package/src/scripts/code_graph/sqlite_store.ts +22 -25
- package/src/scripts/code_graph/types.ts +6 -2
- package/src/scripts/condense.ts +6 -6
- package/src/scripts/hook_chunks.mjs +7 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +20 -17
- package/src/scripts/hooks/code_graph_refresh_hook.ts +10 -7
- package/src/scripts/hooks/dispatch_hook.ts +6 -7
- package/src/scripts/hooks/host_semantics.ts +72 -0
- package/src/scripts/hooks/lazy_concern.ts +4 -0
- package/src/scripts/hooks/user_notice.ts +105 -0
- package/src/scripts/lint_harvest_provenance.ts +137 -11
- package/src/scripts/mcp_server/consumer_tool_catalog.json +69 -15
- package/src/scripts/mcp_server/graph_tools.ts +161 -41
- package/src/scripts/network_sources.ts +24 -6
- package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
- package/src/scripts/update_notice_hook.ts +75 -7
- package/src/vendor/grammars/README.md +7 -6
- package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
- /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
- /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
|
@@ -237,6 +237,9 @@
|
|
|
237
237
|
"properties": {
|
|
238
238
|
"area": {
|
|
239
239
|
"type": "string"
|
|
240
|
+
},
|
|
241
|
+
"project": {
|
|
242
|
+
"type": "string"
|
|
240
243
|
}
|
|
241
244
|
},
|
|
242
245
|
"additionalProperties": false
|
|
@@ -247,7 +250,7 @@
|
|
|
247
250
|
},
|
|
248
251
|
{
|
|
249
252
|
"name": "graph_dead",
|
|
250
|
-
"description": "Symbols
|
|
253
|
+
"description": "Symbols no accepted reference and no declared entry point names. REFUSES (status `refused`) while a source is unreadable: without recorded exportedness an exported-but-unimported symbol looks dead. Never a deletion recommendation.",
|
|
251
254
|
"side_effect": "ro",
|
|
252
255
|
"implemented_on": [
|
|
253
256
|
"stdio"
|
|
@@ -257,12 +260,46 @@
|
|
|
257
260
|
"properties": {
|
|
258
261
|
"entry_points": {
|
|
259
262
|
"type": "string",
|
|
260
|
-
"description": "Path under this root
|
|
263
|
+
"description": "Path under this root: newline-separated ids or labels, `#` comments ok."
|
|
261
264
|
},
|
|
262
265
|
"accept_missing_exports": {
|
|
263
266
|
"type": "boolean",
|
|
264
267
|
"default": false,
|
|
265
268
|
"description": "Answer anyway, accepting the gap. Never a default."
|
|
269
|
+
},
|
|
270
|
+
"project": {
|
|
271
|
+
"type": "string"
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
"additionalProperties": false
|
|
275
|
+
},
|
|
276
|
+
"annotations": {
|
|
277
|
+
"readOnlyHint": true
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"name": "graph_estate",
|
|
282
|
+
"description": "Estate index across registered projects: package users, where a name is used, advisory reach. Other graph tools take `project`.",
|
|
283
|
+
"side_effect": "ro",
|
|
284
|
+
"implemented_on": [
|
|
285
|
+
"stdio"
|
|
286
|
+
],
|
|
287
|
+
"input_schema": {
|
|
288
|
+
"type": "object",
|
|
289
|
+
"required": [
|
|
290
|
+
"query"
|
|
291
|
+
],
|
|
292
|
+
"properties": {
|
|
293
|
+
"query": {
|
|
294
|
+
"type": "string",
|
|
295
|
+
"enum": [
|
|
296
|
+
"where-used",
|
|
297
|
+
"projects-using",
|
|
298
|
+
"advisory-impact"
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
"name": {
|
|
302
|
+
"type": "string"
|
|
266
303
|
}
|
|
267
304
|
},
|
|
268
305
|
"additionalProperties": false
|
|
@@ -273,7 +310,7 @@
|
|
|
273
310
|
},
|
|
274
311
|
{
|
|
275
312
|
"name": "graph_impact",
|
|
276
|
-
"description": "What a diff can break: callers, dependents and test files reaching the changed symbols
|
|
313
|
+
"description": "What a diff can break: callers, dependents and test files reaching the changed symbols, over stated-fact edges only (refused guesses reported). Runs `git diff` for the rev.",
|
|
277
314
|
"side_effect": "shell",
|
|
278
315
|
"implemented_on": [
|
|
279
316
|
"stdio"
|
|
@@ -298,6 +335,9 @@
|
|
|
298
335
|
"type": "boolean",
|
|
299
336
|
"default": false,
|
|
300
337
|
"description": "Instead return changed symbols no test imports."
|
|
338
|
+
},
|
|
339
|
+
"project": {
|
|
340
|
+
"type": "string"
|
|
301
341
|
}
|
|
302
342
|
},
|
|
303
343
|
"additionalProperties": false
|
|
@@ -315,7 +355,11 @@
|
|
|
315
355
|
],
|
|
316
356
|
"input_schema": {
|
|
317
357
|
"type": "object",
|
|
318
|
-
"properties": {
|
|
358
|
+
"properties": {
|
|
359
|
+
"project": {
|
|
360
|
+
"type": "string"
|
|
361
|
+
}
|
|
362
|
+
},
|
|
319
363
|
"additionalProperties": false
|
|
320
364
|
},
|
|
321
365
|
"annotations": {
|
|
@@ -324,7 +368,7 @@
|
|
|
324
368
|
},
|
|
325
369
|
{
|
|
326
370
|
"name": "graph_node",
|
|
327
|
-
"description": "One symbol in full: where it is declared, what reaches it, what it reaches,
|
|
371
|
+
"description": "One symbol in full: where it is declared, what reaches it, what it reaches, how connected it is. Resolves EXACTLY (node id, then label) and refuses anything else — never scores free text.",
|
|
328
372
|
"side_effect": "ro",
|
|
329
373
|
"implemented_on": [
|
|
330
374
|
"stdio"
|
|
@@ -347,7 +391,7 @@
|
|
|
347
391
|
"both"
|
|
348
392
|
],
|
|
349
393
|
"default": "both",
|
|
350
|
-
"description": "
|
|
394
|
+
"description": "`in` = who reaches it, `out` = what it reaches."
|
|
351
395
|
},
|
|
352
396
|
"depth": {
|
|
353
397
|
"type": "integer",
|
|
@@ -375,7 +419,10 @@
|
|
|
375
419
|
"injects",
|
|
376
420
|
"documents"
|
|
377
421
|
],
|
|
378
|
-
"description": "
|
|
422
|
+
"description": "One relation only; omit for all."
|
|
423
|
+
},
|
|
424
|
+
"project": {
|
|
425
|
+
"type": "string"
|
|
379
426
|
}
|
|
380
427
|
},
|
|
381
428
|
"additionalProperties": false
|
|
@@ -386,7 +433,7 @@
|
|
|
386
433
|
},
|
|
387
434
|
{
|
|
388
435
|
"name": "graph_path",
|
|
389
|
-
"description": "The shortest relation chain between two symbols, direction
|
|
436
|
+
"description": "The shortest relation chain between two symbols, direction kept: how A reaches B without reading the files between. `(no path found)` is an answer.",
|
|
390
437
|
"side_effect": "ro",
|
|
391
438
|
"implemented_on": [
|
|
392
439
|
"stdio"
|
|
@@ -409,8 +456,10 @@
|
|
|
409
456
|
"budget": {
|
|
410
457
|
"type": "integer",
|
|
411
458
|
"minimum": 1,
|
|
412
|
-
"default": 1500
|
|
413
|
-
|
|
459
|
+
"default": 1500
|
|
460
|
+
},
|
|
461
|
+
"project": {
|
|
462
|
+
"type": "string"
|
|
414
463
|
}
|
|
415
464
|
},
|
|
416
465
|
"additionalProperties": false
|
|
@@ -421,7 +470,7 @@
|
|
|
421
470
|
},
|
|
422
471
|
{
|
|
423
472
|
"name": "graph_query",
|
|
424
|
-
"description": "A symbol's direct outgoing relations
|
|
473
|
+
"description": "A symbol's direct outgoing relations (calls, imports, uses, inherits), each with its confidence and resolution mechanism: \"what does this touch\" before reading a file.",
|
|
425
474
|
"side_effect": "ro",
|
|
426
475
|
"implemented_on": [
|
|
427
476
|
"stdio"
|
|
@@ -439,8 +488,10 @@
|
|
|
439
488
|
"budget": {
|
|
440
489
|
"type": "integer",
|
|
441
490
|
"minimum": 1,
|
|
442
|
-
"default": 1500
|
|
443
|
-
|
|
491
|
+
"default": 1500
|
|
492
|
+
},
|
|
493
|
+
"project": {
|
|
494
|
+
"type": "string"
|
|
444
495
|
}
|
|
445
496
|
},
|
|
446
497
|
"additionalProperties": false
|
|
@@ -451,7 +502,7 @@
|
|
|
451
502
|
},
|
|
452
503
|
{
|
|
453
504
|
"name": "graph_tests_for",
|
|
454
|
-
"description": "
|
|
505
|
+
"description": "Test files that exercise a symbol: a test file imports it or its module. Empty means none does — a finding, not an error.",
|
|
455
506
|
"side_effect": "ro",
|
|
456
507
|
"implemented_on": [
|
|
457
508
|
"stdio"
|
|
@@ -464,7 +515,10 @@
|
|
|
464
515
|
"properties": {
|
|
465
516
|
"symbol": {
|
|
466
517
|
"type": "string",
|
|
467
|
-
"description": "Node id
|
|
518
|
+
"description": "Node id, label, or free text."
|
|
519
|
+
},
|
|
520
|
+
"project": {
|
|
521
|
+
"type": "string"
|
|
468
522
|
}
|
|
469
523
|
},
|
|
470
524
|
"additionalProperties": false
|
|
@@ -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,
|
|
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 {
|
|
@@ -65,8 +72,49 @@ import {
|
|
|
65
72
|
import { areaReport, graphGeneration, readMap } from '../code_graph/map.js';
|
|
66
73
|
import type { BuiltinTool } from './tools.js';
|
|
67
74
|
|
|
68
|
-
/**
|
|
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,
|
|
139
|
+
const picked = pickSource(detectSources(root, graphHome(root).cachePath));
|
|
92
140
|
if (!picked || picked.kind === 'scip') return noGraph(root);
|
|
93
|
-
|
|
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() : '';
|
|
@@ -143,14 +198,49 @@ function num(args: Record<string, unknown>, key: string, dflt: number): number {
|
|
|
143
198
|
return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : dflt;
|
|
144
199
|
}
|
|
145
200
|
|
|
146
|
-
|
|
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({
|
|
147
237
|
graph_impact: {
|
|
148
238
|
name: 'graph_impact',
|
|
149
239
|
side_effect: 'shell',
|
|
150
240
|
description:
|
|
151
241
|
'What a diff can break: callers, dependents and test files reaching the ' +
|
|
152
|
-
'changed symbols
|
|
153
|
-
'
|
|
242
|
+
'changed symbols, over stated-fact edges only (refused guesses reported). ' +
|
|
243
|
+
'Runs `git diff` for the rev.',
|
|
154
244
|
input_schema: {
|
|
155
245
|
type: 'object',
|
|
156
246
|
required: ['diff'],
|
|
@@ -170,7 +260,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
170
260
|
if (rev === '') return { status: 'error', error: 'diff must be a non-empty git rev' };
|
|
171
261
|
const files = changedFiles(root, rev);
|
|
172
262
|
if (files === null) return { status: 'error', error: `cannot resolve rev '${rev}' in ${root}` };
|
|
173
|
-
const state = graphState(root);
|
|
263
|
+
const state = graphState(root, graphHome(root).cachePath);
|
|
174
264
|
return withGraph(root, (g) =>
|
|
175
265
|
args['untested_only'] === true
|
|
176
266
|
? { ...untested(g, files, state), changed_files: files.length }
|
|
@@ -183,20 +273,20 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
183
273
|
name: 'graph_tests_for',
|
|
184
274
|
side_effect: 'ro',
|
|
185
275
|
description:
|
|
186
|
-
'
|
|
187
|
-
'
|
|
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.',
|
|
188
278
|
input_schema: {
|
|
189
279
|
type: 'object',
|
|
190
280
|
required: ['symbol'],
|
|
191
281
|
properties: {
|
|
192
|
-
symbol: { type: 'string', description: 'Node id
|
|
282
|
+
symbol: { type: 'string', description: 'Node id, label, or free text.' },
|
|
193
283
|
},
|
|
194
284
|
additionalProperties: false,
|
|
195
285
|
},
|
|
196
286
|
handler: async (args, root) => {
|
|
197
287
|
const symbol = str(args, 'symbol');
|
|
198
288
|
if (symbol === '') return { status: 'error', error: 'symbol must be a non-empty string' };
|
|
199
|
-
const state = graphState(root);
|
|
289
|
+
const state = graphState(root, graphHome(root).cachePath);
|
|
200
290
|
return withGraph(root, (g) => ({ ...testsFor(g, symbol, state) }));
|
|
201
291
|
},
|
|
202
292
|
},
|
|
@@ -205,16 +295,15 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
205
295
|
name: 'graph_dead',
|
|
206
296
|
side_effect: 'ro',
|
|
207
297
|
description:
|
|
208
|
-
'Symbols
|
|
209
|
-
'
|
|
210
|
-
'
|
|
211
|
-
'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.',
|
|
212
301
|
input_schema: {
|
|
213
302
|
type: 'object',
|
|
214
303
|
properties: {
|
|
215
304
|
entry_points: {
|
|
216
305
|
type: 'string',
|
|
217
|
-
description: 'Path under this root
|
|
306
|
+
description: 'Path under this root: newline-separated ids or labels, `#` comments ok.',
|
|
218
307
|
},
|
|
219
308
|
accept_missing_exports: {
|
|
220
309
|
type: 'boolean',
|
|
@@ -225,7 +314,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
225
314
|
additionalProperties: false,
|
|
226
315
|
},
|
|
227
316
|
handler: async (args, root) => {
|
|
228
|
-
const state = graphState(root);
|
|
317
|
+
const state = graphState(root, graphHome(root).cachePath);
|
|
229
318
|
const listArg = str(args, 'entry_points');
|
|
230
319
|
let sources = repoEntryPoints(root);
|
|
231
320
|
if (listArg !== '') {
|
|
@@ -281,22 +370,21 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
281
370
|
name: 'graph_query',
|
|
282
371
|
side_effect: 'ro',
|
|
283
372
|
description:
|
|
284
|
-
"A symbol's direct outgoing relations
|
|
285
|
-
'
|
|
286
|
-
'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.',
|
|
287
375
|
input_schema: {
|
|
288
376
|
type: 'object',
|
|
289
377
|
required: ['symbol'],
|
|
290
378
|
properties: {
|
|
291
379
|
symbol: { type: 'string', description: 'Node id, label, or free text.' },
|
|
292
|
-
budget: { type: 'integer', minimum: 1, default: 1500
|
|
380
|
+
budget: { type: 'integer', minimum: 1, default: 1500 },
|
|
293
381
|
},
|
|
294
382
|
additionalProperties: false,
|
|
295
383
|
},
|
|
296
384
|
handler: async (args, root) => {
|
|
297
385
|
const symbol = str(args, 'symbol');
|
|
298
386
|
if (symbol === '') return { status: 'error', error: 'symbol must be a non-empty string' };
|
|
299
|
-
const staleness = graphState(root);
|
|
387
|
+
const staleness = graphState(root, graphHome(root).cachePath);
|
|
300
388
|
return withGraph(root, (g) => ({
|
|
301
389
|
staleness,
|
|
302
390
|
...graphQueryVerb(g, symbol, num(args, 'budget', 1500)),
|
|
@@ -308,10 +396,9 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
308
396
|
name: 'graph_node',
|
|
309
397
|
side_effect: 'ro',
|
|
310
398
|
description:
|
|
311
|
-
'One symbol in full: where it is declared, what reaches it, what it ' +
|
|
312
|
-
'
|
|
313
|
-
'
|
|
314
|
-
'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.',
|
|
315
402
|
input_schema: {
|
|
316
403
|
type: 'object',
|
|
317
404
|
required: ['id'],
|
|
@@ -321,13 +408,13 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
321
408
|
type: 'string',
|
|
322
409
|
enum: ['in', 'out', 'both'],
|
|
323
410
|
default: 'both',
|
|
324
|
-
description: '
|
|
411
|
+
description: '`in` = who reaches it, `out` = what it reaches.',
|
|
325
412
|
},
|
|
326
413
|
depth: { type: 'integer', minimum: 1, maximum: 3, default: 1, description: 'Hop limit.' },
|
|
327
414
|
relation: {
|
|
328
415
|
type: 'string',
|
|
329
416
|
enum: RELATIONS,
|
|
330
|
-
description: '
|
|
417
|
+
description: 'One relation only; omit for all.',
|
|
331
418
|
},
|
|
332
419
|
},
|
|
333
420
|
additionalProperties: false,
|
|
@@ -335,7 +422,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
335
422
|
handler: async (args, root) => {
|
|
336
423
|
const id = str(args, 'id');
|
|
337
424
|
if (id === '') return { status: 'error', error: 'id must be a non-empty string' };
|
|
338
|
-
const staleness = graphState(root);
|
|
425
|
+
const staleness = graphState(root, graphHome(root).cachePath);
|
|
339
426
|
const opts: NodeOptions = {};
|
|
340
427
|
const dir = str(args, 'direction');
|
|
341
428
|
if (dir === 'in' || dir === 'out' || dir === 'both') opts.direction = dir;
|
|
@@ -353,16 +440,15 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
353
440
|
name: 'graph_path',
|
|
354
441
|
side_effect: 'ro',
|
|
355
442
|
description:
|
|
356
|
-
'The shortest relation chain between two symbols, direction
|
|
357
|
-
'
|
|
358
|
-
'`(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.',
|
|
359
445
|
input_schema: {
|
|
360
446
|
type: 'object',
|
|
361
447
|
required: ['from', 'to'],
|
|
362
448
|
properties: {
|
|
363
449
|
from: { type: 'string', description: 'Start node id, label, or free text.' },
|
|
364
450
|
to: { type: 'string', description: 'Goal node id, label, or free text.' },
|
|
365
|
-
budget: { type: 'integer', minimum: 1, default: 1500
|
|
451
|
+
budget: { type: 'integer', minimum: 1, default: 1500 },
|
|
366
452
|
},
|
|
367
453
|
additionalProperties: false,
|
|
368
454
|
},
|
|
@@ -370,7 +456,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
370
456
|
const from = str(args, 'from');
|
|
371
457
|
const to = str(args, 'to');
|
|
372
458
|
if (from === '' || to === '') return { status: 'error', error: 'from and to must be non-empty strings' };
|
|
373
|
-
const staleness = graphState(root);
|
|
459
|
+
const staleness = graphState(root, graphHome(root).cachePath);
|
|
374
460
|
return withGraph(root, (g) => ({
|
|
375
461
|
staleness,
|
|
376
462
|
...graphPathVerb(g, from, to, num(args, 'budget', 1500)),
|
|
@@ -383,7 +469,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
383
469
|
description: 'Is there a code map (agents/map/)? Its freshness, counts and MAP.md path.',
|
|
384
470
|
input_schema: { type: 'object', properties: {}, additionalProperties: false },
|
|
385
471
|
handler: async (_args, root) => {
|
|
386
|
-
const staleness = graphState(root);
|
|
472
|
+
const staleness = graphState(root, graphHome(root).cachePath);
|
|
387
473
|
const map = readMap(root);
|
|
388
474
|
if (!map) {
|
|
389
475
|
return {
|
|
@@ -394,6 +480,8 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
394
480
|
};
|
|
395
481
|
}
|
|
396
482
|
const m = map.model;
|
|
483
|
+
const home = graphHome(root);
|
|
484
|
+
const mapRel = home.kind === 'project' ? 'agents/map' : home.mapDir;
|
|
397
485
|
return {
|
|
398
486
|
status: 'ok',
|
|
399
487
|
staleness,
|
|
@@ -401,7 +489,7 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
401
489
|
drawn_at_commit: map.meta.commit,
|
|
402
490
|
graph_state_when_drawn: map.meta.state,
|
|
403
491
|
// Same generation → the map shows exactly the graph the queries read.
|
|
404
|
-
map_matches_graph: map.meta.graph_generation !== undefined && map.meta.graph_generation === graphGeneration(
|
|
492
|
+
map_matches_graph: map.meta.graph_generation !== undefined && map.meta.graph_generation === graphGeneration(home.cachePath),
|
|
405
493
|
files: m.files.length,
|
|
406
494
|
areas: m.areas.length,
|
|
407
495
|
trusted_links: m.analyses.trustedEdges,
|
|
@@ -409,8 +497,8 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
409
497
|
request_paths: m.analyses.requestPathsTotal,
|
|
410
498
|
providers: (m.providers ?? []).map((p) => `${p.id} (${p.status})`),
|
|
411
499
|
largest_areas: m.areas.slice(0, 5).map((a) => `${a.id} ${a.name} (${a.files.length})`),
|
|
412
|
-
map_md:
|
|
413
|
-
index_html:
|
|
500
|
+
map_md: `${mapRel}/MAP.md`,
|
|
501
|
+
index_html: `${mapRel}/index.html`,
|
|
414
502
|
};
|
|
415
503
|
},
|
|
416
504
|
},
|
|
@@ -431,9 +519,41 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
|
|
|
431
519
|
if (q === '') return { status: 'error', error: 'area must be a non-empty string' };
|
|
432
520
|
const map = readMap(root);
|
|
433
521
|
if (!map) return { status: 'unavailable', error: 'no map — run `agent-config code-graph map`' };
|
|
434
|
-
const
|
|
522
|
+
const home = graphHome(root);
|
|
523
|
+
const r = areaReport(map.model, q, home.kind === 'project' ? undefined : home.mapDir);
|
|
435
524
|
if ('refusal' in r) return { status: 'refused', refusal: r.refusal, candidates: r.candidates };
|
|
436
525
|
return { status: 'ok', drawn_at_commit: map.meta.commit, ...r };
|
|
437
526
|
},
|
|
438
527
|
},
|
|
439
|
-
|
|
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
|
-
/**
|
|
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(
|
|
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
|
-
|
|
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
|
|
238
|
+
return this.codes[i] as string;
|
|
225
239
|
}
|
|
226
240
|
|
|
227
241
|
intakeMarkdown(): string {
|
|
228
|
-
const rows = this.tokens.map((token, i) => `-
|
|
229
|
-
|
|
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 {
|