@agentskit/doc-bridge 1.7.45 → 1.9.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/CHANGELOG.md +465 -0
- package/CONTRIBUTING.md +23 -0
- package/README.md +87 -40
- package/SECURITY.md +7 -0
- package/action.yml +1 -1
- package/bin/ak-docs.js +2 -2
- package/bin/ak-verify.js +13 -7
- package/dist/cli/program.d.ts +3 -1
- package/dist/cli/program.js +15861 -6223
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +86 -6
- package/dist/config/index.js.map +1 -1
- package/dist/{index-BUL0q7s8.d.ts → index-Beor6Yhi.d.ts} +133 -1
- package/dist/index.d.ts +9871 -1062
- package/dist/index.js +16126 -6125
- package/dist/index.js.map +1 -1
- package/docs/MARKETPLACE.md +1 -1
- package/docs/PRD-documentation-efficiency-study.md +406 -0
- package/docs/PRD-knowledge-retrieval-and-enrichment.md +466 -0
- package/docs/adr/0002-documentation-audit-boundary.md +22 -0
- package/docs/adr/0003-study-protocol-and-historical-evidence.md +40 -0
- package/docs/adr/0004-controlled-study-runner.md +25 -0
- package/docs/adr/0005-documentation-quality-and-criticality.md +20 -0
- package/docs/adr/0006-registry-semantic-grounding.md +20 -0
- package/docs/adr/0007-longitudinal-study-metrics.md +21 -0
- package/docs/adr/0008-study-verification-boundary.md +21 -0
- package/docs/adr/0009-study-provider-cli-adapter.md +25 -0
- package/docs/agent-corpus/INDEX.md +12 -1
- package/docs/agent-corpus/OVERVIEW.md +25 -0
- package/docs/agent-corpus/chat.md +5 -1
- package/docs/agent-corpus/cli.md +16 -0
- package/docs/agent-corpus/conformance.md +12 -0
- package/docs/agent-corpus/doc-bridge.md +47 -0
- package/docs/agent-corpus/doctor.md +8 -0
- package/docs/agent-corpus/gates.md +4 -0
- package/docs/agent-corpus/mcp.md +13 -0
- package/docs/agent-corpus/memory.md +4 -0
- package/docs/agent-corpus/query.md +33 -0
- package/docs/bench/README.md +122 -0
- package/docs/bench/retrieval-baseline-v1.json +28 -0
- package/docs/bench/retrieval-suite-v1.json +1033 -0
- package/docs/chat-and-rag.md +3 -2
- package/docs/for-agents.md +9 -1
- package/docs/getting-started.md +4 -11
- package/docs/guides/gate-ci.md +11 -1
- package/docs/guides/install-and-run.md +9 -65
- package/docs/index.md +22 -1
- package/docs/knowledge-engine-runbook.md +38 -3
- package/docs/landing/assets/context-payload-reduction.svg +21 -0
- package/docs/landing/assets/controlled-ab-comparison.svg +30 -0
- package/docs/landing/index.html +119 -5
- package/docs/loop-workflow.md +117 -0
- package/docs/mcp.md +6 -1
- package/docs/parity/public-claims-v1.json +145 -0
- package/docs/playbook/doc-bridge-pattern.md +1 -1
- package/docs/query.md +90 -2
- package/docs/recipes/index-pipeline.md +1 -1
- package/docs/schemas/agent-handoff-v1.md +15 -0
- package/docs/schemas/doc-bridge-index-v1.md +65 -0
- package/docs/spec/benchmark-v1.md +33 -1
- package/docs/spec/cli.md +30 -10
- package/docs/spec/config-v1.md +147 -8
- package/docs/spec/documentation-audit-v1.md +61 -0
- package/docs/spec/enrichment-overlay-v1.md +241 -0
- package/docs/spec/graph-signals-v1.md +92 -0
- package/docs/spec/incremental-scan-v1.md +102 -0
- package/docs/spec/markdown-analyzer-v1.md +73 -0
- package/docs/spec/mcp-knowledge-tools-v1.md +147 -0
- package/docs/spec/measured-enrichment-v1.md +229 -0
- package/docs/spec/public-parity-v1.md +119 -0
- package/docs/spec/registry-agents.md +6 -0
- package/docs/spec/render-v1.md +122 -0
- package/docs/spec/retrieval-index-v1.md +164 -0
- package/docs/spec/study-metrics-v1.md +58 -0
- package/docs/spec/study-protocol-v1.md +46 -0
- package/docs/spec/study-provider-cli-v1.md +116 -0
- package/docs/spec/study-runner-v1.md +35 -0
- package/docs/spec/study-task-suite-v1.md +41 -0
- package/docs/spec/study-verification-v1.md +40 -0
- package/docs/study/README.md +84 -0
- package/docs/study/ab-adjudicated-cost-analysis-v1.md +29 -0
- package/docs/study/ab-adjudicated-cost-plan-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-plan-v2-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-result-v1.json +80 -0
- package/docs/study/ab-baseline-analysis-v1.md +21 -0
- package/docs/study/ab-baseline-plan-v1.json +33 -0
- package/docs/study/ab-baseline-recovery-plan-v1.json +33 -0
- package/docs/study/ab-baseline-result-v1.json +79 -0
- package/docs/study/documentation-audit-round-2026-08-31.json +183 -0
- package/docs/study/historical-evidence-v1.json +252 -0
- package/docs/study/observation-ledger-v1.json +30632 -0
- package/docs/study/phase3-task-coverage-v1.json +34 -0
- package/docs/study/phase4-public-pilot-ledger-v1.json +1344 -0
- package/docs/study/phase4-public-pilot-result-v1.json +52 -0
- package/docs/study/phase4-public-pilot-run-plan-v1.json +26 -0
- package/docs/study/phase4-public-pilot-task-suite-v1.json +71 -0
- package/docs/study/pilot-round-2026-08-31.json +46 -0
- package/docs/study/protocol-v1.json +90 -0
- package/docs/study/publication-gate-v1.md +45 -0
- package/docs/study/quality-scorecard-cycle-plan.md +545 -0
- package/docs/study/quality-scorecard-v1.json +38 -0
- package/docs/study/round-1-adjudicated-smoke-v1.json +30642 -0
- package/docs/study/round-1-instrumentation-plan-v1.md +39 -0
- package/docs/study/round-2-expanded-adjudication-v1.json +91 -0
- package/docs/study/round-2-expanded-validation-v1.md +58 -0
- package/docs/study/round-3-evidence-contract-v1.json +75 -0
- package/docs/study/round-3-evidence-contract-v1.md +57 -0
- package/docs/study/round-4-confirmation-v1.json +75 -0
- package/docs/study/round-4-confirmation-v1.md +55 -0
- package/docs/study/run-plan-v1.json +33 -0
- package/docs/study/semantic-adjudication-cycle-8.md +20 -0
- package/docs/study/task-suite-v1.json +96 -0
- package/docs/study/token-efficiency-plan-v1.md +337 -0
- package/docs/study/token-efficiency-protocol-v2.json +62 -0
- package/docs/study/verification-binding-v1.json +27 -0
- package/docs/validation-cycle-plan.md +14 -0
- package/docs/verification-harness.md +11 -6
- package/ecosystem-claims.json +2 -2
- package/ecosystem-upstream.json +2 -2
- package/ecosystem.json +4 -4
- package/mcpb/manifest.json +9 -1
- package/package.json +25 -6
- package/scripts/check-ecosystem-upstream.mjs +34 -6
- package/skills/doc-bridge-handoff/fixtures/synthetic-repo/docs/for-agents/packages/payments.md +7 -0
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +192 -24
- package/src/audit/documentation.ts +513 -0
- package/src/bench/baseline.ts +198 -0
- package/src/bench/overlay-delta.ts +139 -0
- package/src/bench/retrieval.ts +319 -0
- package/src/budget/compile.ts +91 -0
- package/src/budget/sections.ts +70 -0
- package/src/cli/program.ts +684 -74
- package/src/cli/usage.ts +71 -0
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +4 -0
- package/src/config/schema.ts +117 -2
- package/src/conformance/documentation-standard-v1.ts +8 -6
- package/src/discovery/areas.ts +182 -0
- package/src/discovery/documentation.ts +211 -5
- package/src/discovery/identity.ts +24 -0
- package/src/discovery/incremental.ts +314 -0
- package/src/discovery/inputs.ts +110 -0
- package/src/discovery/markdown.ts +481 -0
- package/src/discovery/repository.ts +484 -101
- package/src/doctor/run-doctor.ts +244 -12
- package/src/enrich/approvals.ts +190 -0
- package/src/enrich/cache.ts +93 -0
- package/src/enrich/context-pack.ts +272 -0
- package/src/enrich/overlay.ts +255 -0
- package/src/enrich/review.ts +106 -0
- package/src/enrich/stage.ts +374 -0
- package/src/enrich/stats.ts +100 -0
- package/src/enrich/validate.ts +410 -0
- package/src/federation/llms.ts +2 -4
- package/src/findings/report.ts +103 -0
- package/src/graph/build.ts +356 -0
- package/src/graph/memory.ts +208 -0
- package/src/index-builder/build-handoffs.ts +22 -11
- package/src/index-builder/build-index.ts +132 -3
- package/src/index-builder/llms-txt.ts +48 -8
- package/src/index-builder/project-corpus.ts +111 -0
- package/src/index.ts +630 -2
- package/src/lib/fuzzy-match.ts +235 -0
- package/src/mcp/knowledge.ts +554 -0
- package/src/mcp/server.ts +113 -18
- package/src/metrics/benchmark.ts +21 -0
- package/src/parity/check.ts +309 -0
- package/src/parity/claims.ts +259 -0
- package/src/parity/resolve.ts +160 -0
- package/src/query/handoff.ts +326 -0
- package/src/query/load-index.ts +53 -1
- package/src/query/query.ts +92 -59
- package/src/query/search.ts +289 -92
- package/src/query/text.ts +155 -0
- package/src/reconciliation/reconcile.ts +101 -10
- package/src/render/data.ts +356 -0
- package/src/render/engine.ts +398 -0
- package/src/render/generated.ts +77 -0
- package/src/render/render.ts +209 -0
- package/src/render/template-source.ts +52 -0
- package/src/render/templates.ts +289 -0
- package/src/report/html.ts +2 -2
- package/src/retrieval/bm25.ts +161 -0
- package/src/retrieval/project.ts +495 -0
- package/src/retrieval/rank.ts +383 -0
- package/src/retrieval/weights.ts +39 -0
- package/src/retriever/doc-bridge-retriever.ts +100 -15
- package/src/rules/engine.ts +30 -10
- package/src/schemas/agent-handoff.ts +56 -0
- package/src/schemas/budget.ts +37 -0
- package/src/schemas/doc-bridge-index.ts +53 -2
- package/src/schemas/enrichment.ts +369 -0
- package/src/schemas/json-schemas.ts +39 -2
- package/src/schemas/knowledge.ts +14 -1
- package/src/schemas/retrieval-index.ts +152 -0
- package/src/shims/graphology.d.ts +91 -0
- package/src/study/adjudication.ts +196 -0
- package/src/study/execution.ts +350 -0
- package/src/study/expectations.ts +219 -0
- package/src/study/metrics.ts +467 -0
- package/src/study/protocol.ts +271 -0
- package/src/study/provider-cli.ts +115 -0
- package/src/study/provider-telemetry.ts +47 -0
- package/src/study/quality-scorecard.ts +164 -0
- package/src/study/runner.ts +461 -0
- package/src/study/task-suite.ts +321 -0
- package/src/study/verification.ts +134 -0
- package/src/validate.ts +1 -4
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +36 -11
- package/scripts/verification-harness.mjs +0 -482
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import { BudgetReportSchema } from './budget.js'
|
|
4
|
+
|
|
3
5
|
export const HANDOFF_SCHEMA_VERSION = 1 as const
|
|
6
|
+
export const AgentQueryModeSchema = z.enum(['discovery', 'editing', 'debugging', 'documentation'])
|
|
7
|
+
export type AgentQueryMode = z.infer<typeof AgentQueryModeSchema>
|
|
4
8
|
|
|
5
9
|
export const HandoffTargetTypeSchema = z.enum([
|
|
6
10
|
'package',
|
|
11
|
+
'area',
|
|
7
12
|
'module',
|
|
13
|
+
'document',
|
|
8
14
|
'app',
|
|
9
15
|
'screen',
|
|
10
16
|
'flow',
|
|
@@ -38,6 +44,20 @@ export const HandoffBridgeSchema = z
|
|
|
38
44
|
|
|
39
45
|
export type HandoffBridge = z.infer<typeof HandoffBridgeSchema>
|
|
40
46
|
|
|
47
|
+
/** An area or package this target's code depends on, or that depends on it, with what proves it. */
|
|
48
|
+
export const HandoffRelatedSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
id: z.string().min(1).max(256),
|
|
51
|
+
path: z.string().min(1).max(512),
|
|
52
|
+
direction: z.enum(['imports', 'imported-by']),
|
|
53
|
+
/** How many import edges cross the boundary. */
|
|
54
|
+
strength: z.number().int().positive(),
|
|
55
|
+
evidence: z.array(z.string().min(1).max(512)).max(8),
|
|
56
|
+
})
|
|
57
|
+
.strict()
|
|
58
|
+
|
|
59
|
+
export type HandoffRelated = z.infer<typeof HandoffRelatedSchema>
|
|
60
|
+
|
|
41
61
|
/** v1 — canonical AgentHandoff. Legacy payloads may omit schemaVersion. */
|
|
42
62
|
export const AgentHandoffV1Schema = z
|
|
43
63
|
.object({
|
|
@@ -53,6 +73,31 @@ export const AgentHandoffV1Schema = z
|
|
|
53
73
|
bridge: HandoffBridgeSchema.optional(),
|
|
54
74
|
playbookPatterns: z.array(z.string().url()).max(16).optional(),
|
|
55
75
|
notes: z.array(z.string().min(1).max(1024)).max(16),
|
|
76
|
+
/*
|
|
77
|
+
* Optional additions, so a handoff built before they existed is still a valid handoff and a
|
|
78
|
+
* reader that predates them sees the same fields it always did.
|
|
79
|
+
*/
|
|
80
|
+
related: z.array(HandoffRelatedSchema).max(16).optional(),
|
|
81
|
+
/** Which relations produced each field, by field name. */
|
|
82
|
+
explain: z.record(z.string().min(1).max(64), z.array(z.string().min(1).max(512)).max(16)).optional(),
|
|
83
|
+
evidence: z
|
|
84
|
+
.array(
|
|
85
|
+
z
|
|
86
|
+
.object({
|
|
87
|
+
source: z.enum(['code', 'configuration', 'documentation', 'agent', 'derived']),
|
|
88
|
+
path: z.string().min(1).max(512),
|
|
89
|
+
lineStart: z.number().int().positive().optional(),
|
|
90
|
+
lineEnd: z.number().int().positive().optional(),
|
|
91
|
+
contentHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
92
|
+
context: z.string().max(1_024).optional(),
|
|
93
|
+
})
|
|
94
|
+
.strict(),
|
|
95
|
+
)
|
|
96
|
+
.max(32)
|
|
97
|
+
.optional(),
|
|
98
|
+
metadata: z.record(z.string().min(1).max(64), z.unknown()).optional(),
|
|
99
|
+
/** Present only when the caller declared `budgetTokens`: what the payload cost and what it shed to fit. */
|
|
100
|
+
budget: BudgetReportSchema.optional(),
|
|
56
101
|
})
|
|
57
102
|
.strict()
|
|
58
103
|
|
|
@@ -84,6 +129,17 @@ export const AgentSearchV1Schema = z
|
|
|
84
129
|
bestMatch: AgentSearchMatchSchema.nullable(),
|
|
85
130
|
matches: z.array(AgentSearchMatchSchema).max(32),
|
|
86
131
|
nextCommands: z.array(z.string().min(1).max(512)).max(16),
|
|
132
|
+
telemetry: z
|
|
133
|
+
.object({
|
|
134
|
+
contextBytes: z.number().int().nonnegative(),
|
|
135
|
+
estimatedTokens: z.number().int().nonnegative(),
|
|
136
|
+
tokenMethod: z.literal('estimate'),
|
|
137
|
+
contextBudgetTokens: z.number().int().positive(),
|
|
138
|
+
mode: AgentQueryModeSchema,
|
|
139
|
+
truncated: z.boolean(),
|
|
140
|
+
})
|
|
141
|
+
.strict()
|
|
142
|
+
.optional(),
|
|
87
143
|
})
|
|
88
144
|
.strict()
|
|
89
145
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The sections a budgeted payload may shed, in the order it sheds them.
|
|
5
|
+
*
|
|
6
|
+
* Excerpts go first: they are the longest text and the easiest to fetch again. Then the related
|
|
7
|
+
* areas, then the neighbourhood, then the summaries. What is never on this list — the entity,
|
|
8
|
+
* the evidence paths and hashes, the handoff fields an agent acts on, the open diagnostics —
|
|
9
|
+
* is never dropped: a payload that no longer fits reports `fits: false` rather than losing them.
|
|
10
|
+
*/
|
|
11
|
+
export const BUDGET_SECTION_ORDER = ['evidenceExcerpts', 'related', 'neighbours', 'summaries'] as const
|
|
12
|
+
|
|
13
|
+
export const BudgetSectionSchema = z.enum(BUDGET_SECTION_ORDER)
|
|
14
|
+
export type BudgetSection = z.infer<typeof BudgetSectionSchema>
|
|
15
|
+
|
|
16
|
+
export const BudgetReportSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
budgetTokens: z.number().int().positive(),
|
|
19
|
+
tokens: z
|
|
20
|
+
.object({
|
|
21
|
+
total: z.number().int().nonnegative(),
|
|
22
|
+
budget: z.number().int().positive(),
|
|
23
|
+
/** What the payload costs with every droppable section removed. */
|
|
24
|
+
core: z.number().int().nonnegative(),
|
|
25
|
+
sections: z.partialRecord(BudgetSectionSchema, z.number().int().nonnegative()),
|
|
26
|
+
})
|
|
27
|
+
.strict(),
|
|
28
|
+
fits: z.boolean(),
|
|
29
|
+
/** The declared order, so a reader can check that what was dropped is a prefix of it. */
|
|
30
|
+
order: z.array(BudgetSectionSchema).max(8),
|
|
31
|
+
kept: z.array(BudgetSectionSchema).max(8),
|
|
32
|
+
dropped: z.array(BudgetSectionSchema).max(8),
|
|
33
|
+
tokenMethod: z.literal('approximate'),
|
|
34
|
+
})
|
|
35
|
+
.strict()
|
|
36
|
+
|
|
37
|
+
export type BudgetReport = z.infer<typeof BudgetReportSchema>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
3
|
import { AgentHandoffLegacySchema } from './agent-handoff.js'
|
|
4
|
+
import { RETRIEVAL_MAX_ENTRIES, RetrievalIndexV1Schema } from './retrieval-index.js'
|
|
4
5
|
|
|
5
6
|
export const INDEX_SCHEMA_VERSION = 1 as const
|
|
6
7
|
|
|
@@ -26,6 +27,10 @@ export const KnowledgeEntrySchema = z
|
|
|
26
27
|
body: z.string().max(8_000).optional(),
|
|
27
28
|
links: z.array(z.string().min(1).max(512)).max(64).optional(),
|
|
28
29
|
tags: z.array(z.string().min(1).max(64)).max(32).optional(),
|
|
30
|
+
/** Names a module exports. The field an agent's symbol query matches against. */
|
|
31
|
+
symbols: z.array(z.string().min(1).max(128)).max(256).optional(),
|
|
32
|
+
/** Hash of the file this entry was projected from, so a stale entry is detectable per file. */
|
|
33
|
+
contentHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
29
34
|
})
|
|
30
35
|
.strict()
|
|
31
36
|
|
|
@@ -45,6 +50,8 @@ export const OwnershipRecordSchema = z
|
|
|
45
50
|
layer: z.string().min(1).max(32).optional(),
|
|
46
51
|
purpose: z.string().max(1024).optional(),
|
|
47
52
|
checks: z.array(z.string().min(1).max(256)).max(32),
|
|
53
|
+
/** Where `checks` came from, decided where the decision is made rather than guessed later. */
|
|
54
|
+
checksSource: z.enum(['ownership', 'frontmatter', 'package-scripts', 'default']).optional(),
|
|
48
55
|
agentDoc: z.string().min(1).max(512).optional(),
|
|
49
56
|
humanDoc: z.string().min(1).max(512).optional(),
|
|
50
57
|
readme: z.string().min(1).max(512).optional(),
|
|
@@ -83,6 +90,35 @@ export const IndexLookupSchema = z
|
|
|
83
90
|
})
|
|
84
91
|
.strict()
|
|
85
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Fingerprint of the repository files the index was built from. Freshness can then be checked by
|
|
95
|
+
* re-hashing the inputs instead of rebuilding the whole index on every query.
|
|
96
|
+
*/
|
|
97
|
+
export const RepositoryInputsSchema = z
|
|
98
|
+
.object({
|
|
99
|
+
hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
100
|
+
fileCount: z.number().int().nonnegative().max(1_000_000),
|
|
101
|
+
projectionVersion: z.number().int().nonnegative().max(1_000),
|
|
102
|
+
/** A safety limit stopped the walk, so the projection does not cover the whole repository. */
|
|
103
|
+
incomplete: z.boolean().optional(),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* How this index was prepared for ranking. Recorded so a ranking is reproducible: a different
|
|
109
|
+
* stopword list or a different field weight produces a different artifact, visibly.
|
|
110
|
+
*/
|
|
111
|
+
export const RetrievalMetadataSchema = z
|
|
112
|
+
.object({
|
|
113
|
+
lexiconVersion: z.number().int().nonnegative().max(1_000),
|
|
114
|
+
weights: z.record(z.string().min(1).max(64), z.number().min(0).max(1_000)),
|
|
115
|
+
params: z
|
|
116
|
+
.object({ k1: z.number().min(0).max(100), b: z.number().min(0).max(1) })
|
|
117
|
+
.strict()
|
|
118
|
+
.optional(),
|
|
119
|
+
})
|
|
120
|
+
.strict()
|
|
121
|
+
|
|
86
122
|
export const DocBridgeIndexV1Schema = z
|
|
87
123
|
.object({
|
|
88
124
|
schemaVersion: z.literal(INDEX_SCHEMA_VERSION),
|
|
@@ -97,12 +133,27 @@ export const DocBridgeIndexV1Schema = z
|
|
|
97
133
|
.strict()
|
|
98
134
|
.optional(),
|
|
99
135
|
properties: z.array(EcosystemPropertySchema).max(16).optional(),
|
|
100
|
-
|
|
136
|
+
/*
|
|
137
|
+
* `knowledge[]` and `projection.entries` describe the same entries, so they carry the same
|
|
138
|
+
* bound. They did not: a monorepo projecting eleven thousand entries produced an index the
|
|
139
|
+
* builder wrote and `parseDocBridgeIndex` refused, which made `doctor`, `search` and the MCP
|
|
140
|
+
* server fail on a repository `index` had reported building successfully.
|
|
141
|
+
*/
|
|
142
|
+
knowledge: z.array(KnowledgeEntrySchema).max(RETRIEVAL_MAX_ENTRIES),
|
|
101
143
|
capabilities: z.array(CapabilityRefSchema).max(5_000).optional(),
|
|
102
144
|
handoffs: z.record(z.string().min(1).max(256), AgentHandoffLegacySchema).optional(),
|
|
103
145
|
lookup: IndexLookupSchema.optional(),
|
|
146
|
+
inputs: RepositoryInputsSchema.optional(),
|
|
147
|
+
retrieval: RetrievalMetadataSchema.optional(),
|
|
148
|
+
/**
|
|
149
|
+
* The retrieval projection of the snapshot: what ranking reads. `knowledge[]` stays for every
|
|
150
|
+
* reader that predates it, and the two describe the same entries.
|
|
151
|
+
*/
|
|
152
|
+
projection: RetrievalIndexV1Schema.optional(),
|
|
104
153
|
})
|
|
105
154
|
.strict()
|
|
106
155
|
|
|
107
156
|
export type DocBridgeIndexV1 = z.infer<typeof DocBridgeIndexV1Schema>
|
|
108
|
-
export type KnowledgeEntry = z.infer<typeof KnowledgeEntrySchema>
|
|
157
|
+
export type KnowledgeEntry = z.infer<typeof KnowledgeEntrySchema>
|
|
158
|
+
export type RepositoryInputs = z.infer<typeof RepositoryInputsSchema>
|
|
159
|
+
export type RetrievalMetadata = z.infer<typeof RetrievalMetadataSchema>
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { sha256NormalizedV1 } from '../index-builder/content-hash.js'
|
|
4
|
+
import { EvidenceSchema, ProjectIdentitySchema, type Evidence } from './knowledge.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Typed enrichment proposals and the overlay that stores what became of them.
|
|
8
|
+
*
|
|
9
|
+
* `AgentProposalV1` could say "review this finding"; it could not say that a document is
|
|
10
|
+
* canonical for an area, that an alias should resolve to an entity, or that a relation exists
|
|
11
|
+
* with a given confidence. Each of those is a different claim with a different validator and a
|
|
12
|
+
* different acceptance policy, so each is its own kind here, and the union is closed: a kind
|
|
13
|
+
* this file does not know is rejected as `invalid-kind` rather than carried along.
|
|
14
|
+
*
|
|
15
|
+
* Nothing in an overlay is authority. An accepted entry is layered over the observed snapshot
|
|
16
|
+
* at projection time, bound to the content hash of the entity it describes, and expires the
|
|
17
|
+
* moment that hash moves. No entry can delete or alter an observed fact.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const ENRICHMENT_SCHEMA_VERSION = 1 as const
|
|
21
|
+
|
|
22
|
+
const hash = z.string().regex(/^[a-f0-9]{64}$/)
|
|
23
|
+
const boundedString = (max: number) => z.string().min(1).max(max)
|
|
24
|
+
const entityRef = boundedString(256)
|
|
25
|
+
/** A BCP 47-shaped language tag: `en`, `pt-BR`. Presence is what the validators check; this keeps it a tag. */
|
|
26
|
+
const languageTag = z.string().regex(/^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/)
|
|
27
|
+
|
|
28
|
+
export const ENRICHMENT_KINDS = [
|
|
29
|
+
'classify-document',
|
|
30
|
+
'summarize',
|
|
31
|
+
'add-alias',
|
|
32
|
+
'add-intent',
|
|
33
|
+
'mark-canonical',
|
|
34
|
+
'propose-relation',
|
|
35
|
+
'flag-contradiction',
|
|
36
|
+
'flag-redundancy',
|
|
37
|
+
'flag-gap',
|
|
38
|
+
'rank-hint',
|
|
39
|
+
'suggest-area',
|
|
40
|
+
] as const
|
|
41
|
+
export type EnrichmentKind = (typeof ENRICHMENT_KINDS)[number]
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* What acceptance means per kind.
|
|
45
|
+
*
|
|
46
|
+
* `policy`: low-risk, reversible, and expiring with its target — accepted by the validators.
|
|
47
|
+
* `human`: structural — a relation, a canonical marker, a conflict — held for a person.
|
|
48
|
+
* `finding`: accepted by policy, but only ever as a finding to review, never as a fact the
|
|
49
|
+
* projection ranks on.
|
|
50
|
+
*/
|
|
51
|
+
export type EnrichmentPolicy = 'policy' | 'human' | 'finding'
|
|
52
|
+
|
|
53
|
+
export const ENRICHMENT_POLICY: Readonly<Record<EnrichmentKind, EnrichmentPolicy>> = {
|
|
54
|
+
'classify-document': 'policy',
|
|
55
|
+
summarize: 'policy',
|
|
56
|
+
'add-alias': 'policy',
|
|
57
|
+
'add-intent': 'policy',
|
|
58
|
+
'mark-canonical': 'human',
|
|
59
|
+
'propose-relation': 'human',
|
|
60
|
+
'flag-contradiction': 'human',
|
|
61
|
+
'flag-redundancy': 'human',
|
|
62
|
+
'flag-gap': 'finding',
|
|
63
|
+
'rank-hint': 'policy',
|
|
64
|
+
'suggest-area': 'human',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const isEnrichmentKind = (value: unknown): value is EnrichmentKind =>
|
|
68
|
+
typeof value === 'string' && (ENRICHMENT_KINDS as readonly string[]).includes(value)
|
|
69
|
+
|
|
70
|
+
/** Relation kinds an agent may propose. Structural kinds the analyzers observe are not for an agent to assert. */
|
|
71
|
+
export const PROPOSABLE_RELATION_KINDS = ['covers', 'mentions', 'links-to', 'depends-on', 'related-to', 'documents'] as const
|
|
72
|
+
|
|
73
|
+
export const EnrichmentOriginSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
agentId: boundedString(256),
|
|
76
|
+
agentVersion: boundedString(64),
|
|
77
|
+
promptVersion: boundedString(64),
|
|
78
|
+
model: boundedString(256).optional(),
|
|
79
|
+
provider: boundedString(128).optional(),
|
|
80
|
+
})
|
|
81
|
+
.strict()
|
|
82
|
+
export type EnrichmentOrigin = z.infer<typeof EnrichmentOriginSchema>
|
|
83
|
+
|
|
84
|
+
export const DocumentTypeSchema = z.enum(['guide', 'reference', 'runbook', 'adr', 'spec', 'index', 'changelog', 'policy', 'other'])
|
|
85
|
+
export const DocumentAudienceSchema = z.enum(['agent', 'human', 'human-and-agent'])
|
|
86
|
+
export const DocumentLifecycleSchema = z.enum(['draft', 'active', 'deprecated', 'archived'])
|
|
87
|
+
export const DocumentCriticalitySchema = z.enum(['tier-0', 'tier-1', 'tier-2'])
|
|
88
|
+
|
|
89
|
+
export const SUMMARY_MAX = 400
|
|
90
|
+
export const ALIAS_MAX = 64
|
|
91
|
+
export const INTENT_MAX = 120
|
|
92
|
+
|
|
93
|
+
const payloads = {
|
|
94
|
+
'classify-document': z
|
|
95
|
+
.object({ type: DocumentTypeSchema, audience: DocumentAudienceSchema, lifecycle: DocumentLifecycleSchema, criticality: DocumentCriticalitySchema })
|
|
96
|
+
.strict(),
|
|
97
|
+
summarize: z.object({ summary: boundedString(SUMMARY_MAX), language: languageTag }).strict(),
|
|
98
|
+
'add-alias': z.object({ alias: boundedString(ALIAS_MAX) }).strict(),
|
|
99
|
+
'add-intent': z.object({ phrase: boundedString(INTENT_MAX), language: languageTag }).strict(),
|
|
100
|
+
'mark-canonical': z.object({ scope: entityRef }).strict(),
|
|
101
|
+
'propose-relation': z
|
|
102
|
+
.object({ from: entityRef, to: entityRef, kind: z.enum(PROPOSABLE_RELATION_KINDS), detection: boundedString(512) })
|
|
103
|
+
.strict(),
|
|
104
|
+
'flag-contradiction': z.object({ against: entityRef, claim: boundedString(1_000), observed: boundedString(1_000) }).strict(),
|
|
105
|
+
'flag-redundancy': z.object({ with: entityRef }).strict(),
|
|
106
|
+
'flag-gap': z.object({ area: entityRef, missing: boundedString(1_000) }).strict(),
|
|
107
|
+
'rank-hint': z.object({ relevance: z.enum(['strong', 'weak']) }).strict(),
|
|
108
|
+
'suggest-area': z.object({ directories: z.array(boundedString(512)).min(1).max(32), name: boundedString(128) }).strict(),
|
|
109
|
+
} as const
|
|
110
|
+
|
|
111
|
+
export type EnrichmentPayloads = { readonly [K in EnrichmentKind]: z.infer<(typeof payloads)[K]> }
|
|
112
|
+
|
|
113
|
+
const envelope = {
|
|
114
|
+
type: z.literal('enrichment-proposal'),
|
|
115
|
+
schemaVersion: z.literal(ENRICHMENT_SCHEMA_VERSION),
|
|
116
|
+
/** Content-derived: see `enrichmentProposalId`. A re-run over the same inputs produces the same id. */
|
|
117
|
+
proposalId: hash,
|
|
118
|
+
/** The entity the claim is about. It must exist in the snapshot the proposal was made against. */
|
|
119
|
+
entity: entityRef,
|
|
120
|
+
/** The entity's content hash when the proposal was made. The entry expires when this moves. */
|
|
121
|
+
targetContentHash: hash,
|
|
122
|
+
confidence: z.number().min(0).max(1),
|
|
123
|
+
reason: boundedString(1_000),
|
|
124
|
+
/** At least one item, and every item must be present in the supplied snapshot or report. */
|
|
125
|
+
evidence: z.array(EvidenceSchema).min(1).max(32),
|
|
126
|
+
relatedDiagnosticIds: z.array(boundedString(256)).max(32).optional(),
|
|
127
|
+
origin: EnrichmentOriginSchema,
|
|
128
|
+
baseSnapshotHash: hash,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const proposalOf = <K extends EnrichmentKind>(kind: K) => z.object({ ...envelope, kind: z.literal(kind), payload: payloads[kind] }).strict()
|
|
132
|
+
|
|
133
|
+
export const EnrichmentProposalV1Schema = z.discriminatedUnion('kind', [
|
|
134
|
+
proposalOf('classify-document'),
|
|
135
|
+
proposalOf('summarize'),
|
|
136
|
+
proposalOf('add-alias'),
|
|
137
|
+
proposalOf('add-intent'),
|
|
138
|
+
proposalOf('mark-canonical'),
|
|
139
|
+
proposalOf('propose-relation'),
|
|
140
|
+
proposalOf('flag-contradiction'),
|
|
141
|
+
proposalOf('flag-redundancy'),
|
|
142
|
+
proposalOf('flag-gap'),
|
|
143
|
+
proposalOf('rank-hint'),
|
|
144
|
+
proposalOf('suggest-area'),
|
|
145
|
+
])
|
|
146
|
+
export type EnrichmentProposalV1 = z.infer<typeof EnrichmentProposalV1Schema>
|
|
147
|
+
export type EnrichmentProposalOf<K extends EnrichmentKind> = Extract<EnrichmentProposalV1, { kind: K }>
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The part of a payload that makes two proposals of one kind about one entity different things.
|
|
151
|
+
*
|
|
152
|
+
* A summary, a classification, a canonical marker and a rank hint are slots: one per entity per
|
|
153
|
+
* agent and prompt, so a re-run replaces rather than accumulates. An alias, an intent, a relation
|
|
154
|
+
* or a flag can legitimately exist several times for one entity, so the field that tells them
|
|
155
|
+
* apart is part of the identity.
|
|
156
|
+
*/
|
|
157
|
+
export const enrichmentProposalKey = (kind: EnrichmentKind, payload: unknown): unknown => {
|
|
158
|
+
const record = (payload && typeof payload === 'object' ? payload : {}) as Record<string, unknown>
|
|
159
|
+
switch (kind) {
|
|
160
|
+
case 'add-alias':
|
|
161
|
+
return { alias: record.alias }
|
|
162
|
+
case 'add-intent':
|
|
163
|
+
return { phrase: record.phrase, language: record.language }
|
|
164
|
+
case 'propose-relation':
|
|
165
|
+
return { from: record.from, to: record.to, kind: record.kind }
|
|
166
|
+
case 'flag-contradiction':
|
|
167
|
+
return { against: record.against }
|
|
168
|
+
case 'flag-redundancy':
|
|
169
|
+
return { with: record.with }
|
|
170
|
+
case 'flag-gap':
|
|
171
|
+
return { area: record.area, missing: record.missing }
|
|
172
|
+
case 'suggest-area':
|
|
173
|
+
return { directories: record.directories }
|
|
174
|
+
default:
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export type EnrichmentIdentity = {
|
|
180
|
+
readonly kind: EnrichmentKind
|
|
181
|
+
readonly entity: string
|
|
182
|
+
readonly targetContentHash: string
|
|
183
|
+
readonly origin: Pick<EnrichmentOrigin, 'agentId' | 'promptVersion'>
|
|
184
|
+
readonly payload?: unknown
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The proposal id: kind, entity, target hash, agent identity, prompt version — and the payload key. */
|
|
188
|
+
export const enrichmentProposalId = (identity: EnrichmentIdentity): string =>
|
|
189
|
+
sha256NormalizedV1({
|
|
190
|
+
kind: identity.kind,
|
|
191
|
+
entity: identity.entity,
|
|
192
|
+
targetContentHash: identity.targetContentHash,
|
|
193
|
+
agentId: identity.origin.agentId,
|
|
194
|
+
promptVersion: identity.origin.promptVersion,
|
|
195
|
+
key: enrichmentProposalKey(identity.kind, identity.payload),
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
/** Why a proposal was not accepted. A closed list, so the histogram in `stats` is comparable across runs. */
|
|
199
|
+
export const ENRICHMENT_REJECTION_REASONS = [
|
|
200
|
+
'invalid-kind',
|
|
201
|
+
'schema',
|
|
202
|
+
'no-evidence',
|
|
203
|
+
'unknown-entity',
|
|
204
|
+
'stale-target',
|
|
205
|
+
'base-snapshot-mismatch',
|
|
206
|
+
'unknown-diagnostic',
|
|
207
|
+
'evidence-outside-artifacts',
|
|
208
|
+
'proposal-id-mismatch',
|
|
209
|
+
'entity-outside-pack',
|
|
210
|
+
'entity-kind',
|
|
211
|
+
'redaction',
|
|
212
|
+
'summary-unchanged',
|
|
213
|
+
'alias-collision',
|
|
214
|
+
'unknown-scope',
|
|
215
|
+
'canonical-conflict',
|
|
216
|
+
'unknown-endpoint',
|
|
217
|
+
'relation-already-observed',
|
|
218
|
+
'evidence-outside-endpoints',
|
|
219
|
+
'evidence-missing-for-endpoint',
|
|
220
|
+
'already-duplicate',
|
|
221
|
+
'already-covered',
|
|
222
|
+
'unknown-directory',
|
|
223
|
+
'area-overlap',
|
|
224
|
+
'self-approval',
|
|
225
|
+
'self-adjudication',
|
|
226
|
+
'adjudicated',
|
|
227
|
+
'human-rejected',
|
|
228
|
+
'expired',
|
|
229
|
+
] as const
|
|
230
|
+
export type EnrichmentRejectionReason = (typeof ENRICHMENT_REJECTION_REASONS)[number]
|
|
231
|
+
|
|
232
|
+
export const AcceptedEnrichmentSchema = z
|
|
233
|
+
.object({
|
|
234
|
+
proposal: EnrichmentProposalV1Schema,
|
|
235
|
+
acceptedAt: z.string().datetime(),
|
|
236
|
+
/** `policy` for accept-by-policy kinds; otherwise the person who approved it. Never an agent. */
|
|
237
|
+
acceptedBy: boundedString(256),
|
|
238
|
+
approvalId: hash.optional(),
|
|
239
|
+
})
|
|
240
|
+
.strict()
|
|
241
|
+
.superRefine((value, context) => {
|
|
242
|
+
const policy = ENRICHMENT_POLICY[value.proposal.kind]
|
|
243
|
+
if (value.acceptedBy === 'policy' && policy === 'human') {
|
|
244
|
+
context.addIssue({ code: z.ZodIssueCode.custom, path: ['acceptedBy'], message: `${value.proposal.kind} requires human approval; acceptedBy cannot be "policy"` })
|
|
245
|
+
}
|
|
246
|
+
if (value.acceptedBy === value.proposal.origin.agentId) {
|
|
247
|
+
context.addIssue({ code: z.ZodIssueCode.custom, path: ['acceptedBy'], message: 'a proposal cannot be accepted by its own author' })
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
export type AcceptedEnrichment = z.infer<typeof AcceptedEnrichmentSchema>
|
|
251
|
+
|
|
252
|
+
export const PendingEnrichmentSchema = z
|
|
253
|
+
.object({
|
|
254
|
+
proposal: EnrichmentProposalV1Schema,
|
|
255
|
+
/** The approval record this entry waits on: the hash of the proposal id and the target hash. */
|
|
256
|
+
approvalId: hash,
|
|
257
|
+
/** Why it is more than merely pending: a canonical conflict, a disputed contradiction. */
|
|
258
|
+
note: z.enum(['canonical-conflict', 'disputed-contradiction']).optional(),
|
|
259
|
+
})
|
|
260
|
+
.strict()
|
|
261
|
+
export type PendingEnrichment = z.infer<typeof PendingEnrichmentSchema>
|
|
262
|
+
|
|
263
|
+
export const RejectedEnrichmentSchema = z
|
|
264
|
+
.object({
|
|
265
|
+
proposalId: boundedString(128),
|
|
266
|
+
kind: boundedString(128),
|
|
267
|
+
entity: boundedString(256).optional(),
|
|
268
|
+
reason: z.enum(ENRICHMENT_REJECTION_REASONS),
|
|
269
|
+
detail: z.string().max(1_024).optional(),
|
|
270
|
+
origin: EnrichmentOriginSchema.optional(),
|
|
271
|
+
})
|
|
272
|
+
.strict()
|
|
273
|
+
export type RejectedEnrichment = z.infer<typeof RejectedEnrichmentSchema>
|
|
274
|
+
|
|
275
|
+
const count = z.number().int().nonnegative()
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* A rejection that means the agent named something that does not exist.
|
|
279
|
+
*
|
|
280
|
+
* It is counted on its own rather than folded into the rejection total, because it is the one
|
|
281
|
+
* number that says whether an agent is inventing structure. A curator that classifies a document
|
|
282
|
+
* badly is wrong about a judgement; one that proposes a relation to a module the repository does
|
|
283
|
+
* not contain is making things up, and that number must trend to zero or the agent is unusable.
|
|
284
|
+
*/
|
|
285
|
+
export const INVENTED_RELATION_REASONS = ['unknown-endpoint', 'unknown-entity', 'unknown-scope', 'unknown-directory', 'unknown-diagnostic'] as const
|
|
286
|
+
|
|
287
|
+
export const EnrichmentStatsSchema = z
|
|
288
|
+
.object({
|
|
289
|
+
byKind: z.record(z.string().max(128), z.object({ proposed: count, accepted: count, pending: count, rejected: count }).strict()),
|
|
290
|
+
rejectionReasons: z.record(z.string().max(128), count),
|
|
291
|
+
/** Rejections that named a non-existent entity, endpoint, scope, directory or diagnostic. */
|
|
292
|
+
inventedReferences: count,
|
|
293
|
+
agentRuns: count,
|
|
294
|
+
cacheHits: count,
|
|
295
|
+
/** Cache hits over cache lookups, rounded to six places. 1 means the run asked no agent anything. */
|
|
296
|
+
cacheHitRate: z.number().min(0).max(1),
|
|
297
|
+
packs: count,
|
|
298
|
+
inputBytes: count,
|
|
299
|
+
outputBytes: count,
|
|
300
|
+
/** Wall time of the run. Outside the overlay's content hash, like every other cost figure. */
|
|
301
|
+
wallTimeMs: count,
|
|
302
|
+
expired: count,
|
|
303
|
+
})
|
|
304
|
+
.strict()
|
|
305
|
+
export type EnrichmentStats = z.infer<typeof EnrichmentStatsSchema>
|
|
306
|
+
|
|
307
|
+
export const EnrichmentOverlayV1Schema = z
|
|
308
|
+
.object({
|
|
309
|
+
type: z.literal('enrichment-overlay'),
|
|
310
|
+
schemaVersion: z.literal(ENRICHMENT_SCHEMA_VERSION),
|
|
311
|
+
contentHash: hash,
|
|
312
|
+
contentHashAlgo: z.literal('sha256-normalized-v1'),
|
|
313
|
+
project: ProjectIdentitySchema,
|
|
314
|
+
sourceRevision: boundedString(128),
|
|
315
|
+
sourceRevisionKind: z.enum(['git', 'content']),
|
|
316
|
+
configurationHash: hash,
|
|
317
|
+
pipelineVersion: boundedString(64),
|
|
318
|
+
analyzerVersions: z.record(boundedString(128), boundedString(64)),
|
|
319
|
+
baseSnapshotHash: hash,
|
|
320
|
+
accepted: z.array(AcceptedEnrichmentSchema).max(10_000),
|
|
321
|
+
pending: z.array(PendingEnrichmentSchema).max(10_000),
|
|
322
|
+
rejected: z.array(RejectedEnrichmentSchema).max(10_000),
|
|
323
|
+
stats: EnrichmentStatsSchema,
|
|
324
|
+
})
|
|
325
|
+
.strict()
|
|
326
|
+
export type EnrichmentOverlayV1 = z.infer<typeof EnrichmentOverlayV1Schema>
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* The overlay's own hash, over what it decided and not over when or at what cost.
|
|
330
|
+
*
|
|
331
|
+
* `acceptedAt` is a fact about a person's action, and `stats` describe the run that produced the
|
|
332
|
+
* file — how many agent calls, how many cache hits. Neither is a fact about the repository, and
|
|
333
|
+
* two runs over one unchanged repository must produce one overlay hash, so both are outside it.
|
|
334
|
+
* What is inside: every accepted, pending and rejected entry and the hashes they bind to.
|
|
335
|
+
*/
|
|
336
|
+
export const enrichmentOverlayContentHash = (overlay: Omit<EnrichmentOverlayV1, 'contentHash'> & { readonly contentHash?: string }): string => {
|
|
337
|
+
const { contentHash: _contentHash, stats: _stats, ...rest } = overlay
|
|
338
|
+
return sha256NormalizedV1({ ...rest, accepted: rest.accepted.map(({ acceptedAt: _acceptedAt, ...entry }) => entry) })
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* An adjudication: a third agent's verdict on proposals two others could not settle — a canonical
|
|
343
|
+
* conflict, a disputed contradiction. It judges; it never approves. The agent that made a
|
|
344
|
+
* proposal cannot be the agent that judges it, which the validator enforces by identity.
|
|
345
|
+
*/
|
|
346
|
+
export const EnrichmentAdjudicationV1Schema = z
|
|
347
|
+
.object({
|
|
348
|
+
type: z.literal('enrichment-adjudication'),
|
|
349
|
+
schemaVersion: z.literal(ENRICHMENT_SCHEMA_VERSION),
|
|
350
|
+
adjudicationId: hash,
|
|
351
|
+
/** The proposals judged, by id. Every one must be pending. */
|
|
352
|
+
judges: z.array(hash).min(1).max(16),
|
|
353
|
+
/** Which of the judged proposals survive; the rest are rejected as `adjudicated`. */
|
|
354
|
+
keep: z.array(hash).max(16),
|
|
355
|
+
reason: boundedString(1_000),
|
|
356
|
+
origin: EnrichmentOriginSchema,
|
|
357
|
+
})
|
|
358
|
+
.strict()
|
|
359
|
+
.superRefine((value, context) => {
|
|
360
|
+
if (value.keep.some((id) => !value.judges.includes(id))) {
|
|
361
|
+
context.addIssue({ code: z.ZodIssueCode.custom, path: ['keep'], message: 'keep may only name judged proposals' })
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
export type EnrichmentAdjudicationV1 = z.infer<typeof EnrichmentAdjudicationV1Schema>
|
|
365
|
+
|
|
366
|
+
export const enrichmentAdjudicationId = (input: Pick<EnrichmentAdjudicationV1, 'judges' | 'keep' | 'origin'>): string =>
|
|
367
|
+
sha256NormalizedV1({ judges: [...input.judges].sort(), keep: [...input.keep].sort(), agentId: input.origin.agentId, promptVersion: input.origin.promptVersion })
|
|
368
|
+
|
|
369
|
+
export type { Evidence as EnrichmentEvidence }
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RETRIEVAL_MAX_ENTRIES } from './retrieval-index.js'
|
|
2
|
+
|
|
1
3
|
type JsonSchema = {
|
|
2
4
|
readonly [key: string]: unknown
|
|
3
5
|
}
|
|
@@ -35,7 +37,7 @@ export const AgentHandoffV1JsonSchema = {
|
|
|
35
37
|
required: ['type', 'id'],
|
|
36
38
|
properties: {
|
|
37
39
|
type: {
|
|
38
|
-
enum: ['package', 'module', 'app', 'screen', 'flow', 'component', 'intent', 'change', 'search'],
|
|
40
|
+
enum: ['package', 'area', 'module', 'document', 'app', 'screen', 'flow', 'component', 'intent', 'change', 'search'],
|
|
39
41
|
},
|
|
40
42
|
id: { type: 'string', minLength: 1, maxLength: 256 },
|
|
41
43
|
path: { type: 'string', minLength: 1, maxLength: 512 },
|
|
@@ -50,6 +52,41 @@ export const AgentHandoffV1JsonSchema = {
|
|
|
50
52
|
humanDoc: { anyOf: [{ type: 'string', minLength: 1, maxLength: 512 }, { type: 'null' }] },
|
|
51
53
|
playbookPatterns: { type: 'array', items: { type: 'string', format: 'uri' }, maxItems: 16 },
|
|
52
54
|
notes: stringArray(16),
|
|
55
|
+
related: {
|
|
56
|
+
type: 'array',
|
|
57
|
+
maxItems: 16,
|
|
58
|
+
items: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
required: ['id', 'path', 'direction', 'strength', 'evidence'],
|
|
62
|
+
properties: {
|
|
63
|
+
id: { type: 'string', minLength: 1, maxLength: 256 },
|
|
64
|
+
path: { type: 'string', minLength: 1, maxLength: 512 },
|
|
65
|
+
direction: { enum: ['imports', 'imported-by'] },
|
|
66
|
+
strength: { type: 'integer', minimum: 1 },
|
|
67
|
+
evidence: stringArray(8),
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
explain: { type: 'object', additionalProperties: stringArray(16) },
|
|
72
|
+
evidence: {
|
|
73
|
+
type: 'array',
|
|
74
|
+
maxItems: 32,
|
|
75
|
+
items: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
required: ['source', 'path'],
|
|
79
|
+
properties: {
|
|
80
|
+
source: { enum: ['code', 'configuration', 'documentation', 'agent', 'derived'] },
|
|
81
|
+
path: { type: 'string', minLength: 1, maxLength: 512 },
|
|
82
|
+
lineStart: { type: 'integer', minimum: 1 },
|
|
83
|
+
lineEnd: { type: 'integer', minimum: 1 },
|
|
84
|
+
contentHash: { type: 'string', pattern: '^[a-f0-9]{64}$' },
|
|
85
|
+
context: { type: 'string', maxLength: 1024 },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
metadata: { type: 'object' },
|
|
53
90
|
},
|
|
54
91
|
} as const satisfies JsonSchema
|
|
55
92
|
|
|
@@ -111,7 +148,7 @@ export const DocBridgeIndexV1JsonSchema = {
|
|
|
111
148
|
},
|
|
112
149
|
knowledge: {
|
|
113
150
|
type: 'array',
|
|
114
|
-
maxItems:
|
|
151
|
+
maxItems: RETRIEVAL_MAX_ENTRIES,
|
|
115
152
|
items: {
|
|
116
153
|
type: 'object',
|
|
117
154
|
additionalProperties: false,
|