@descryy/core 0.1.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/LICENSE +21 -0
- package/dist/capabilities/index.d.ts +3 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +2 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/preconditions.d.ts +99 -0
- package/dist/capabilities/preconditions.d.ts.map +1 -0
- package/dist/capabilities/preconditions.js +94 -0
- package/dist/capabilities/preconditions.js.map +1 -0
- package/dist/contracts/engine.d.ts +90 -0
- package/dist/contracts/engine.d.ts.map +1 -0
- package/dist/contracts/engine.js +291 -0
- package/dist/contracts/engine.js.map +1 -0
- package/dist/contracts/index.d.ts +9 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/orphans.d.ts +96 -0
- package/dist/contracts/orphans.d.ts.map +1 -0
- package/dist/contracts/orphans.js +241 -0
- package/dist/contracts/orphans.js.map +1 -0
- package/dist/contracts/paths.d.ts +132 -0
- package/dist/contracts/paths.d.ts.map +1 -0
- package/dist/contracts/paths.js +245 -0
- package/dist/contracts/paths.js.map +1 -0
- package/dist/contracts/shapes.d.ts +99 -0
- package/dist/contracts/shapes.d.ts.map +1 -0
- package/dist/contracts/shapes.js +196 -0
- package/dist/contracts/shapes.js.map +1 -0
- package/dist/governance/budget.d.ts +89 -0
- package/dist/governance/budget.d.ts.map +1 -0
- package/dist/governance/budget.js +101 -0
- package/dist/governance/budget.js.map +1 -0
- package/dist/governance/candidate-boundary.d.ts +49 -0
- package/dist/governance/candidate-boundary.d.ts.map +1 -0
- package/dist/governance/candidate-boundary.js +51 -0
- package/dist/governance/candidate-boundary.js.map +1 -0
- package/dist/governance/escalation.d.ts +63 -0
- package/dist/governance/escalation.d.ts.map +1 -0
- package/dist/governance/escalation.js +72 -0
- package/dist/governance/escalation.js.map +1 -0
- package/dist/governance/fact-boundary.d.ts +77 -0
- package/dist/governance/fact-boundary.d.ts.map +1 -0
- package/dist/governance/fact-boundary.js +91 -0
- package/dist/governance/fact-boundary.js.map +1 -0
- package/dist/governance/finding-funnel.d.ts +109 -0
- package/dist/governance/finding-funnel.d.ts.map +1 -0
- package/dist/governance/finding-funnel.js +137 -0
- package/dist/governance/finding-funnel.js.map +1 -0
- package/dist/governance/index.d.ts +11 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +6 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/graph/build.d.ts +127 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +0 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/cross-language.d.ts +70 -0
- package/dist/graph/cross-language.d.ts.map +1 -0
- package/dist/graph/cross-language.js +104 -0
- package/dist/graph/cross-language.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/merge.d.ts +95 -0
- package/dist/graph/merge.d.ts.map +1 -0
- package/dist/graph/merge.js +131 -0
- package/dist/graph/merge.js.map +1 -0
- package/dist/graph/persist.d.ts +42 -0
- package/dist/graph/persist.d.ts.map +1 -0
- package/dist/graph/persist.js +52 -0
- package/dist/graph/persist.js.map +1 -0
- package/dist/graph/runtime-confirmation.d.ts +169 -0
- package/dist/graph/runtime-confirmation.d.ts.map +1 -0
- package/dist/graph/runtime-confirmation.js +318 -0
- package/dist/graph/runtime-confirmation.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/index.d.ts +5 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +3 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/mint.d.ts +81 -0
- package/dist/ledger/mint.d.ts.map +1 -0
- package/dist/ledger/mint.js +138 -0
- package/dist/ledger/mint.js.map +1 -0
- package/dist/ledger/resolve.d.ts +124 -0
- package/dist/ledger/resolve.d.ts.map +1 -0
- package/dist/ledger/resolve.js +290 -0
- package/dist/ledger/resolve.js.map +1 -0
- package/dist/multipr/fingerprint.d.ts +56 -0
- package/dist/multipr/fingerprint.d.ts.map +1 -0
- package/dist/multipr/fingerprint.js +169 -0
- package/dist/multipr/fingerprint.js.map +1 -0
- package/dist/multipr/hidden-dependency.d.ts +120 -0
- package/dist/multipr/hidden-dependency.d.ts.map +1 -0
- package/dist/multipr/hidden-dependency.js +185 -0
- package/dist/multipr/hidden-dependency.js.map +1 -0
- package/dist/multipr/index.d.ts +14 -0
- package/dist/multipr/index.d.ts.map +1 -0
- package/dist/multipr/index.js +8 -0
- package/dist/multipr/index.js.map +1 -0
- package/dist/multipr/mechanical.d.ts +239 -0
- package/dist/multipr/mechanical.d.ts.map +1 -0
- package/dist/multipr/mechanical.js +333 -0
- package/dist/multipr/mechanical.js.map +1 -0
- package/dist/multipr/migration-heads.d.ts +160 -0
- package/dist/multipr/migration-heads.d.ts.map +1 -0
- package/dist/multipr/migration-heads.js +212 -0
- package/dist/multipr/migration-heads.js.map +1 -0
- package/dist/multipr/overlap.d.ts +186 -0
- package/dist/multipr/overlap.d.ts.map +1 -0
- package/dist/multipr/overlap.js +254 -0
- package/dist/multipr/overlap.js.map +1 -0
- package/dist/multipr/scope-store.d.ts +105 -0
- package/dist/multipr/scope-store.d.ts.map +1 -0
- package/dist/multipr/scope-store.js +182 -0
- package/dist/multipr/scope-store.js.map +1 -0
- package/dist/multipr/superseded.d.ts +133 -0
- package/dist/multipr/superseded.d.ts.map +1 -0
- package/dist/multipr/superseded.js +128 -0
- package/dist/multipr/superseded.js.map +1 -0
- package/dist/query/confirmed-facts.d.ts +56 -0
- package/dist/query/confirmed-facts.d.ts.map +1 -0
- package/dist/query/confirmed-facts.js +64 -0
- package/dist/query/confirmed-facts.js.map +1 -0
- package/dist/query/declared-value-closure.d.ts +126 -0
- package/dist/query/declared-value-closure.d.ts.map +1 -0
- package/dist/query/declared-value-closure.js +156 -0
- package/dist/query/declared-value-closure.js.map +1 -0
- package/dist/query/index.d.ts +28 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +15 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/memory.d.ts +20 -0
- package/dist/query/memory.d.ts.map +1 -0
- package/dist/query/memory.js +55 -0
- package/dist/query/memory.js.map +1 -0
- package/dist/query/provider.d.ts +127 -0
- package/dist/query/provider.d.ts.map +1 -0
- package/dist/query/provider.js +54 -0
- package/dist/query/provider.js.map +1 -0
- package/dist/query/queries.d.ts +119 -0
- package/dist/query/queries.d.ts.map +1 -0
- package/dist/query/queries.js +267 -0
- package/dist/query/queries.js.map +1 -0
- package/dist/query/refusal-fetch.d.ts +114 -0
- package/dist/query/refusal-fetch.d.ts.map +1 -0
- package/dist/query/refusal-fetch.js +200 -0
- package/dist/query/refusal-fetch.js.map +1 -0
- package/dist/query/refusal-questions.d.ts +123 -0
- package/dist/query/refusal-questions.d.ts.map +1 -0
- package/dist/query/refusal-questions.js +122 -0
- package/dist/query/refusal-questions.js.map +1 -0
- package/dist/query/root-cause-score.d.ts +218 -0
- package/dist/query/root-cause-score.d.ts.map +1 -0
- package/dist/query/root-cause-score.js +251 -0
- package/dist/query/root-cause-score.js.map +1 -0
- package/dist/query/row-closure-picture.d.ts +108 -0
- package/dist/query/row-closure-picture.d.ts.map +1 -0
- package/dist/query/row-closure-picture.js +131 -0
- package/dist/query/row-closure-picture.js.map +1 -0
- package/dist/query/similar-incidents.d.ts +76 -0
- package/dist/query/similar-incidents.d.ts.map +1 -0
- package/dist/query/similar-incidents.js +169 -0
- package/dist/query/similar-incidents.js.map +1 -0
- package/dist/query/sqlite.d.ts +13 -0
- package/dist/query/sqlite.d.ts.map +1 -0
- package/dist/query/sqlite.js +54 -0
- package/dist/query/sqlite.js.map +1 -0
- package/dist/query/traverse.d.ts +110 -0
- package/dist/query/traverse.d.ts.map +1 -0
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -0
- package/dist/query/unresolved.d.ts +195 -0
- package/dist/query/unresolved.d.ts.map +1 -0
- package/dist/query/unresolved.js +225 -0
- package/dist/query/unresolved.js.map +1 -0
- package/dist/query/verification-status.d.ts +134 -0
- package/dist/query/verification-status.d.ts.map +1 -0
- package/dist/query/verification-status.js +163 -0
- package/dist/query/verification-status.js.map +1 -0
- package/dist/recording/index.d.ts +26 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +30 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/migrate.d.ts +22 -0
- package/dist/recording/migrate.d.ts.map +1 -0
- package/dist/recording/migrate.js +51 -0
- package/dist/recording/migrate.js.map +1 -0
- package/dist/recording/reader.d.ts +27 -0
- package/dist/recording/reader.d.ts.map +1 -0
- package/dist/recording/reader.js +55 -0
- package/dist/recording/reader.js.map +1 -0
- package/dist/recording/redact.d.ts +63 -0
- package/dist/recording/redact.d.ts.map +1 -0
- package/dist/recording/redact.js +98 -0
- package/dist/recording/redact.js.map +1 -0
- package/dist/recording/schema.d.ts +41 -0
- package/dist/recording/schema.d.ts.map +1 -0
- package/dist/recording/schema.js +104 -0
- package/dist/recording/schema.js.map +1 -0
- package/dist/recording/writer.d.ts +120 -0
- package/dist/recording/writer.d.ts.map +1 -0
- package/dist/recording/writer.js +81 -0
- package/dist/recording/writer.js.map +1 -0
- package/dist/scoping/fanout.d.ts +86 -0
- package/dist/scoping/fanout.d.ts.map +1 -0
- package/dist/scoping/fanout.js +95 -0
- package/dist/scoping/fanout.js.map +1 -0
- package/dist/scoping/index.d.ts +10 -0
- package/dist/scoping/index.d.ts.map +1 -0
- package/dist/scoping/index.js +6 -0
- package/dist/scoping/index.js.map +1 -0
- package/dist/scoping/score.d.ts +83 -0
- package/dist/scoping/score.d.ts.map +1 -0
- package/dist/scoping/score.js +125 -0
- package/dist/scoping/score.js.map +1 -0
- package/dist/scoping/tiers.d.ts +32 -0
- package/dist/scoping/tiers.d.ts.map +1 -0
- package/dist/scoping/tiers.js +35 -0
- package/dist/scoping/tiers.js.map +1 -0
- package/dist/scoping/traverse.d.ts +60 -0
- package/dist/scoping/traverse.d.ts.map +1 -0
- package/dist/scoping/traverse.js +172 -0
- package/dist/scoping/traverse.js.map +1 -0
- package/dist/scoping/weights.d.ts +97 -0
- package/dist/scoping/weights.d.ts.map +1 -0
- package/dist/scoping/weights.js +167 -0
- package/dist/scoping/weights.js.map +1 -0
- package/dist/sources/git/diff.d.ts +53 -0
- package/dist/sources/git/diff.d.ts.map +1 -0
- package/dist/sources/git/diff.js +140 -0
- package/dist/sources/git/diff.js.map +1 -0
- package/dist/sources/git/env.d.ts +2 -0
- package/dist/sources/git/env.d.ts.map +1 -0
- package/dist/sources/git/env.js +21 -0
- package/dist/sources/git/env.js.map +1 -0
- package/dist/sources/git/history.d.ts +140 -0
- package/dist/sources/git/history.d.ts.map +1 -0
- package/dist/sources/git/history.js +314 -0
- package/dist/sources/git/history.js.map +1 -0
- package/dist/sources/git/index.d.ts +7 -0
- package/dist/sources/git/index.d.ts.map +1 -0
- package/dist/sources/git/index.js +4 -0
- package/dist/sources/git/index.js.map +1 -0
- package/dist/sources/git/source.d.ts +95 -0
- package/dist/sources/git/source.d.ts.map +1 -0
- package/dist/sources/git/source.js +0 -0
- package/dist/sources/git/source.js.map +1 -0
- package/dist/sources/migrations/dialects.d.ts +59 -0
- package/dist/sources/migrations/dialects.d.ts.map +1 -0
- package/dist/sources/migrations/dialects.js +84 -0
- package/dist/sources/migrations/dialects.js.map +1 -0
- package/dist/sources/migrations/index.d.ts +3 -0
- package/dist/sources/migrations/index.d.ts.map +1 -0
- package/dist/sources/migrations/index.js +2 -0
- package/dist/sources/migrations/index.js.map +1 -0
- package/dist/sources/migrations/read.d.ts +113 -0
- package/dist/sources/migrations/read.d.ts.map +1 -0
- package/dist/sources/migrations/read.js +270 -0
- package/dist/sources/migrations/read.js.map +1 -0
- package/dist/sources/workspace/index.d.ts +3 -0
- package/dist/sources/workspace/index.d.ts.map +1 -0
- package/dist/sources/workspace/index.js +2 -0
- package/dist/sources/workspace/index.js.map +1 -0
- package/dist/sources/workspace/workspace.d.ts +64 -0
- package/dist/sources/workspace/workspace.d.ts.map +1 -0
- package/dist/sources/workspace/workspace.js +294 -0
- package/dist/sources/workspace/workspace.js.map +1 -0
- package/dist/store/driver/driver.d.ts +82 -0
- package/dist/store/driver/driver.d.ts.map +1 -0
- package/dist/store/driver/driver.js +31 -0
- package/dist/store/driver/driver.js.map +1 -0
- package/dist/store/driver/node-sqlite.d.ts +28 -0
- package/dist/store/driver/node-sqlite.d.ts.map +1 -0
- package/dist/store/driver/node-sqlite.js +188 -0
- package/dist/store/driver/node-sqlite.js.map +1 -0
- package/dist/store/index.d.ts +33 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +46 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/migrate.d.ts +68 -0
- package/dist/store/migrate.d.ts.map +1 -0
- package/dist/store/migrate.js +140 -0
- package/dist/store/migrate.js.map +1 -0
- package/dist/store/reader.d.ts +127 -0
- package/dist/store/reader.d.ts.map +1 -0
- package/dist/store/reader.js +269 -0
- package/dist/store/reader.js.map +1 -0
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +322 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/writer.d.ts +116 -0
- package/dist/store/writer.d.ts.map +1 -0
- package/dist/store/writer.js +279 -0
- package/dist/store/writer.js.map +1 -0
- package/dist/tiers/certify.d.ts +375 -0
- package/dist/tiers/certify.d.ts.map +1 -0
- package/dist/tiers/certify.js +434 -0
- package/dist/tiers/certify.js.map +1 -0
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.d.ts.map +1 -0
- package/dist/tiers/index.js +3 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/tiers/ladder.d.ts +73 -0
- package/dist/tiers/ladder.d.ts.map +1 -0
- package/dist/tiers/ladder.js +197 -0
- package/dist/tiers/ladder.js.map +1 -0
- package/dist/validation/config-graph.d.ts +104 -0
- package/dist/validation/config-graph.d.ts.map +1 -0
- package/dist/validation/config-graph.js +205 -0
- package/dist/validation/config-graph.js.map +1 -0
- package/dist/validation/env.d.ts +243 -0
- package/dist/validation/env.d.ts.map +1 -0
- package/dist/validation/env.js +447 -0
- package/dist/validation/env.js.map +1 -0
- package/dist/validation/index.d.ts +16 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/join-substitution.d.ts +186 -0
- package/dist/validation/join-substitution.d.ts.map +1 -0
- package/dist/validation/join-substitution.js +191 -0
- package/dist/validation/join-substitution.js.map +1 -0
- package/dist/validation/migrations.d.ts +133 -0
- package/dist/validation/migrations.d.ts.map +1 -0
- package/dist/validation/migrations.js +315 -0
- package/dist/validation/migrations.js.map +1 -0
- package/dist/validation/nodes.d.ts +32 -0
- package/dist/validation/nodes.d.ts.map +1 -0
- package/dist/validation/nodes.js +45 -0
- package/dist/validation/nodes.js.map +1 -0
- package/dist/validation/rollback.d.ts +124 -0
- package/dist/validation/rollback.d.ts.map +1 -0
- package/dist/validation/rollback.js +302 -0
- package/dist/validation/rollback.js.map +1 -0
- package/dist/validation/route-drift.d.ts +108 -0
- package/dist/validation/route-drift.d.ts.map +1 -0
- package/dist/validation/route-drift.js +103 -0
- package/dist/validation/route-drift.js.map +1 -0
- package/dist/validation/sources.d.ts +153 -0
- package/dist/validation/sources.d.ts.map +1 -0
- package/dist/validation/sources.js +409 -0
- package/dist/validation/sources.js.map +1 -0
- package/dist/worker/index.d.ts +5 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +3 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/pool.d.ts +73 -0
- package/dist/worker/pool.d.ts.map +1 -0
- package/dist/worker/pool.js +203 -0
- package/dist/worker/pool.js.map +1 -0
- package/dist/worker/protocol.d.ts +91 -0
- package/dist/worker/protocol.d.ts.map +1 -0
- package/dist/worker/protocol.js +19 -0
- package/dist/worker/protocol.js.map +1 -0
- package/dist/worker/traversal.worker.d.ts +25 -0
- package/dist/worker/traversal.worker.d.ts.map +1 -0
- package/dist/worker/traversal.worker.js +124 -0
- package/dist/worker/traversal.worker.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The static half of `verification_status` — `ai-tool-contract.md` §5.6, §8
|
|
3
|
+
* endpoint 13; AI layer §19/§20 (`Verification status` moved off the
|
|
4
|
+
* confidence axis); `ai-layer-plan.md` §2 (the trigger → stage mask), §4
|
|
5
|
+
* technique 6 (a dedicated tool, not appended to every response).
|
|
6
|
+
*
|
|
7
|
+
* ## Scope boundary — read this before assuming more than this module builds
|
|
8
|
+
*
|
|
9
|
+
* A complete `Verification status` also depends on **which pipeline stages
|
|
10
|
+
* ran under which trigger mask** (`ai-layer-plan.md` §2: the fast path skips
|
|
11
|
+
* stages 8-13 entirely) — that is runtime information about one specific
|
|
12
|
+
* run, and it does not exist in `descry-core`. This repo is the static
|
|
13
|
+
* knowledge plane; there is no live run here to ask "did stage 9 actually
|
|
14
|
+
* execute against this file." **This module builds the static half only:**
|
|
15
|
+
* what a static reading of the graph and a language's declared capabilities
|
|
16
|
+
* can determine about analysability and resolution. Combining that with the
|
|
17
|
+
* runtime half — which stages actually ran — is `descry-runtime`/the
|
|
18
|
+
* pipeline's job, not this one's.
|
|
19
|
+
*
|
|
20
|
+
* ## Output shape: `@descryy/ir`'s `VerificationStatus`, not a parallel one
|
|
21
|
+
*
|
|
22
|
+
* `packages/ir/src/finding.ts` already defines the shared shape two
|
|
23
|
+
* repositories (the pipeline and the desktop golden-report test) assert
|
|
24
|
+
* against — `{ exercised: string[], notExercised: UntestedScope[] }`. This
|
|
25
|
+
* module produces exactly that, deliberately, rather than a second
|
|
26
|
+
* "coverage" record: two structures answering the same question would be
|
|
27
|
+
* exactly the kind of divergence `finding.ts`'s own module doc warns about
|
|
28
|
+
* for `Finding` itself, one level down.
|
|
29
|
+
*
|
|
30
|
+
* An `exercised` entry is `"<ref> (R<resolution>)"` — the scope item's
|
|
31
|
+
* identifier plus the real resolution level the graph actually holds for it,
|
|
32
|
+
* because a present node's resolution is a fact worth keeping even though
|
|
33
|
+
* `VerificationStatus.exercised` is typed as bare strings; see
|
|
34
|
+
* {@link formatExercised} for the exact grammar and
|
|
35
|
+
* {@link resolutionOfExercised} for the matching parser. A present item's
|
|
36
|
+
* resolution is reported as-is and is **never filtered against the
|
|
37
|
+
* requested check's `minResolution`** — whether R1 is "enough" for a given
|
|
38
|
+
* claim is a judgement for something above this layer (rule 3's cap, or G4),
|
|
39
|
+
* not a fact this function may assert.
|
|
40
|
+
*
|
|
41
|
+
* ## Hard constraint: no report category, ever
|
|
42
|
+
*
|
|
43
|
+
* §19's five report categories (`confirmed` / `strongly supported` /
|
|
44
|
+
* `unconfirmed` / `not analysable` / `unsupported`) are assigned at G4,
|
|
45
|
+
* above this surface (`ai-tool-contract.md` §1.2 draws the same line for
|
|
46
|
+
* result states). This module returns only the raw coverage facts —
|
|
47
|
+
* reachable-or-not, resolution, a named reason — and never the literal
|
|
48
|
+
* category vocabulary; a test asserts none of the five strings ever appear
|
|
49
|
+
* in the output.
|
|
50
|
+
*
|
|
51
|
+
* ## Reuses `tiers/certify.ts`'s capability comparison, not a second one
|
|
52
|
+
*
|
|
53
|
+
* "Does this language's declared `CapabilityMatrix` cover what a check
|
|
54
|
+
* needs" is the same question `certify.ts`'s `checkCapability` already
|
|
55
|
+
* answers for a whole adapter's tier. {@link capabilityGaps} restates that
|
|
56
|
+
* comparison — declared node/edge types, declared `maxResolution` — at
|
|
57
|
+
* per-scope-item granularity rather than adapter-wide, so the two never
|
|
58
|
+
* quietly disagree about what "this language's capabilities satisfy this
|
|
59
|
+
* requirement" means. It is a restatement rather than an import because
|
|
60
|
+
* `certify.ts`'s function also folds in precision-draw attestation and
|
|
61
|
+
* corpus conformance, both properties of a *published tier*, not of one live
|
|
62
|
+
* scope item — pulling those in here would answer a different question than
|
|
63
|
+
* the one being asked.
|
|
64
|
+
*
|
|
65
|
+
* ## Why this never looks up capabilities by language itself
|
|
66
|
+
*
|
|
67
|
+
* `ScopeItem.capabilities` is supplied by the caller, already resolved, per
|
|
68
|
+
* item — never looked up here from a `language` string. A `Map.get(item.
|
|
69
|
+
* language)` shape inside `packages/core/src/query/` would be exactly the
|
|
70
|
+
* IR-boundary leak `descry-boundary/no-language-branch` exists to catch
|
|
71
|
+
* (architecture principle 7 / `CLAUDE.md` rule 1): deciding behaviour from
|
|
72
|
+
* *which* language a node came from, not merely from whether one is known.
|
|
73
|
+
* `certify.ts` holds the same discipline one layer up — it takes one
|
|
74
|
+
* `GateEvidence` per call and never builds a language-keyed registry inside
|
|
75
|
+
* `packages/core` either. Resolving "which capabilities apply to this
|
|
76
|
+
* item's language" is the caller's job, done above this boundary; `language`
|
|
77
|
+
* is read here only for disclosure (naming it in a reason string), never for
|
|
78
|
+
* a branch, comparison or lookup.
|
|
79
|
+
*/
|
|
80
|
+
/** `certify.ts`'s own `missing()`, restated: what `wanted` names that `have` does not declare. */
|
|
81
|
+
function missing(wanted, have) {
|
|
82
|
+
if (wanted === undefined)
|
|
83
|
+
return [];
|
|
84
|
+
const present = new Set(have);
|
|
85
|
+
return wanted.filter((w) => !present.has(w));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The same comparison `tiers/certify.ts`'s `checkCapability` makes — declared
|
|
89
|
+
* node/edge types, declared resolution ceiling — restated at one scope
|
|
90
|
+
* item's granularity. Empty when `caps` declares everything `check` asks
|
|
91
|
+
* for (which does not, by itself, mean the item was reachable — see the
|
|
92
|
+
* caller in {@link computeVerificationStatus}).
|
|
93
|
+
*/
|
|
94
|
+
function capabilityGaps(caps, check) {
|
|
95
|
+
const gaps = [];
|
|
96
|
+
const nodeGap = missing(check.nodeTypes, caps.nodeTypes);
|
|
97
|
+
if (nodeGap.length > 0)
|
|
98
|
+
gaps.push(`capabilities() declares no ${nodeGap.join(", ")}`);
|
|
99
|
+
const edgeGap = missing(check.edgeTypes, caps.edgeTypes);
|
|
100
|
+
if (edgeGap.length > 0)
|
|
101
|
+
gaps.push(`capabilities() declares no ${edgeGap.join(", ")}`);
|
|
102
|
+
if (check.minResolution !== undefined && caps.maxResolution < check.minResolution) {
|
|
103
|
+
gaps.push(`maxResolution is R${caps.maxResolution}, below R${check.minResolution}`);
|
|
104
|
+
}
|
|
105
|
+
return gaps;
|
|
106
|
+
}
|
|
107
|
+
/** The grammar `computeVerificationStatus` writes `exercised` entries in — see {@link resolutionOfExercised} for the inverse. */
|
|
108
|
+
function formatExercised(ref, resolution) {
|
|
109
|
+
return `${ref} (R${resolution})`;
|
|
110
|
+
}
|
|
111
|
+
const EXERCISED_PATTERN = /^(.*) \(R([0-4])\)$/;
|
|
112
|
+
/** Parses an `exercised` entry back into its ref and resolution. `null` if `entry` was not produced by {@link formatExercised}. */
|
|
113
|
+
export function resolutionOfExercised(entry) {
|
|
114
|
+
const m = EXERCISED_PATTERN.exec(entry);
|
|
115
|
+
if (m === null)
|
|
116
|
+
return null;
|
|
117
|
+
return { ref: m[1], resolution: Number(m[2]) };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Compute the static half of `VerificationStatus` for a scope.
|
|
121
|
+
*
|
|
122
|
+
* Per item, one of three outcomes — see the module header for the full
|
|
123
|
+
* reasoning:
|
|
124
|
+
*
|
|
125
|
+
* 1. **Present in the graph** → `exercised`, carrying its real resolution.
|
|
126
|
+
* 2. **Absent, no capabilities supplied** → `notExercised`, naming the
|
|
127
|
+
* language (or that none could be determined).
|
|
128
|
+
* 3. **Absent, capabilities supplied** → `notExercised`, naming the specific
|
|
129
|
+
* declared gap against `check` when there is one, or — the honest edge
|
|
130
|
+
* case — that a registered adapter declares everything asked for and a
|
|
131
|
+
* static capability comparison alone cannot say why the item is still
|
|
132
|
+
* absent.
|
|
133
|
+
*/
|
|
134
|
+
export function computeVerificationStatus(scope, check = {}) {
|
|
135
|
+
const exercised = [];
|
|
136
|
+
const notExercised = [];
|
|
137
|
+
for (const item of scope) {
|
|
138
|
+
if (item.node !== null) {
|
|
139
|
+
exercised.push(formatExercised(item.ref, item.node.resolution));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (item.capabilities === undefined) {
|
|
143
|
+
notExercised.push({
|
|
144
|
+
scope: item.ref,
|
|
145
|
+
reason: item.language === null
|
|
146
|
+
? "this item's language could not be determined, so no adapter can be attributed to it"
|
|
147
|
+
: `no adapter is registered for "${item.language}"`,
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const gaps = capabilityGaps(item.capabilities, check);
|
|
152
|
+
notExercised.push({
|
|
153
|
+
scope: item.ref,
|
|
154
|
+
reason: gaps.length > 0
|
|
155
|
+
? gaps.join("; ")
|
|
156
|
+
: `an adapter is registered${item.language === null ? "" : ` for "${item.language}"`} and declares ` +
|
|
157
|
+
"everything this check asks for, but this item was not found in the graph — a static capability " +
|
|
158
|
+
"comparison cannot say why",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return { exercised, notExercised };
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=verification-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-status.js","sourceRoot":"","sources":["../../src/query/verification-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AA8CH,kGAAkG;AAClG,SAAS,OAAO,CAAI,MAAgC,EAAE,IAAkB;IACtE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,IAAsB,EAAE,KAAoB;IAClE,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEtF,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEtF,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;QAClF,IAAI,CAAC,IAAI,CAAC,qBAAqB,IAAI,CAAC,aAAa,YAAY,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iIAAiI;AACjI,SAAS,eAAe,CAAC,GAAW,EAAE,UAA2B;IAC/D,OAAO,GAAG,GAAG,MAAM,UAAU,GAAG,CAAC;AACnC,CAAC;AAED,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,mIAAmI;AACnI,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAoB,EAAE,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAA2B,EAC3B,QAAuB,EAAE;IAEzB,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAoB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAChE,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACpC,YAAY,CAAC,IAAI,CAAC;gBAChB,KAAK,EAAE,IAAI,CAAC,GAAG;gBACf,MAAM,EACJ,IAAI,CAAC,QAAQ,KAAK,IAAI;oBACpB,CAAC,CAAC,qFAAqF;oBACvF,CAAC,CAAC,iCAAiC,IAAI,CAAC,QAAQ,GAAG;aACxD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACtD,YAAY,CAAC,IAAI,CAAC;YAChB,KAAK,EAAE,IAAI,CAAC,GAAG;YACf,MAAM,EACJ,IAAI,CAAC,MAAM,GAAG,CAAC;gBACb,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBACjB,CAAC,CAAC,2BAA2B,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,QAAQ,GAAG,gBAAgB;oBAClG,iGAAiG;oBACjG,2BAA2B;SAClC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The recording store: open it, start a run, record its calls, read them back
|
|
3
|
+
* in order. See `schema.ts`'s module header for why this is a store of its
|
|
4
|
+
* own rather than tables added to `../store`.
|
|
5
|
+
*/
|
|
6
|
+
import type { OpenOptions, SqlDriver } from "../store/driver/driver.ts";
|
|
7
|
+
export interface OpenRecordingStoreOptions extends OpenOptions {
|
|
8
|
+
/** Swap the backend, same as `openStore` — same driver interface, DEC-018. */
|
|
9
|
+
readonly driver?: SqlDriver;
|
|
10
|
+
}
|
|
11
|
+
export interface RecordingStore {
|
|
12
|
+
readonly driver: SqlDriver;
|
|
13
|
+
/** True when this call created the schema, rather than opening an existing store. */
|
|
14
|
+
readonly created: boolean;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
/** Open (or create) a recording store at `path`, or `":memory:"` for a throwaway one. */
|
|
18
|
+
export declare function openRecordingStore(path: string, options?: OpenRecordingStoreOptions): RecordingStore;
|
|
19
|
+
export { RecordingSchemaVersionError, migrateRecording } from "./migrate.ts";
|
|
20
|
+
export { RECORDING_SCHEMA_VERSION, SCHEMA_SQL as RECORDING_SCHEMA_SQL } from "./schema.ts";
|
|
21
|
+
export { startRun, recordCall } from "./writer.ts";
|
|
22
|
+
export type { RunStamp, StartRunOptions, ToolCallRecord, RecordCallOptions, RecordCallResult } from "./writer.ts";
|
|
23
|
+
export { readRun } from "./reader.ts";
|
|
24
|
+
export type { RecordedCall, RecordedRun } from "./reader.ts";
|
|
25
|
+
export { createRecordingRedactor, redactSecretsInText } from "./redact.ts";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recording/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAGxE,MAAM,WAAW,yBAA0B,SAAQ,WAAW;IAC5D,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,cAAc,CAcxG;AAED,OAAO,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElH,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The recording store: open it, start a run, record its calls, read them back
|
|
3
|
+
* in order. See `schema.ts`'s module header for why this is a store of its
|
|
4
|
+
* own rather than tables added to `../store`.
|
|
5
|
+
*/
|
|
6
|
+
import { NodeSqliteDriver } from "../store/driver/node-sqlite.js";
|
|
7
|
+
import { migrateRecording } from "./migrate.js";
|
|
8
|
+
/** Open (or create) a recording store at `path`, or `":memory:"` for a throwaway one. */
|
|
9
|
+
export function openRecordingStore(path, options = {}) {
|
|
10
|
+
const driver = options.driver ?? new NodeSqliteDriver();
|
|
11
|
+
const openOptions = {
|
|
12
|
+
...(options.wal === undefined ? {} : { wal: options.wal }),
|
|
13
|
+
...(options.readOnly === undefined ? {} : { readOnly: options.readOnly }),
|
|
14
|
+
};
|
|
15
|
+
driver.open(path, openOptions);
|
|
16
|
+
try {
|
|
17
|
+
const { created } = migrateRecording(driver);
|
|
18
|
+
return { driver, created, close: () => driver.close() };
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
driver.close();
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export { RecordingSchemaVersionError, migrateRecording } from "./migrate.js";
|
|
26
|
+
export { RECORDING_SCHEMA_VERSION, SCHEMA_SQL as RECORDING_SCHEMA_SQL } from "./schema.js";
|
|
27
|
+
export { startRun, recordCall } from "./writer.js";
|
|
28
|
+
export { readRun } from "./reader.js";
|
|
29
|
+
export { createRecordingRedactor, redactSecretsInText } from "./redact.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/recording/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAchD,yFAAyF;AACzF,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,UAAqC,EAAE;IACtF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;IACxD,MAAM,WAAW,GAAgB;QAC/B,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1D,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;KAC1E,CAAC;IACF,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,OAAO,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGnD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema creation and version enforcement for the recording store.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `../store/migrate.ts`'s shape deliberately (create-if-absent, then
|
|
5
|
+
* verify) but not its recovery path: the graph store's `dropDerived()` exists
|
|
6
|
+
* because a version mismatch there means "rebuild from source." There is no
|
|
7
|
+
* source to rebuild a recorded call from, so a mismatch here has exactly one
|
|
8
|
+
* safe response — refuse to open — and no `dropDerived` equivalent is
|
|
9
|
+
* provided.
|
|
10
|
+
*/
|
|
11
|
+
import { type SqlDriver } from "../store/driver/driver.ts";
|
|
12
|
+
export declare class RecordingSchemaVersionError extends Error {
|
|
13
|
+
readonly name = "RecordingSchemaVersionError";
|
|
14
|
+
readonly found: number;
|
|
15
|
+
readonly expected: number;
|
|
16
|
+
constructor(found: number, expected: number);
|
|
17
|
+
}
|
|
18
|
+
/** Create the schema if absent, then verify this build can read the store. */
|
|
19
|
+
export declare function migrateRecording(driver: SqlDriver): {
|
|
20
|
+
created: boolean;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/recording/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAGxE,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,SAAkB,IAAI,iCAAiC;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAS5C;AAmBD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAkBxE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema creation and version enforcement for the recording store.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `../store/migrate.ts`'s shape deliberately (create-if-absent, then
|
|
5
|
+
* verify) but not its recovery path: the graph store's `dropDerived()` exists
|
|
6
|
+
* because a version mismatch there means "rebuild from source." There is no
|
|
7
|
+
* source to rebuild a recorded call from, so a mismatch here has exactly one
|
|
8
|
+
* safe response — refuse to open — and no `dropDerived` equivalent is
|
|
9
|
+
* provided.
|
|
10
|
+
*/
|
|
11
|
+
import { DriverError } from "../store/driver/driver.js";
|
|
12
|
+
import { RECORDING_SCHEMA_VERSION, SCHEMA_SQL } from "./schema.js";
|
|
13
|
+
export class RecordingSchemaVersionError extends Error {
|
|
14
|
+
name = "RecordingSchemaVersionError";
|
|
15
|
+
found;
|
|
16
|
+
expected;
|
|
17
|
+
constructor(found, expected) {
|
|
18
|
+
super(`recording store was written under schema version ${found}, this build expects ${expected}. ` +
|
|
19
|
+
`Unlike the graph store, recorded calls have no source to rebuild from — this store cannot ` +
|
|
20
|
+
`be migrated automatically.`);
|
|
21
|
+
this.found = found;
|
|
22
|
+
this.expected = expected;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function readMeta(driver, key) {
|
|
26
|
+
return driver.prepare("SELECT key, value FROM recording_meta WHERE key = ?").get(key)?.value;
|
|
27
|
+
}
|
|
28
|
+
function writeMeta(driver, key, value) {
|
|
29
|
+
driver
|
|
30
|
+
.prepare("INSERT INTO recording_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value")
|
|
31
|
+
.run(key, value);
|
|
32
|
+
}
|
|
33
|
+
/** Create the schema if absent, then verify this build can read the store. */
|
|
34
|
+
export function migrateRecording(driver) {
|
|
35
|
+
return driver.transaction(() => {
|
|
36
|
+
driver.exec(SCHEMA_SQL);
|
|
37
|
+
const stored = readMeta(driver, "schema_version");
|
|
38
|
+
if (stored === undefined) {
|
|
39
|
+
writeMeta(driver, "schema_version", String(RECORDING_SCHEMA_VERSION));
|
|
40
|
+
return { created: true };
|
|
41
|
+
}
|
|
42
|
+
const found = Number(stored);
|
|
43
|
+
if (!Number.isInteger(found)) {
|
|
44
|
+
throw new DriverError(`recording_meta.schema_version is not an integer: ${stored}`);
|
|
45
|
+
}
|
|
46
|
+
if (found !== RECORDING_SCHEMA_VERSION)
|
|
47
|
+
throw new RecordingSchemaVersionError(found, RECORDING_SCHEMA_VERSION);
|
|
48
|
+
return { created: false };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/recording/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAkB,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAClC,IAAI,GAAG,6BAA6B,CAAC;IAC9C,KAAK,CAAS;IACd,QAAQ,CAAS;IAE1B,YAAY,KAAa,EAAE,QAAgB;QACzC,KAAK,CACH,oDAAoD,KAAK,wBAAwB,QAAQ,IAAI;YAC3F,4FAA4F;YAC5F,4BAA4B,CAC/B,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAOD,SAAS,QAAQ,CAAC,MAAiB,EAAE,GAAW;IAC9C,OAAO,MAAM,CAAC,OAAO,CAAU,qDAAqD,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;AACxG,CAAC;AAED,SAAS,SAAS,CAAC,MAAiB,EAAE,GAAW,EAAE,KAAa;IAC9D,MAAM;SACH,OAAO,CACN,6GAA6G,CAC9G;SACA,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAExB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QAClD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC;YACtE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,WAAW,CAAC,oDAAoD,MAAM,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,KAAK,KAAK,wBAAwB;YAAE,MAAM,IAAI,2BAA2B,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;QAE/G,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads over the recording store — the replay path.
|
|
3
|
+
*
|
|
4
|
+
* `readRun` is deliberately the only read here: replay needs the whole run,
|
|
5
|
+
* in order, with nothing summarised out. A caller wanting "just the last
|
|
6
|
+
* call" or "just this tool's calls" can filter `readRun`'s result; a second
|
|
7
|
+
* entry point that pre-filters would be a second place the ordering guarantee
|
|
8
|
+
* has to be re-proven.
|
|
9
|
+
*/
|
|
10
|
+
import type { SqlDriver } from "../store/driver/driver.ts";
|
|
11
|
+
import type { RunStamp, ToolCallRecord } from "./writer.ts";
|
|
12
|
+
export interface RecordedCall extends ToolCallRecord {
|
|
13
|
+
readonly seq: number;
|
|
14
|
+
readonly recordedAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface RecordedRun {
|
|
17
|
+
readonly runId: string;
|
|
18
|
+
readonly startedAt: number;
|
|
19
|
+
readonly stamp: RunStamp;
|
|
20
|
+
readonly promptVersion: string | null;
|
|
21
|
+
readonly modelId: string | null;
|
|
22
|
+
/** Ordered by `seq` ascending — the order the calls actually happened in. */
|
|
23
|
+
readonly calls: readonly RecordedCall[];
|
|
24
|
+
}
|
|
25
|
+
/** The full run, calls ordered by `seq`. `undefined` if no such run was started. */
|
|
26
|
+
export declare function readRun(driver: SqlDriver, runId: string): RecordedRun | undefined;
|
|
27
|
+
//# sourceMappingURL=reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/recording/reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;CACzC;AA4CD,oFAAoF;AACpF,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CA4BjF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads over the recording store — the replay path.
|
|
3
|
+
*
|
|
4
|
+
* `readRun` is deliberately the only read here: replay needs the whole run,
|
|
5
|
+
* in order, with nothing summarised out. A caller wanting "just the last
|
|
6
|
+
* call" or "just this tool's calls" can filter `readRun`'s result; a second
|
|
7
|
+
* entry point that pre-filters would be a second place the ordering guarantee
|
|
8
|
+
* has to be re-proven.
|
|
9
|
+
*/
|
|
10
|
+
import { isToolClass } from "@descryy/ir";
|
|
11
|
+
function rowToCall(row) {
|
|
12
|
+
if (!isToolClass(row.class)) {
|
|
13
|
+
// Only reachable if something wrote around recordCall, which is exactly
|
|
14
|
+
// the case worth failing loudly on rather than coercing.
|
|
15
|
+
throw new Error(`recorded call at seq ${row.seq} has an unknown class: ${row.class}`);
|
|
16
|
+
}
|
|
17
|
+
const toolClass = row.class;
|
|
18
|
+
return {
|
|
19
|
+
seq: row.seq,
|
|
20
|
+
tool: row.tool,
|
|
21
|
+
toolVersion: row.tool_version,
|
|
22
|
+
class: toolClass,
|
|
23
|
+
arguments: JSON.parse(row.arguments),
|
|
24
|
+
result: JSON.parse(row.result),
|
|
25
|
+
durationMs: row.duration_ms,
|
|
26
|
+
cost: row.cost,
|
|
27
|
+
recordedAt: row.recorded_at,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** The full run, calls ordered by `seq`. `undefined` if no such run was started. */
|
|
31
|
+
export function readRun(driver, runId) {
|
|
32
|
+
const run = driver
|
|
33
|
+
.prepare(`SELECT id, started_at, commit_sha, built_at, ir_schema_version, prompt_version, model_id
|
|
34
|
+
FROM recorded_runs WHERE id = ?`)
|
|
35
|
+
.get(runId);
|
|
36
|
+
if (run === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
const rows = driver
|
|
39
|
+
.prepare(`SELECT seq, tool, tool_version, class, arguments, result, duration_ms, cost, recorded_at
|
|
40
|
+
FROM recorded_calls WHERE run_id = ? ORDER BY seq ASC`)
|
|
41
|
+
.all(runId);
|
|
42
|
+
return {
|
|
43
|
+
runId: run.id,
|
|
44
|
+
startedAt: run.started_at,
|
|
45
|
+
stamp: {
|
|
46
|
+
commitSha: run.commit_sha,
|
|
47
|
+
builtAt: run.built_at,
|
|
48
|
+
irSchemaVersion: run.ir_schema_version,
|
|
49
|
+
},
|
|
50
|
+
promptVersion: run.prompt_version,
|
|
51
|
+
modelId: run.model_id,
|
|
52
|
+
calls: rows.map(rowToCall),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/recording/reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AA0C1D,SAAS,SAAS,CAAC,GAAY;IAC7B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,wEAAwE;QACxE,yDAAyD;QACzD,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,GAAG,0BAA0B,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,SAAS,GAAc,GAAG,CAAC,KAAK,CAAC;IACvC,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAY;QAC/C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAY;QACzC,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,WAAW;KAC5B,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,OAAO,CAAC,MAAiB,EAAE,KAAa;IACtD,MAAM,GAAG,GAAG,MAAM;SACf,OAAO,CACN;yCACmC,CACpC;SACA,GAAG,CAAC,KAAK,CAAC,CAAC;IACd,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,IAAI,GAAG,MAAM;SAChB,OAAO,CACN;+DACyD,CAC1D;SACA,GAAG,CAAC,KAAK,CAAC,CAAC;IAEd,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,EAAE;QACb,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,KAAK,EAAE;YACL,SAAS,EAAE,GAAG,CAAC,UAAU;YACzB,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,eAAe,EAAE,GAAG,CAAC,iBAAiB;SACvC;QACD,aAAa,EAAE,GAAG,CAAC,cAAc;QACjC,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A concrete implementation for `writer.ts`'s `RecordCallOptions.redact` seam.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `schema.ts` and `writer.ts`'s module headers both say the same thing: the
|
|
7
|
+
* `redact` option is a documented seam, and until something is plugged into
|
|
8
|
+
* it every row this store writes is exactly what the tool call carried,
|
|
9
|
+
* including secrets. That was true because nothing in this repository ever
|
|
10
|
+
* built a redactor — not because building one needs work outside this
|
|
11
|
+
* repo's scope. This file is that redactor.
|
|
12
|
+
*
|
|
13
|
+
* ## Literal substring replacement, never a pattern
|
|
14
|
+
*
|
|
15
|
+
* Ported from descry-desktop's `packages/session/src/redact.ts`
|
|
16
|
+
* (`redactSecrets`) — the model-client redaction seam's own implementation,
|
|
17
|
+
* the one place in the four-repo split this logic already existed and was
|
|
18
|
+
* already tested. That file's doc comment states the reason plainly and it
|
|
19
|
+
* applies here unchanged: **a secret is arbitrary text and must never be
|
|
20
|
+
* interpreted as a pattern.** A regex-based "looks like an API key" detector
|
|
21
|
+
* is a recall play — it guesses, and a wrong guess either redacts real data
|
|
22
|
+
* that was not a secret or misses a secret shaped unlike the pattern.
|
|
23
|
+
* `CLAUDE.md`'s "precision over recall, when in doubt omit" applies to
|
|
24
|
+
* redaction exactly as it applies to a graph edge: only redact a value the
|
|
25
|
+
* caller can actually name as a secret.
|
|
26
|
+
*
|
|
27
|
+
* There is no shared package between `descry-core` and `descry-desktop`
|
|
28
|
+
* today (DEC-001's four-repo split names no fifth "shared utilities" repo),
|
|
29
|
+
* so this is a deliberate duplication of the minimal literal-substitution
|
|
30
|
+
* logic rather than a new cross-repo dependency edge. If a third call site
|
|
31
|
+
* needs this, that is the trigger to extract a shared package — not before.
|
|
32
|
+
*
|
|
33
|
+
* ## Why this is still opt-in, not automatic inside `recordCall`
|
|
34
|
+
*
|
|
35
|
+
* `recordCall` has no way to know, on its own, which strings in an
|
|
36
|
+
* arbitrary `arguments`/`result` payload are secrets — that is exactly the
|
|
37
|
+
* "arbitrary text, not a pattern" problem above. Only a caller holding the
|
|
38
|
+
* deployment's actual configured secret values (an API key, a token, a DB
|
|
39
|
+
* password) can supply that list, the same division of responsibility
|
|
40
|
+
* `createPromptRedactor` uses on the model-client side. Wiring this
|
|
41
|
+
* redactor into a live `recordCall` call site — the MCP server's tool
|
|
42
|
+
* dispatch, or `descry-runtime`'s — with real configured secret values is
|
|
43
|
+
* separate work (the same "P3, the tool boundary" wiring the module headers
|
|
44
|
+
* already point at), not something this file can do for a caller that does
|
|
45
|
+
* not exist yet in this repo.
|
|
46
|
+
*/
|
|
47
|
+
import type { ToolCallRecord } from "./writer.ts";
|
|
48
|
+
/**
|
|
49
|
+
* Replace every occurrence of each secret value in `text` with a fixed
|
|
50
|
+
* marker. Literal substring replacement, not regex — see this file's module
|
|
51
|
+
* header for why.
|
|
52
|
+
*/
|
|
53
|
+
export declare function redactSecretsInText(text: string, secretValues: readonly string[]): string;
|
|
54
|
+
/**
|
|
55
|
+
* Builds a `redact` function for `recordCall`'s seam (`RecordCallOptions`),
|
|
56
|
+
* given the secret values known at the call site — whatever the caller's own
|
|
57
|
+
* configuration surface can name as sensitive. Walks both `arguments` and
|
|
58
|
+
* `result` in full, since a secret passed in can just as easily be echoed
|
|
59
|
+
* back in a tool's result (an error message quoting the bad input, for
|
|
60
|
+
* instance) as it can appear in the arguments themselves.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createRecordingRedactor(secretValues: readonly string[]): (call: ToolCallRecord) => ToolCallRecord;
|
|
63
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/recording/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAOzF;AAsBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,CAAC,IAAI,EAAE,cAAc,KAAK,cAAc,CAM1C"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A concrete implementation for `writer.ts`'s `RecordCallOptions.redact` seam.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `schema.ts` and `writer.ts`'s module headers both say the same thing: the
|
|
7
|
+
* `redact` option is a documented seam, and until something is plugged into
|
|
8
|
+
* it every row this store writes is exactly what the tool call carried,
|
|
9
|
+
* including secrets. That was true because nothing in this repository ever
|
|
10
|
+
* built a redactor — not because building one needs work outside this
|
|
11
|
+
* repo's scope. This file is that redactor.
|
|
12
|
+
*
|
|
13
|
+
* ## Literal substring replacement, never a pattern
|
|
14
|
+
*
|
|
15
|
+
* Ported from descry-desktop's `packages/session/src/redact.ts`
|
|
16
|
+
* (`redactSecrets`) — the model-client redaction seam's own implementation,
|
|
17
|
+
* the one place in the four-repo split this logic already existed and was
|
|
18
|
+
* already tested. That file's doc comment states the reason plainly and it
|
|
19
|
+
* applies here unchanged: **a secret is arbitrary text and must never be
|
|
20
|
+
* interpreted as a pattern.** A regex-based "looks like an API key" detector
|
|
21
|
+
* is a recall play — it guesses, and a wrong guess either redacts real data
|
|
22
|
+
* that was not a secret or misses a secret shaped unlike the pattern.
|
|
23
|
+
* `CLAUDE.md`'s "precision over recall, when in doubt omit" applies to
|
|
24
|
+
* redaction exactly as it applies to a graph edge: only redact a value the
|
|
25
|
+
* caller can actually name as a secret.
|
|
26
|
+
*
|
|
27
|
+
* There is no shared package between `descry-core` and `descry-desktop`
|
|
28
|
+
* today (DEC-001's four-repo split names no fifth "shared utilities" repo),
|
|
29
|
+
* so this is a deliberate duplication of the minimal literal-substitution
|
|
30
|
+
* logic rather than a new cross-repo dependency edge. If a third call site
|
|
31
|
+
* needs this, that is the trigger to extract a shared package — not before.
|
|
32
|
+
*
|
|
33
|
+
* ## Why this is still opt-in, not automatic inside `recordCall`
|
|
34
|
+
*
|
|
35
|
+
* `recordCall` has no way to know, on its own, which strings in an
|
|
36
|
+
* arbitrary `arguments`/`result` payload are secrets — that is exactly the
|
|
37
|
+
* "arbitrary text, not a pattern" problem above. Only a caller holding the
|
|
38
|
+
* deployment's actual configured secret values (an API key, a token, a DB
|
|
39
|
+
* password) can supply that list, the same division of responsibility
|
|
40
|
+
* `createPromptRedactor` uses on the model-client side. Wiring this
|
|
41
|
+
* redactor into a live `recordCall` call site — the MCP server's tool
|
|
42
|
+
* dispatch, or `descry-runtime`'s — with real configured secret values is
|
|
43
|
+
* separate work (the same "P3, the tool boundary" wiring the module headers
|
|
44
|
+
* already point at), not something this file can do for a caller that does
|
|
45
|
+
* not exist yet in this repo.
|
|
46
|
+
*/
|
|
47
|
+
const REDACTED = "[REDACTED]";
|
|
48
|
+
/**
|
|
49
|
+
* Replace every occurrence of each secret value in `text` with a fixed
|
|
50
|
+
* marker. Literal substring replacement, not regex — see this file's module
|
|
51
|
+
* header for why.
|
|
52
|
+
*/
|
|
53
|
+
export function redactSecretsInText(text, secretValues) {
|
|
54
|
+
let result = text;
|
|
55
|
+
for (const value of secretValues) {
|
|
56
|
+
if (value.length === 0)
|
|
57
|
+
continue; // an empty value would "redact" every character
|
|
58
|
+
result = result.split(value).join(REDACTED);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Walks arbitrary JSON-shaped data — exactly what `ToolCallRecord.arguments`
|
|
64
|
+
* and `.result` are typed as (`unknown`) — replacing every string leaf via
|
|
65
|
+
* `redactSecretsInText`. Non-string leaves (numbers, booleans, null) pass
|
|
66
|
+
* through untouched: a secret value the caller supplied is text, by
|
|
67
|
+
* definition, so there is nothing to compare a number against.
|
|
68
|
+
*/
|
|
69
|
+
function redactDeep(value, secretValues) {
|
|
70
|
+
if (typeof value === "string")
|
|
71
|
+
return redactSecretsInText(value, secretValues);
|
|
72
|
+
if (Array.isArray(value))
|
|
73
|
+
return value.map((item) => redactDeep(item, secretValues));
|
|
74
|
+
if (value !== null && typeof value === "object") {
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
77
|
+
out[key] = redactDeep(entry, secretValues);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Builds a `redact` function for `recordCall`'s seam (`RecordCallOptions`),
|
|
85
|
+
* given the secret values known at the call site — whatever the caller's own
|
|
86
|
+
* configuration surface can name as sensitive. Walks both `arguments` and
|
|
87
|
+
* `result` in full, since a secret passed in can just as easily be echoed
|
|
88
|
+
* back in a tool's result (an error message quoting the bad input, for
|
|
89
|
+
* instance) as it can appear in the arguments themselves.
|
|
90
|
+
*/
|
|
91
|
+
export function createRecordingRedactor(secretValues) {
|
|
92
|
+
return (call) => ({
|
|
93
|
+
...call,
|
|
94
|
+
arguments: redactDeep(call.arguments, secretValues),
|
|
95
|
+
result: redactDeep(call.result, secretValues),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/recording/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,YAA+B;IAC/E,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS,CAAC,gDAAgD;QAClF,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,KAAc,EAAE,YAA+B;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,mBAAmB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IACrF,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YAC5E,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,YAA+B;IAE/B,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAChB,GAAG,IAAI;QACP,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACnD,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;KAC9C,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call-recording schema — `ai-tool-contract.md` §6, rule 5: "every call is
|
|
3
|
+
* recorded" (§41.15), the basis of replay (`ai-layer-plan.md` §9 layer 6,
|
|
4
|
+
* §12's reproducibility requirement).
|
|
5
|
+
*
|
|
6
|
+
* A separate store from `../store` on purpose, not an extra table bolted onto
|
|
7
|
+
* the graph schema:
|
|
8
|
+
*
|
|
9
|
+
* 1. **The two version numbers move for unrelated reasons.** `IR_SCHEMA_VERSION`
|
|
10
|
+
* bumps when the graph's own shape changes; this schema bumps when the
|
|
11
|
+
* recording format changes. Coupling them would force a recording-format
|
|
12
|
+
* change to carry a graph rebuild, and vice versa.
|
|
13
|
+
* 2. **Nothing here is "derived."** DEC-017's derived/earned split exists
|
|
14
|
+
* because derived rows can be regenerated from source. A recorded call is a
|
|
15
|
+
* fact about one already-finished execution — there is no source to
|
|
16
|
+
* regenerate it from, ever. Every table below is earned in DEC-017's sense,
|
|
17
|
+
* and the version check refuses on mismatch rather than offering a
|
|
18
|
+
* `dropDerived()`-equivalent: there is nothing safe to drop.
|
|
19
|
+
* 3. Same reasoning DEC-018 gives for the graph store applies here unchanged:
|
|
20
|
+
* `node:sqlite` behind the existing `SqlDriver` interface, not a second
|
|
21
|
+
* persistence technology. This module imports the driver, it does not
|
|
22
|
+
* reinvent one.
|
|
23
|
+
*
|
|
24
|
+
* ## Unredacted material — read before wiring this to a live tool boundary
|
|
25
|
+
*
|
|
26
|
+
* Contract §6: redaction happens *at the tool boundary, before the model
|
|
27
|
+
* call*. **This store holds whatever `arguments` and `result` it is given,
|
|
28
|
+
* verbatim, unless a redactor was supplied.** `recordCall`'s `redact` option
|
|
29
|
+
* (`writer.ts`) is the seam; `redact.ts`'s `createRecordingRedactor` is a
|
|
30
|
+
* real, tested implementation of it, given the caller's own configured
|
|
31
|
+
* secret values. No live call site in this repo constructs and passes one
|
|
32
|
+
* today — recording is not yet wired into the MCP server's or
|
|
33
|
+
* `descry-runtime`'s actual tool dispatch — so every row written by this
|
|
34
|
+
* repo's current callers is still unredacted. Treat a database file under
|
|
35
|
+
* this schema as containing the same sensitive material the tool calls
|
|
36
|
+
* themselves carried, until whoever wires the live call path passes a
|
|
37
|
+
* redactor built from real secret values.
|
|
38
|
+
*/
|
|
39
|
+
export declare const RECORDING_SCHEMA_VERSION = 1;
|
|
40
|
+
export declare const SCHEMA_SQL = "\n-- ---------------------------------------------------------------------------\n-- recording_meta \u2014 same role as the graph store's graph_meta: identity of the\n-- store itself, checked before anything else is trusted.\n-- ---------------------------------------------------------------------------\nCREATE TABLE IF NOT EXISTS recording_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n) STRICT;\n\n-- ---------------------------------------------------------------------------\n-- recorded_runs \u2014 one row per AI-layer run. The graph stamp (commit_sha,\n-- built_at, ir_schema_version) is captured ONCE here, per \u00A712: replay needs to\n-- know what graph the run saw, not re-derive it from the first call.\n--\n-- prompt_version and model_id are present and nullable, never a column that\n-- does not exist: \u00A73's rule that \"a missing field and a null field read\n-- identically to a careless caller, and only one of them is honest\" applies\n-- to a database column exactly as it applies to the wire envelope. No model\n-- calls exist yet in this repo, so both are null on every row written today \u2014\n-- that is a true statement about the current build, not a gap in the schema.\n--\n-- next_seq is the ordering counter writer.ts increments atomically with\n-- every insert into recorded_calls, so \"what order did these calls happen in\"\n-- is answerable without trusting wall-clock timestamps, which two calls in the\n-- same millisecond cannot distinguish.\n-- ---------------------------------------------------------------------------\nCREATE TABLE IF NOT EXISTS recorded_runs (\n id TEXT PRIMARY KEY,\n started_at INTEGER NOT NULL,\n commit_sha TEXT,\n built_at TEXT,\n ir_schema_version INTEGER,\n prompt_version TEXT,\n model_id TEXT,\n next_seq INTEGER NOT NULL DEFAULT 0\n) STRICT;\n\n-- ---------------------------------------------------------------------------\n-- recorded_calls \u2014 one row per tool call. (run_id, seq) is the primary key\n-- rather than an autoincrement id: ordering within a run is the whole point of\n-- seq, and a composite key makes \"give me run X in order\" a primary-key range\n-- scan instead of a sort.\n--\n-- arguments and result are stored as canonicalJson of exactly what the\n-- caller passed \u2014 the full envelope, not a summary. Dropping a field here\n-- (say, disclosures, or the resolution level buried in graph) would make\n-- this store unable to reproduce whatever the finding drew from that field,\n-- which is the one failure mode replay exists to rule out.\n-- ---------------------------------------------------------------------------\nCREATE TABLE IF NOT EXISTS recorded_calls (\n run_id TEXT NOT NULL REFERENCES recorded_runs(id) ON DELETE CASCADE,\n seq INTEGER NOT NULL,\n tool TEXT NOT NULL,\n tool_version TEXT NOT NULL,\n class TEXT NOT NULL,\n arguments TEXT NOT NULL,\n result TEXT NOT NULL,\n duration_ms INTEGER NOT NULL,\n cost REAL,\n recorded_at INTEGER NOT NULL,\n PRIMARY KEY (run_id, seq)\n) STRICT;\n";
|
|
41
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/recording/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,UAAU,kmGA+DtB,CAAC"}
|