@descryy/mcp 0.11.5 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-handshake.d.ts +38 -0
- package/dist/action-handshake.d.ts.map +1 -0
- package/dist/action-handshake.js +127 -0
- package/dist/action-handshake.js.map +1 -0
- package/dist/bin/descry-mcp.d.ts +7 -0
- package/dist/bin/descry-mcp.d.ts.map +1 -0
- package/dist/bin/descry-mcp.js +73 -0
- package/dist/bin/descry-mcp.js.map +1 -0
- package/dist/browser/auth.d.ts +84 -0
- package/dist/browser/auth.d.ts.map +1 -0
- package/dist/browser/auth.js +233 -0
- package/dist/browser/auth.js.map +1 -0
- package/dist/browser/capability-probe.d.ts +109 -0
- package/dist/browser/capability-probe.d.ts.map +1 -0
- package/dist/browser/capability-probe.js +201 -0
- package/dist/browser/capability-probe.js.map +1 -0
- package/dist/browser/driver.d.ts +591 -0
- package/dist/browser/driver.d.ts.map +1 -0
- package/dist/browser/driver.js +116 -0
- package/dist/browser/driver.js.map +1 -0
- package/dist/browser/evidence.d.ts +28 -0
- package/dist/browser/evidence.d.ts.map +1 -0
- package/dist/browser/evidence.js +82 -0
- package/dist/browser/evidence.js.map +1 -0
- package/dist/browser/fake-driver.d.ts +116 -0
- package/dist/browser/fake-driver.d.ts.map +1 -0
- package/dist/browser/fake-driver.js +416 -0
- package/dist/browser/fake-driver.js.map +1 -0
- package/dist/browser/fault-attribution.d.ts +178 -0
- package/dist/browser/fault-attribution.d.ts.map +1 -0
- package/dist/browser/fault-attribution.js +266 -0
- package/dist/browser/fault-attribution.js.map +1 -0
- package/dist/browser/graph-write.d.ts +45 -0
- package/dist/browser/graph-write.d.ts.map +1 -0
- package/dist/browser/graph-write.js +103 -0
- package/dist/browser/graph-write.js.map +1 -0
- package/dist/browser/identity-graph-write.d.ts +55 -0
- package/dist/browser/identity-graph-write.d.ts.map +1 -0
- package/dist/browser/identity-graph-write.js +45 -0
- package/dist/browser/identity-graph-write.js.map +1 -0
- package/dist/browser/page-probe.d.ts +81 -0
- package/dist/browser/page-probe.d.ts.map +1 -0
- package/dist/browser/page-probe.js +202 -0
- package/dist/browser/page-probe.js.map +1 -0
- package/dist/browser/playwright-driver.d.ts +115 -0
- package/dist/browser/playwright-driver.d.ts.map +1 -0
- package/dist/browser/playwright-driver.js +1545 -0
- package/dist/browser/playwright-driver.js.map +1 -0
- package/dist/browser/provider.d.ts +26 -0
- package/dist/browser/provider.d.ts.map +1 -0
- package/dist/browser/provider.js +14 -0
- package/dist/browser/provider.js.map +1 -0
- package/dist/browser/reach-recording-session.d.ts +44 -0
- package/dist/browser/reach-recording-session.d.ts.map +1 -0
- package/dist/browser/reach-recording-session.js +151 -0
- package/dist/browser/reach-recording-session.js.map +1 -0
- package/dist/browser/reachability.d.ts +168 -0
- package/dist/browser/reachability.d.ts.map +1 -0
- package/dist/browser/reachability.js +294 -0
- package/dist/browser/reachability.js.map +1 -0
- package/dist/browser/registry.d.ts +126 -0
- package/dist/browser/registry.d.ts.map +1 -0
- package/dist/browser/registry.js +177 -0
- package/dist/browser/registry.js.map +1 -0
- package/dist/browser/scenario-provenance.d.ts +23 -0
- package/dist/browser/scenario-provenance.d.ts.map +1 -0
- package/dist/browser/scenario-provenance.js +73 -0
- package/dist/browser/scenario-provenance.js.map +1 -0
- package/dist/browser/scenario-resolve.d.ts +28 -0
- package/dist/browser/scenario-resolve.d.ts.map +1 -0
- package/dist/browser/scenario-resolve.js +96 -0
- package/dist/browser/scenario-resolve.js.map +1 -0
- package/dist/browser/scenario-runner.d.ts +72 -0
- package/dist/browser/scenario-runner.d.ts.map +1 -0
- package/dist/browser/scenario-runner.js +353 -0
- package/dist/browser/scenario-runner.js.map +1 -0
- package/dist/browser/stack-parser.d.ts +19 -0
- package/dist/browser/stack-parser.d.ts.map +1 -0
- package/dist/browser/stack-parser.js +85 -0
- package/dist/browser/stack-parser.js.map +1 -0
- package/dist/browser/tool-support.d.ts +71 -0
- package/dist/browser/tool-support.d.ts.map +1 -0
- package/dist/browser/tool-support.js +216 -0
- package/dist/browser/tool-support.js.map +1 -0
- package/dist/browser/url-scheme.d.ts +14 -0
- package/dist/browser/url-scheme.d.ts.map +1 -0
- package/dist/browser/url-scheme.js +38 -0
- package/dist/browser/url-scheme.js.map +1 -0
- package/dist/browser/wait-target.d.ts +37 -0
- package/dist/browser/wait-target.d.ts.map +1 -0
- package/dist/browser/wait-target.js +54 -0
- package/dist/browser/wait-target.js.map +1 -0
- package/dist/cancellation.d.ts +20 -0
- package/dist/cancellation.d.ts.map +1 -0
- package/dist/cancellation.js +41 -0
- package/dist/cancellation.js.map +1 -0
- package/dist/capped.d.ts +39 -0
- package/dist/capped.d.ts.map +1 -0
- package/dist/capped.js +43 -0
- package/dist/capped.js.map +1 -0
- package/dist/credential-store.d.ts +22 -0
- package/dist/credential-store.d.ts.map +1 -0
- package/dist/credential-store.js +33 -0
- package/dist/credential-store.js.map +1 -0
- package/dist/disclosure-ledger.d.ts +14 -0
- package/dist/disclosure-ledger.d.ts.map +1 -0
- package/dist/disclosure-ledger.js +18 -0
- package/dist/disclosure-ledger.js.map +1 -0
- package/dist/evidence/observed-route-evidence.d.ts +25 -0
- package/dist/evidence/observed-route-evidence.d.ts.map +1 -0
- package/dist/evidence/observed-route-evidence.js +76 -0
- package/dist/evidence/observed-route-evidence.js.map +1 -0
- package/dist/heap.d.ts +61 -0
- package/dist/heap.d.ts.map +1 -0
- package/dist/heap.js +82 -0
- package/dist/heap.js.map +1 -0
- package/dist/heartbeat.d.ts +46 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +66 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/module-trust.d.ts +37 -0
- package/dist/module-trust.d.ts.map +1 -0
- package/dist/module-trust.js +77 -0
- package/dist/module-trust.js.map +1 -0
- package/dist/path-confinement.d.ts +31 -0
- package/dist/path-confinement.d.ts.map +1 -0
- package/dist/path-confinement.js +44 -0
- package/dist/path-confinement.js.map +1 -0
- package/dist/planner/predict-then-propose.d.ts +134 -0
- package/dist/planner/predict-then-propose.d.ts.map +1 -0
- package/dist/planner/predict-then-propose.js +138 -0
- package/dist/planner/predict-then-propose.js.map +1 -0
- package/dist/planner/propose-journey.d.ts +75 -0
- package/dist/planner/propose-journey.d.ts.map +1 -0
- package/dist/planner/propose-journey.js +96 -0
- package/dist/planner/propose-journey.js.map +1 -0
- package/dist/protocol.d.ts +57 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +90 -0
- package/dist/protocol.js.map +1 -0
- package/dist/ready-checks.d.ts +12 -0
- package/dist/ready-checks.d.ts.map +1 -0
- package/dist/ready-checks.js +43 -0
- package/dist/ready-checks.js.map +1 -0
- package/dist/registry.d.ts +103 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +327 -0
- package/dist/registry.js.map +1 -0
- package/dist/render.d.ts +623 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +968 -0
- package/dist/render.js.map +1 -0
- package/dist/runtime-registry.d.ts +22 -0
- package/dist/runtime-registry.d.ts.map +1 -0
- package/dist/runtime-registry.js +142 -0
- package/dist/runtime-registry.js.map +1 -0
- package/dist/sandbox-defaults.d.ts +41 -0
- package/dist/sandbox-defaults.d.ts.map +1 -0
- package/dist/sandbox-defaults.js +14 -0
- package/dist/sandbox-defaults.js.map +1 -0
- package/dist/scenarios/browser-run-plan-projection.d.ts +161 -0
- package/dist/scenarios/browser-run-plan-projection.d.ts.map +1 -0
- package/dist/scenarios/browser-run-plan-projection.js +264 -0
- package/dist/scenarios/browser-run-plan-projection.js.map +1 -0
- package/dist/scenarios/credential-ref.d.ts +102 -0
- package/dist/scenarios/credential-ref.d.ts.map +1 -0
- package/dist/scenarios/credential-ref.js +148 -0
- package/dist/scenarios/credential-ref.js.map +1 -0
- package/dist/scenarios/index.d.ts +7 -0
- package/dist/scenarios/index.d.ts.map +1 -0
- package/dist/scenarios/index.js +7 -0
- package/dist/scenarios/index.js.map +1 -0
- package/dist/scenarios/parse.d.ts +18 -0
- package/dist/scenarios/parse.d.ts.map +1 -0
- package/dist/scenarios/parse.js +212 -0
- package/dist/scenarios/parse.js.map +1 -0
- package/dist/scenarios/scenario.d.ts +93 -0
- package/dist/scenarios/scenario.d.ts.map +1 -0
- package/dist/scenarios/scenario.js +27 -0
- package/dist/scenarios/scenario.js.map +1 -0
- package/dist/scenarios/secret-ref.d.ts +86 -0
- package/dist/scenarios/secret-ref.d.ts.map +1 -0
- package/dist/scenarios/secret-ref.js +125 -0
- package/dist/scenarios/secret-ref.js.map +1 -0
- package/dist/scenarios/storage.d.ts +21 -0
- package/dist/scenarios/storage.d.ts.map +1 -0
- package/dist/scenarios/storage.js +103 -0
- package/dist/scenarios/storage.js.map +1 -0
- package/dist/server.d.ts +61 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +642 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +356 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +779 -0
- package/dist/session.js.map +1 -0
- package/dist/tools/alias-kit.d.ts +29 -0
- package/dist/tools/alias-kit.d.ts.map +1 -0
- package/dist/tools/alias-kit.js +21 -0
- package/dist/tools/alias-kit.js.map +1 -0
- package/dist/tools/aliases.d.ts +42 -0
- package/dist/tools/aliases.d.ts.map +1 -0
- package/dist/tools/aliases.js +52 -0
- package/dist/tools/aliases.js.map +1 -0
- package/dist/tools/analyze-workspace.d.ts +52 -0
- package/dist/tools/analyze-workspace.d.ts.map +1 -0
- package/dist/tools/analyze-workspace.js +207 -0
- package/dist/tools/analyze-workspace.js.map +1 -0
- package/dist/tools/analyze.d.ts +163 -0
- package/dist/tools/analyze.d.ts.map +1 -0
- package/dist/tools/analyze.js +724 -0
- package/dist/tools/analyze.js.map +1 -0
- package/dist/tools/browser-click.d.ts +18 -0
- package/dist/tools/browser-click.d.ts.map +1 -0
- package/dist/tools/browser-click.js +110 -0
- package/dist/tools/browser-click.js.map +1 -0
- package/dist/tools/browser-close-session.d.ts +39 -0
- package/dist/tools/browser-close-session.d.ts.map +1 -0
- package/dist/tools/browser-close-session.js +120 -0
- package/dist/tools/browser-close-session.js.map +1 -0
- package/dist/tools/browser-fill.d.ts +18 -0
- package/dist/tools/browser-fill.d.ts.map +1 -0
- package/dist/tools/browser-fill.js +117 -0
- package/dist/tools/browser-fill.js.map +1 -0
- package/dist/tools/browser-navigate.d.ts +15 -0
- package/dist/tools/browser-navigate.d.ts.map +1 -0
- package/dist/tools/browser-navigate.js +106 -0
- package/dist/tools/browser-navigate.js.map +1 -0
- package/dist/tools/browser-read.d.ts +33 -0
- package/dist/tools/browser-read.d.ts.map +1 -0
- package/dist/tools/browser-read.js +105 -0
- package/dist/tools/browser-read.js.map +1 -0
- package/dist/tools/browser-run-scenario.d.ts +28 -0
- package/dist/tools/browser-run-scenario.d.ts.map +1 -0
- package/dist/tools/browser-run-scenario.js +238 -0
- package/dist/tools/browser-run-scenario.js.map +1 -0
- package/dist/tools/browser-save-scenario.d.ts +19 -0
- package/dist/tools/browser-save-scenario.d.ts.map +1 -0
- package/dist/tools/browser-save-scenario.js +447 -0
- package/dist/tools/browser-save-scenario.js.map +1 -0
- package/dist/tools/browser-select.d.ts +20 -0
- package/dist/tools/browser-select.d.ts.map +1 -0
- package/dist/tools/browser-select.js +120 -0
- package/dist/tools/browser-select.js.map +1 -0
- package/dist/tools/browser-snapshot.d.ts +46 -0
- package/dist/tools/browser-snapshot.d.ts.map +1 -0
- package/dist/tools/browser-snapshot.js +127 -0
- package/dist/tools/browser-snapshot.js.map +1 -0
- package/dist/tools/browser-start-session.d.ts +24 -0
- package/dist/tools/browser-start-session.d.ts.map +1 -0
- package/dist/tools/browser-start-session.js +653 -0
- package/dist/tools/browser-start-session.js.map +1 -0
- package/dist/tools/browser-submit.d.ts +18 -0
- package/dist/tools/browser-submit.d.ts.map +1 -0
- package/dist/tools/browser-submit.js +106 -0
- package/dist/tools/browser-submit.js.map +1 -0
- package/dist/tools/browser-type.d.ts +18 -0
- package/dist/tools/browser-type.d.ts.map +1 -0
- package/dist/tools/browser-type.js +115 -0
- package/dist/tools/browser-type.js.map +1 -0
- package/dist/tools/browser-wait-for.d.ts +45 -0
- package/dist/tools/browser-wait-for.d.ts.map +1 -0
- package/dist/tools/browser-wait-for.js +164 -0
- package/dist/tools/browser-wait-for.js.map +1 -0
- package/dist/tools/browser.d.ts +69 -0
- package/dist/tools/browser.d.ts.map +1 -0
- package/dist/tools/browser.js +326 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/chain.d.ts +41 -0
- package/dist/tools/chain.d.ts.map +1 -0
- package/dist/tools/chain.js +101 -0
- package/dist/tools/chain.js.map +1 -0
- package/dist/tools/change-scope.d.ts +25 -0
- package/dist/tools/change-scope.d.ts.map +1 -0
- package/dist/tools/change-scope.js +103 -0
- package/dist/tools/change-scope.js.map +1 -0
- package/dist/tools/code-context.d.ts +83 -0
- package/dist/tools/code-context.d.ts.map +1 -0
- package/dist/tools/code-context.js +432 -0
- package/dist/tools/code-context.js.map +1 -0
- package/dist/tools/contracts.d.ts +74 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +214 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/cross-pr.d.ts +178 -0
- package/dist/tools/cross-pr.d.ts.map +1 -0
- package/dist/tools/cross-pr.js +362 -0
- package/dist/tools/cross-pr.js.map +1 -0
- package/dist/tools/disclosure.d.ts +75 -0
- package/dist/tools/disclosure.d.ts.map +1 -0
- package/dist/tools/disclosure.js +325 -0
- package/dist/tools/disclosure.js.map +1 -0
- package/dist/tools/draw-conclusion.d.ts +55 -0
- package/dist/tools/draw-conclusion.d.ts.map +1 -0
- package/dist/tools/draw-conclusion.js +243 -0
- package/dist/tools/draw-conclusion.js.map +1 -0
- package/dist/tools/git-context.d.ts +39 -0
- package/dist/tools/git-context.d.ts.map +1 -0
- package/dist/tools/git-context.js +374 -0
- package/dist/tools/git-context.js.map +1 -0
- package/dist/tools/git-diff.d.ts +45 -0
- package/dist/tools/git-diff.d.ts.map +1 -0
- package/dist/tools/git-diff.js +338 -0
- package/dist/tools/git-diff.js.map +1 -0
- package/dist/tools/git-history.d.ts +16 -0
- package/dist/tools/git-history.d.ts.map +1 -0
- package/dist/tools/git-history.js +106 -0
- package/dist/tools/git-history.js.map +1 -0
- package/dist/tools/history.d.ts +34 -0
- package/dist/tools/history.d.ts.map +1 -0
- package/dist/tools/history.js +121 -0
- package/dist/tools/history.js.map +1 -0
- package/dist/tools/impact.d.ts +46 -0
- package/dist/tools/impact.d.ts.map +1 -0
- package/dist/tools/impact.js +225 -0
- package/dist/tools/impact.js.map +1 -0
- package/dist/tools/index.d.ts +59 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +70 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kit.d.ts +165 -0
- package/dist/tools/kit.d.ts.map +1 -0
- package/dist/tools/kit.js +153 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/link-workspace.d.ts +24 -0
- package/dist/tools/link-workspace.d.ts.map +1 -0
- package/dist/tools/link-workspace.js +131 -0
- package/dist/tools/link-workspace.js.map +1 -0
- package/dist/tools/lookup.d.ts +19 -0
- package/dist/tools/lookup.d.ts.map +1 -0
- package/dist/tools/lookup.js +63 -0
- package/dist/tools/lookup.js.map +1 -0
- package/dist/tools/mark-incident.d.ts +28 -0
- package/dist/tools/mark-incident.d.ts.map +1 -0
- package/dist/tools/mark-incident.js +171 -0
- package/dist/tools/mark-incident.js.map +1 -0
- package/dist/tools/merged-browser.d.ts +11 -0
- package/dist/tools/merged-browser.d.ts.map +1 -0
- package/dist/tools/merged-browser.js +20 -0
- package/dist/tools/merged-browser.js.map +1 -0
- package/dist/tools/merged-context.d.ts +11 -0
- package/dist/tools/merged-context.d.ts.map +1 -0
- package/dist/tools/merged-context.js +21 -0
- package/dist/tools/merged-context.js.map +1 -0
- package/dist/tools/merged-observe.d.ts +13 -0
- package/dist/tools/merged-observe.d.ts.map +1 -0
- package/dist/tools/merged-observe.js +21 -0
- package/dist/tools/merged-observe.js.map +1 -0
- package/dist/tools/merged-workspace.d.ts +14 -0
- package/dist/tools/merged-workspace.d.ts.map +1 -0
- package/dist/tools/merged-workspace.js +21 -0
- package/dist/tools/merged-workspace.js.map +1 -0
- package/dist/tools/observe-runtime.d.ts +262 -0
- package/dist/tools/observe-runtime.d.ts.map +1 -0
- package/dist/tools/observe-runtime.js +1966 -0
- package/dist/tools/observe-runtime.js.map +1 -0
- package/dist/tools/observe-tests.d.ts +57 -0
- package/dist/tools/observe-tests.d.ts.map +1 -0
- package/dist/tools/observe-tests.js +391 -0
- package/dist/tools/observe-tests.js.map +1 -0
- package/dist/tools/observe.d.ts +28 -0
- package/dist/tools/observe.d.ts.map +1 -0
- package/dist/tools/observe.js +291 -0
- package/dist/tools/observe.js.map +1 -0
- package/dist/tools/pr-analysis.d.ts +189 -0
- package/dist/tools/pr-analysis.d.ts.map +1 -0
- package/dist/tools/pr-analysis.js +365 -0
- package/dist/tools/pr-analysis.js.map +1 -0
- package/dist/tools/pre-push.d.ts +168 -0
- package/dist/tools/pre-push.d.ts.map +1 -0
- package/dist/tools/pre-push.js +416 -0
- package/dist/tools/pre-push.js.map +1 -0
- package/dist/tools/predict-reach.d.ts +57 -0
- package/dist/tools/predict-reach.d.ts.map +1 -0
- package/dist/tools/predict-reach.js +183 -0
- package/dist/tools/predict-reach.js.map +1 -0
- package/dist/tools/propagation.d.ts +37 -0
- package/dist/tools/propagation.d.ts.map +1 -0
- package/dist/tools/propagation.js +162 -0
- package/dist/tools/propagation.js.map +1 -0
- package/dist/tools/propose-journey.d.ts +16 -0
- package/dist/tools/propose-journey.d.ts.map +1 -0
- package/dist/tools/propose-journey.js +132 -0
- package/dist/tools/propose-journey.js.map +1 -0
- package/dist/tools/prove-reach.d.ts +74 -0
- package/dist/tools/prove-reach.d.ts.map +1 -0
- package/dist/tools/prove-reach.js +274 -0
- package/dist/tools/prove-reach.js.map +1 -0
- package/dist/tools/questions.d.ts +30 -0
- package/dist/tools/questions.d.ts.map +1 -0
- package/dist/tools/questions.js +230 -0
- package/dist/tools/questions.js.map +1 -0
- package/dist/tools/refusal-fetch.d.ts +35 -0
- package/dist/tools/refusal-fetch.d.ts.map +1 -0
- package/dist/tools/refusal-fetch.js +123 -0
- package/dist/tools/refusal-fetch.js.map +1 -0
- package/dist/tools/runtime-incident.d.ts +33 -0
- package/dist/tools/runtime-incident.d.ts.map +1 -0
- package/dist/tools/runtime-incident.js +67 -0
- package/dist/tools/runtime-incident.js.map +1 -0
- package/dist/tools/runtime-journey-drive.d.ts +102 -0
- package/dist/tools/runtime-journey-drive.d.ts.map +1 -0
- package/dist/tools/runtime-journey-drive.js +247 -0
- package/dist/tools/runtime-journey-drive.js.map +1 -0
- package/dist/tools/scope.d.ts +45 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +217 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/similar-incidents.d.ts +43 -0
- package/dist/tools/similar-incidents.d.ts.map +1 -0
- package/dist/tools/similar-incidents.js +190 -0
- package/dist/tools/similar-incidents.js.map +1 -0
- package/dist/tools/validate.d.ts +133 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +488 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/verb.d.ts +46 -0
- package/dist/tools/verb.d.ts.map +1 -0
- package/dist/tools/verb.js +78 -0
- package/dist/tools/verb.js.map +1 -0
- package/dist/tools/verification-status.d.ts +38 -0
- package/dist/tools/verification-status.d.ts.map +1 -0
- package/dist/tools/verification-status.js +203 -0
- package/dist/tools/verification-status.js.map +1 -0
- package/dist/tools/verify-claim.d.ts +29 -0
- package/dist/tools/verify-claim.d.ts.map +1 -0
- package/dist/tools/verify-claim.js +218 -0
- package/dist/tools/verify-claim.js.map +1 -0
- package/dist/tools/workspace.d.ts +28 -0
- package/dist/tools/workspace.d.ts.map +1 -0
- package/dist/tools/workspace.js +94 -0
- package/dist/tools/workspace.js.map +1 -0
- package/dist/transport.d.ts +32 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +85 -0
- package/dist/transport.js.map +1 -0
- package/dist/workspace-index.d.ts +164 -0
- package/dist/workspace-index.d.ts.map +1 -0
- package/dist/workspace-index.js +381 -0
- package/dist/workspace-index.js.map +1 -0
- package/package.json +3 -3
package/dist/session.js
ADDED
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One repo, one graph, one process — owns the SQLite handle, config, sources; tools
|
|
3
|
+
* own nothing. `.descry/config.json` is optional, defaults to local-first, except the
|
|
4
|
+
* source list (this repo names no adapter, `registry.ts`): unconfigured sessions can
|
|
5
|
+
* query, not build. Graph opens lazily so a missing one fails as a tool result, not a crash. */
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
9
|
+
import { isConfirmedFactScope } from "@descryy/ir";
|
|
10
|
+
import { computeGraphFreshness, createStoreProvider, distinctFilePaths, gitHead, isGitRepository, NodeSqliteDriver, openStore, rebuildStaleGraph, } from "@descryy/core";
|
|
11
|
+
import { BROWSER_BACKEND_NOT_INSTALLED } from "./browser/provider.js";
|
|
12
|
+
import { createBrowserSessionRegistry } from "./browser/registry.js";
|
|
13
|
+
import { normaliseSpec } from "./registry.js";
|
|
14
|
+
export const CONFIG_RELATIVE_PATH = join(".descry", "config.json");
|
|
15
|
+
export const DEFAULT_GRAPH_RELATIVE_PATH = join(".descry", "graph.db");
|
|
16
|
+
function parseSavedReadiness(raw, configPath, service) {
|
|
17
|
+
const e = (typeof raw === "object" && raw !== null ? raw : {});
|
|
18
|
+
const kind = e["kind"];
|
|
19
|
+
if (kind !== "http" && kind !== "tcp-port" && kind !== "command") {
|
|
20
|
+
throw new ConfigError(`${configPath}: savedStart.services.${service}.readiness.kind must be "http", "tcp-port" or "command"`);
|
|
21
|
+
}
|
|
22
|
+
const path = typeof e["path"] === "string" ? e["path"] : undefined;
|
|
23
|
+
const expectedStatus = typeof e["expectedStatus"] === "number" ? e["expectedStatus"] : undefined;
|
|
24
|
+
const host = typeof e["host"] === "string" ? e["host"] : undefined;
|
|
25
|
+
const command = typeof e["command"] === "string" ? e["command"] : undefined;
|
|
26
|
+
const rawArgs = e["args"];
|
|
27
|
+
const args = Array.isArray(rawArgs) && rawArgs.every((a) => typeof a === "string") ? rawArgs : undefined;
|
|
28
|
+
const timeoutMs = typeof e["timeoutMs"] === "number" ? e["timeoutMs"] : undefined;
|
|
29
|
+
return {
|
|
30
|
+
kind,
|
|
31
|
+
...(path === undefined ? {} : { path }),
|
|
32
|
+
...(expectedStatus === undefined ? {} : { expectedStatus }),
|
|
33
|
+
...(host === undefined ? {} : { host }),
|
|
34
|
+
...(command === undefined ? {} : { command }),
|
|
35
|
+
...(args === undefined ? {} : { args }),
|
|
36
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** `savedStart`'s reader, paired with `writeSavedStart` below. Lenient the same way
|
|
40
|
+
* `parseConfirmedIncidents` is about a malformed sub-object — coerced to `{}` rather
|
|
41
|
+
* than thrown on — because this file's own writer is the only thing that ever
|
|
42
|
+
* produces this shape; a hand-edited config that gets it wrong is refused field by
|
|
43
|
+
* field below, same as every other section here. */
|
|
44
|
+
function parseSavedStart(raw, configPath) {
|
|
45
|
+
if (raw === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
const e = (typeof raw === "object" && raw !== null ? raw : {});
|
|
48
|
+
const savedAt = e["savedAt"];
|
|
49
|
+
if (typeof savedAt !== "string" || savedAt === "") {
|
|
50
|
+
throw new ConfigError(`${configPath}: "savedStart.savedAt" must be a non-empty string`);
|
|
51
|
+
}
|
|
52
|
+
const servicesRaw = e["services"];
|
|
53
|
+
if (typeof servicesRaw !== "object" || servicesRaw === null || Array.isArray(servicesRaw)) {
|
|
54
|
+
throw new ConfigError(`${configPath}: "savedStart.services" must be an object`);
|
|
55
|
+
}
|
|
56
|
+
const services = {};
|
|
57
|
+
for (const [name, value] of Object.entries(servicesRaw)) {
|
|
58
|
+
const v = (typeof value === "object" && value !== null ? value : {});
|
|
59
|
+
const command = typeof v["command"] === "string" ? v["command"] : undefined;
|
|
60
|
+
const cwd = v["cwd"];
|
|
61
|
+
if (typeof cwd !== "string" || cwd === "") {
|
|
62
|
+
throw new ConfigError(`${configPath}: "savedStart.services.${name}.cwd" must be a non-empty string`);
|
|
63
|
+
}
|
|
64
|
+
const port = typeof v["port"] === "number" ? v["port"] : undefined;
|
|
65
|
+
const rawDependsOn = v["dependsOn"];
|
|
66
|
+
const dependsOn = Array.isArray(rawDependsOn) && rawDependsOn.every((d) => typeof d === "string")
|
|
67
|
+
? rawDependsOn
|
|
68
|
+
: undefined;
|
|
69
|
+
const rawEnvNames = v["envNames"];
|
|
70
|
+
const envNames = Array.isArray(rawEnvNames) && rawEnvNames.every((n) => typeof n === "string")
|
|
71
|
+
? rawEnvNames
|
|
72
|
+
: undefined;
|
|
73
|
+
const attachRaw = v["attach"];
|
|
74
|
+
let attach;
|
|
75
|
+
if (attachRaw !== undefined) {
|
|
76
|
+
const a = (typeof attachRaw === "object" && attachRaw !== null ? attachRaw : {});
|
|
77
|
+
if (typeof a["pid"] !== "number" || typeof a["logFilePath"] !== "string") {
|
|
78
|
+
throw new ConfigError(`${configPath}: "savedStart.services.${name}.attach" must have a numeric "pid" and a string "logFilePath"`);
|
|
79
|
+
}
|
|
80
|
+
attach = { pid: a["pid"], logFilePath: a["logFilePath"] };
|
|
81
|
+
}
|
|
82
|
+
services[name] = {
|
|
83
|
+
...(command === undefined ? {} : { command }),
|
|
84
|
+
cwd,
|
|
85
|
+
...(port === undefined ? {} : { port }),
|
|
86
|
+
...(dependsOn === undefined ? {} : { dependsOn }),
|
|
87
|
+
...(envNames === undefined ? {} : { envNames }),
|
|
88
|
+
...(attach === undefined ? {} : { attach }),
|
|
89
|
+
readiness: parseSavedReadiness(v["readiness"], configPath, name),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { services, savedAt };
|
|
93
|
+
}
|
|
94
|
+
function parseConfirmedIncidents(raw, configPath) {
|
|
95
|
+
if (raw === undefined)
|
|
96
|
+
return [];
|
|
97
|
+
if (!Array.isArray(raw)) {
|
|
98
|
+
throw new ConfigError(`${configPath}: "confirmedIncidents" must be an array`);
|
|
99
|
+
}
|
|
100
|
+
return raw.map((entry, index) => {
|
|
101
|
+
const e = entry;
|
|
102
|
+
for (const key of ["repo", "summary", "confirmedBy", "confirmedAt"]) {
|
|
103
|
+
if (typeof e[key] !== "string" || e[key] === "") {
|
|
104
|
+
throw new ConfigError(`${configPath}: confirmedIncidents[${index}].${key} must be a non-empty string`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const files = e["files"];
|
|
108
|
+
if (!Array.isArray(files) || files.length === 0 || files.some((f) => typeof f !== "string" || f === "")) {
|
|
109
|
+
throw new ConfigError(`${configPath}: confirmedIncidents[${index}].files must be a non-empty array of paths — ` +
|
|
110
|
+
"an incident naming no file has nothing to attach it to");
|
|
111
|
+
}
|
|
112
|
+
for (const key of ["errorText", "fixedAtCommit", "observedInRun"]) {
|
|
113
|
+
if (e[key] !== undefined && (typeof e[key] !== "string" || e[key] === "")) {
|
|
114
|
+
throw new ConfigError(`${configPath}: confirmedIncidents[${index}].${key} must be a non-empty string when present`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const occurrences = e["occurrences"];
|
|
118
|
+
if (occurrences !== undefined &&
|
|
119
|
+
(!Array.isArray(occurrences) ||
|
|
120
|
+
occurrences.length === 0 ||
|
|
121
|
+
occurrences.some((o) => typeof o !== "string" || o === ""))) {
|
|
122
|
+
throw new ConfigError(`${configPath}: confirmedIncidents[${index}].occurrences must be a non-empty array of timestamps when present`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
repo: e["repo"],
|
|
126
|
+
summary: e["summary"],
|
|
127
|
+
files: files,
|
|
128
|
+
...(e["errorText"] === undefined ? {} : { errorText: e["errorText"] }),
|
|
129
|
+
...(e["fixedAtCommit"] === undefined ? {} : { fixedAtCommit: e["fixedAtCommit"] }),
|
|
130
|
+
...(e["observedInRun"] === undefined ? {} : { observedInRun: e["observedInRun"] }),
|
|
131
|
+
confirmedBy: e["confirmedBy"],
|
|
132
|
+
confirmedAt: e["confirmedAt"],
|
|
133
|
+
...(occurrences === undefined ? {} : { occurrences: occurrences }),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function parseConfirmedFacts(raw, configPath) {
|
|
138
|
+
if (raw === undefined)
|
|
139
|
+
return [];
|
|
140
|
+
if (!Array.isArray(raw)) {
|
|
141
|
+
throw new ConfigError(`${configPath}: "confirmedFacts" must be an array`);
|
|
142
|
+
}
|
|
143
|
+
return raw.map((entry, index) => {
|
|
144
|
+
const e = entry;
|
|
145
|
+
for (const key of ["repo", "blockedBy", "value", "confirmedBy", "confirmedAt"]) {
|
|
146
|
+
if (typeof e[key] !== "string" || e[key] === "") {
|
|
147
|
+
throw new ConfigError(`${configPath}: confirmedFacts[${index}].${key} must be a non-empty string`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const key of ["environment", "confirmedAtCommit", "workspace"]) {
|
|
151
|
+
if (e[key] !== undefined && (typeof e[key] !== "string" || e[key] === "")) {
|
|
152
|
+
throw new ConfigError(`${configPath}: confirmedFacts[${index}].${key} must be a non-empty string when present`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (e["scope"] !== undefined && !isConfirmedFactScope(e["scope"])) {
|
|
156
|
+
throw new ConfigError(`${configPath}: confirmedFacts[${index}].scope must be "repo" or "workspace" when present`);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
repo: e["repo"],
|
|
160
|
+
blockedBy: e["blockedBy"],
|
|
161
|
+
value: e["value"],
|
|
162
|
+
...(e["environment"] === undefined ? {} : { environment: e["environment"] }),
|
|
163
|
+
confirmedBy: e["confirmedBy"],
|
|
164
|
+
confirmedAt: e["confirmedAt"],
|
|
165
|
+
// Round-tripped rather than dropped — `confirmedAtCommit` was written by
|
|
166
|
+
// `writeConfirmedFact`; discarding it on reload would silence the staleness disclosure.
|
|
167
|
+
...(e["confirmedAtCommit"] === undefined ? {} : { confirmedAtCommit: e["confirmedAtCommit"] }),
|
|
168
|
+
...(e["scope"] === undefined ? {} : { scope: e["scope"] }),
|
|
169
|
+
...(e["workspace"] === undefined ? {} : { workspace: e["workspace"] }),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** `(repo, blockedBy, environment)` — `undefined` environment is its own value, never a wildcard. */
|
|
174
|
+
function sameFactKey(a, b) {
|
|
175
|
+
return a.repo === b.repo && a.blockedBy === b.blockedBy && a.environment === b.environment;
|
|
176
|
+
}
|
|
177
|
+
export class ConfigError extends Error {
|
|
178
|
+
constructor(message) {
|
|
179
|
+
super(message);
|
|
180
|
+
this.name = "ConfigError";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export async function loadConfig(repoPath) {
|
|
184
|
+
const configPath = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
185
|
+
let raw = {};
|
|
186
|
+
let found = null;
|
|
187
|
+
try {
|
|
188
|
+
raw = JSON.parse(await readFile(configPath, "utf8"));
|
|
189
|
+
found = configPath;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const code = error.code;
|
|
193
|
+
if (code !== "ENOENT") {
|
|
194
|
+
// A config that exists and can't be parsed is refused, not defaulted past —
|
|
195
|
+
// silently falling back would run the session under settings the user thinks they overrode.
|
|
196
|
+
throw new ConfigError(`${configPath} exists but could not be read: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (raw.sources !== undefined && !Array.isArray(raw.sources)) {
|
|
200
|
+
throw new ConfigError(`${configPath}: "sources" must be an array`);
|
|
201
|
+
}
|
|
202
|
+
const sources = (raw.sources ?? []).map((entry, index) => {
|
|
203
|
+
if (typeof entry === "string")
|
|
204
|
+
return normaliseSpec(entry);
|
|
205
|
+
if (typeof entry === "object" && entry !== null && typeof entry.module === "string") {
|
|
206
|
+
return entry;
|
|
207
|
+
}
|
|
208
|
+
throw new ConfigError(`${configPath}: sources[${index}] must be a module specifier or an object with a "module" field`);
|
|
209
|
+
});
|
|
210
|
+
const workspace = typeof raw.workspace === "string" && raw.workspace !== "" ? raw.workspace : undefined;
|
|
211
|
+
const workspaceRepos = parseWorkspaceRepos(raw.workspaceRepos, configPath);
|
|
212
|
+
const savedStart = parseSavedStart(raw.savedStart, configPath);
|
|
213
|
+
return {
|
|
214
|
+
// Directory name is a defensible default and poor identity — changes on clone,
|
|
215
|
+
// and every repo-scoped node id changes with it. Disclosed in `analyze`, not
|
|
216
|
+
// discovered when two machines produce different graphs.
|
|
217
|
+
repo: typeof raw.repo === "string" && raw.repo !== "" ? raw.repo : basename(resolve(repoPath)),
|
|
218
|
+
workspace,
|
|
219
|
+
graph: typeof raw.graph === "string" && raw.graph !== "" ? raw.graph : DEFAULT_GRAPH_RELATIVE_PATH,
|
|
220
|
+
sources,
|
|
221
|
+
confirmedFacts: parseConfirmedFacts(raw.confirmedFacts, configPath),
|
|
222
|
+
confirmedIncidents: parseConfirmedIncidents(raw.confirmedIncidents, configPath),
|
|
223
|
+
workspaceRepos,
|
|
224
|
+
linkedFacts: await readLinkedFacts(repoPath, workspace, workspaceRepos),
|
|
225
|
+
...(savedStart === undefined ? {} : { savedStart }),
|
|
226
|
+
configPath: found,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Fields compared for staleness (13.3.16 / N2). Deliberately excludes `linkedFacts`:
|
|
231
|
+
* it is derived from sibling repos' own files at load time, not something a local
|
|
232
|
+
* edit to *this* repo's config changes, and diffing it here would double-count
|
|
233
|
+
* `workspaceRepos` drift under a second name.
|
|
234
|
+
*/
|
|
235
|
+
const DRIFT_FIELDS = [
|
|
236
|
+
"repo",
|
|
237
|
+
"workspace",
|
|
238
|
+
"graph",
|
|
239
|
+
"sources",
|
|
240
|
+
"confirmedFacts",
|
|
241
|
+
"confirmedIncidents",
|
|
242
|
+
"workspaceRepos",
|
|
243
|
+
"savedStart",
|
|
244
|
+
"configPath",
|
|
245
|
+
];
|
|
246
|
+
function driftedKeys(onDisk, loaded) {
|
|
247
|
+
return DRIFT_FIELDS.filter((key) => JSON.stringify(onDisk[key]) !== JSON.stringify(loaded[key]));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 13.3.16 (N2): a count match on `sources` is not a content match — two sessions
|
|
251
|
+
* swapping one adapter for another, or changing an adapter's options, leave
|
|
252
|
+
* `sources.length` unchanged, which is exactly the gap that let a concurrent edit
|
|
253
|
+
* go undetected and left a graph stuck at R0 with no disclosure of why. Every
|
|
254
|
+
* field is compared by value, and the note names which ones actually changed —
|
|
255
|
+
* `graph`, `confirmedFacts`/`confirmedIncidents` and `workspaceRepos` were
|
|
256
|
+
* previously not compared at all.
|
|
257
|
+
*/
|
|
258
|
+
export function configDriftNote(onDisk, loaded) {
|
|
259
|
+
if (onDisk === null)
|
|
260
|
+
return undefined;
|
|
261
|
+
const changed = driftedKeys(onDisk, loaded);
|
|
262
|
+
if (changed.length === 0)
|
|
263
|
+
return undefined;
|
|
264
|
+
return ("The on-disk .descry/config.json no longer matches what this server loaded at startup " +
|
|
265
|
+
`(changed: ${changed.join(", ")}). Configuration is read once per process, not on every call ` +
|
|
266
|
+
"(session.ts) — this run is still using the configuration this server started with, not the " +
|
|
267
|
+
"current file. Restart the MCP server for on-disk changes to take effect; the notes below " +
|
|
268
|
+
"describe the stale, running configuration, not what is actually on disk now.");
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 13.3.16 (N2) steps 2-3: called once per tool call from `server.ts`, not only from
|
|
272
|
+
* `analyze`, so every reply discloses a stale session rather than the one tool that
|
|
273
|
+
* happened to re-read the config. A config that cannot even be read right now
|
|
274
|
+
* (deleted mid-session, or edited into something `loadConfig` refuses) is itself
|
|
275
|
+
* drift and must not read as "no drift" merely because there is nothing to diff
|
|
276
|
+
* against — the previous `.catch(() => null)` collapsed that case into silence.
|
|
277
|
+
*/
|
|
278
|
+
export async function checkConfigDrift(session) {
|
|
279
|
+
if (!existsSync(session.repoPath))
|
|
280
|
+
return { stale: false, note: undefined };
|
|
281
|
+
try {
|
|
282
|
+
const onDisk = await loadConfig(session.repoPath);
|
|
283
|
+
const note = configDriftNote(onDisk, session.config);
|
|
284
|
+
return { stale: note !== undefined, note };
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
288
|
+
return {
|
|
289
|
+
stale: true,
|
|
290
|
+
note: `The on-disk .descry/config.json could not be read as of this call (${reason}). This run ` +
|
|
291
|
+
"is still using the configuration loaded at startup; whether that configuration still " +
|
|
292
|
+
"matches what is on disk now cannot be determined.",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Lane "map-freshness" item 3 — starting value only, **not measured**
|
|
298
|
+
* (lightweight mode: this lane could not run a 1-file-change refresh against
|
|
299
|
+
* a large reference repo and time it). `DEC-NEXT-auto-refresh-budget` records
|
|
300
|
+
* this exact number and says plainly that it is a guess, not a measurement —
|
|
301
|
+
* whoever next runs `npm run build && npm run lint && node --test` with a
|
|
302
|
+
* real reference repo available should replace it with a measured p50/p95
|
|
303
|
+
* and update that decision file rather than this comment alone.
|
|
304
|
+
*/
|
|
305
|
+
export const AUTO_REFRESH_BUDGET_MS = 10_000;
|
|
306
|
+
/**
|
|
307
|
+
* Lane "map-freshness" item 1 — whether the stored graph still matches the
|
|
308
|
+
* repository on disk, read once per call from `server.ts`, the same place
|
|
309
|
+
* `checkConfigDrift` is read from. `undefined` when there is nothing honest
|
|
310
|
+
* to report: the graph is empty (`baseStamp().commitSha === null` — nothing
|
|
311
|
+
* to be fresh or stale *relative to*, same reasoning `computeGraphFreshness`
|
|
312
|
+
* itself already documents) or the stamp/driver could not be read at all
|
|
313
|
+
* (`freshnessField()` in `render.ts` reports that as `unknown` once a real
|
|
314
|
+
* `commitSha` exists downstream, never as a false `current`).
|
|
315
|
+
*
|
|
316
|
+
* Deliberately not a `Session` method (`Session#checkFreshness()` in an
|
|
317
|
+
* earlier note here was this function's own working name) — `checkConfigDrift`
|
|
318
|
+
* is a standalone function taking `session` for the same reason: it needs no
|
|
319
|
+
* private state this class doesn't already expose, and keeping it a plain
|
|
320
|
+
* function keeps this module free of a growing method surface for what are,
|
|
321
|
+
* one at a time, one-shot per-call reads.
|
|
322
|
+
*/
|
|
323
|
+
export async function checkFreshness(session) {
|
|
324
|
+
let commitSha;
|
|
325
|
+
try {
|
|
326
|
+
commitSha = session.provider().baseStamp().commitSha;
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
if (commitSha === null)
|
|
332
|
+
return undefined;
|
|
333
|
+
try {
|
|
334
|
+
const indexedFiles = distinctFilePaths(session.store().driver);
|
|
335
|
+
return await computeGraphFreshness({ repoPath: session.repoPath, commitSha, indexedFiles });
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function parseWorkspaceRepos(raw, configPath) {
|
|
342
|
+
if (raw === undefined)
|
|
343
|
+
return [];
|
|
344
|
+
if (!Array.isArray(raw)) {
|
|
345
|
+
throw new ConfigError(`${configPath}: "workspaceRepos" must be an array`);
|
|
346
|
+
}
|
|
347
|
+
return raw.map((entry, index) => {
|
|
348
|
+
const e = entry;
|
|
349
|
+
for (const key of ["path", "repo"]) {
|
|
350
|
+
if (typeof e[key] !== "string" || e[key] === "") {
|
|
351
|
+
throw new ConfigError(`${configPath}: workspaceRepos[${index}].${key} must be a non-empty string`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return { path: e["path"], repo: e["repo"] };
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/** Workspace-scoped facts from this workspace's *other* repos, one level only (no
|
|
358
|
+
* recursive follow of a sibling's own `workspaceRepos`). Every failure here is silent
|
|
359
|
+
* by design — a broken/uncloned sibling must not block this session; `questions` discloses it. */
|
|
360
|
+
async function readLinkedFacts(repoPath, workspace, repos) {
|
|
361
|
+
if (workspace === undefined || repos.length === 0)
|
|
362
|
+
return [];
|
|
363
|
+
const self = resolve(repoPath);
|
|
364
|
+
const out = [];
|
|
365
|
+
for (const entry of repos) {
|
|
366
|
+
if (resolve(entry.path) === self)
|
|
367
|
+
continue;
|
|
368
|
+
const path = join(entry.path, CONFIG_RELATIVE_PATH);
|
|
369
|
+
let facts;
|
|
370
|
+
try {
|
|
371
|
+
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
372
|
+
facts = parseConfirmedFacts(raw.confirmedFacts, path);
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
// Checked on the fact itself, not inferred from being found in a linked repo —
|
|
378
|
+
// a repo-scoped fact in a linked config still does not cross.
|
|
379
|
+
for (const fact of facts) {
|
|
380
|
+
if (fact.scope === "workspace" && fact.workspace === workspace)
|
|
381
|
+
out.push(fact);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return out;
|
|
385
|
+
}
|
|
386
|
+
/** `.descry/config.json`'s writer, paired with `loadConfig`. One writer used by both
|
|
387
|
+
* `descry init` and `link_workspace` so they can't drift into writing different
|
|
388
|
+
* shapes. Patches `repo`/`workspace` only — other fields survive untouched. */
|
|
389
|
+
export async function writeConfig(repoPath, patch) {
|
|
390
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
391
|
+
let existing = {};
|
|
392
|
+
try {
|
|
393
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
// An unparseable config is fixed here (start from empty), unlike `loadConfig`
|
|
397
|
+
// which refuses — this writer's job is to repair, not run a session. ENOENT
|
|
398
|
+
// takes the same path.
|
|
399
|
+
void error;
|
|
400
|
+
existing = {};
|
|
401
|
+
}
|
|
402
|
+
const previousWorkspace = typeof existing.workspace === "string" ? existing.workspace : undefined;
|
|
403
|
+
const next = { ...existing, repo: patch.repo };
|
|
404
|
+
if (patch.workspace !== undefined)
|
|
405
|
+
next.workspace = patch.workspace;
|
|
406
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
407
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
408
|
+
return { path, previousWorkspace };
|
|
409
|
+
}
|
|
410
|
+
/** `workspaceRepos`' writer — one writer per field a shared file owns (see
|
|
411
|
+
* `writeConfig`, `writeConfirmedFact`, `writeSources`). Replaces the whole list
|
|
412
|
+
* rather than merging: membership is one declaration made at one moment. */
|
|
413
|
+
export async function writeWorkspaceRepos(repoPath, repos) {
|
|
414
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
415
|
+
let existing = {};
|
|
416
|
+
try {
|
|
417
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
void error;
|
|
421
|
+
existing = {};
|
|
422
|
+
}
|
|
423
|
+
const next = {
|
|
424
|
+
...existing,
|
|
425
|
+
workspaceRepos: repos.map((r) => ({ path: r.path, repo: r.repo })),
|
|
426
|
+
};
|
|
427
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
428
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
429
|
+
return { path };
|
|
430
|
+
}
|
|
431
|
+
/** `confirmedFacts`' writer, separate from `writeConfig` (one writer per field). Keyed
|
|
432
|
+
* on exact `(repo, blockedBy, environment)` (DEC-223 §3) — undefined environment is its
|
|
433
|
+
* own key, not a wildcard. Same key replaces; a different environment adds a new fact. */
|
|
434
|
+
export async function writeConfirmedFact(repoPath, fact) {
|
|
435
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
436
|
+
let existing = {};
|
|
437
|
+
try {
|
|
438
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
void error;
|
|
442
|
+
existing = {};
|
|
443
|
+
}
|
|
444
|
+
const priorRaw = Array.isArray(existing["confirmedFacts"]) ? existing["confirmedFacts"] : [];
|
|
445
|
+
const prior = parseConfirmedFacts(priorRaw, path);
|
|
446
|
+
const replaced = prior.find((f) => sameFactKey(f, fact)) ?? null;
|
|
447
|
+
const rest = prior.filter((f) => !sameFactKey(f, fact));
|
|
448
|
+
const next = { ...existing, confirmedFacts: [...rest, fact] };
|
|
449
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
450
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
451
|
+
return { path, replaced };
|
|
452
|
+
}
|
|
453
|
+
/** `confirmedIncidents`' writer — appends a genuinely new incident, but merges a
|
|
454
|
+
* resubmission of the same content into the record already on file (UAT phase 5,
|
|
455
|
+
* F9). `confirmedAt` used to be the dedup key alongside `summary`, and a retried
|
|
456
|
+
* submission always carries a fresh timestamp — so every retry minted a second
|
|
457
|
+
* `INCIDENT` node, silently doubling its weight in blast-radius bias and
|
|
458
|
+
* `similar_incidents` scoring. Identity is now content (`repo` + `summary` +
|
|
459
|
+
* `errorText`, matching `confirmedIncidentId`), and a match appends this call's
|
|
460
|
+
* timestamp to `occurrences` and unions `files` rather than minting a new record.
|
|
461
|
+
* A retry and a genuine recurrence look identical from content alone; both are
|
|
462
|
+
* representable and countable this way, and neither doubles the graph. */
|
|
463
|
+
export async function writeConfirmedIncident(repoPath, incident) {
|
|
464
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
465
|
+
let existing = {};
|
|
466
|
+
try {
|
|
467
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
void error;
|
|
471
|
+
existing = {};
|
|
472
|
+
}
|
|
473
|
+
const priorRaw = Array.isArray(existing["confirmedIncidents"]) ? existing["confirmedIncidents"] : [];
|
|
474
|
+
const prior = parseConfirmedIncidents(priorRaw, path);
|
|
475
|
+
const same = (a) => a.repo === incident.repo &&
|
|
476
|
+
a.summary === incident.summary &&
|
|
477
|
+
(a.errorText ?? "") === (incident.errorText ?? "");
|
|
478
|
+
const match = prior.find(same) ?? null;
|
|
479
|
+
let next;
|
|
480
|
+
let record;
|
|
481
|
+
let occurrences;
|
|
482
|
+
if (match === null) {
|
|
483
|
+
next = [...prior, incident];
|
|
484
|
+
record = incident;
|
|
485
|
+
occurrences = 1;
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
record = {
|
|
489
|
+
...match,
|
|
490
|
+
// The incident may have been reported with a narrower or wider file list
|
|
491
|
+
// each time — the union is what it has ever been correlated to.
|
|
492
|
+
files: [...new Set([...match.files, ...incident.files])].sort(),
|
|
493
|
+
...(incident.fixedAtCommit === undefined ? {} : { fixedAtCommit: incident.fixedAtCommit }),
|
|
494
|
+
occurrences: [...(match.occurrences ?? [match.confirmedAt]), incident.confirmedAt],
|
|
495
|
+
};
|
|
496
|
+
next = [...prior.filter((i) => !same(i)), record];
|
|
497
|
+
occurrences = record.occurrences.length;
|
|
498
|
+
}
|
|
499
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
500
|
+
await writeFile(path, `${JSON.stringify({ ...existing, confirmedIncidents: next }, null, 2)}\n`, "utf8");
|
|
501
|
+
return { path, total: next.length, merged: match !== null, occurrences, record, records: next };
|
|
502
|
+
}
|
|
503
|
+
/** `savedStart`'s writer, one writer per field like the others. Called only by
|
|
504
|
+
* `observe-runtime.ts`'s `run`, and only once that run's execution state is
|
|
505
|
+
* `"COMPLETED"` — never on a failed run, so this field's presence already means
|
|
506
|
+
* "reached readiness at least once" without this writer having to track that
|
|
507
|
+
* itself. Overwrites unconditionally: the newest successful run is what is worth
|
|
508
|
+
* remembering, not a merge with whatever an older one declared. `services` must
|
|
509
|
+
* already exclude secret values by the time it reaches here — this writer trusts
|
|
510
|
+
* its caller the same way `writeConfirmedIncident` does, rather than re-scanning
|
|
511
|
+
* a shape it did not define. */
|
|
512
|
+
export async function writeSavedStart(repoPath, services) {
|
|
513
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
514
|
+
let existing = {};
|
|
515
|
+
try {
|
|
516
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
void error;
|
|
520
|
+
existing = {};
|
|
521
|
+
}
|
|
522
|
+
const savedAt = new Date().toISOString();
|
|
523
|
+
const next = { ...existing, savedStart: { services, savedAt } };
|
|
524
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
525
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
526
|
+
return { path, savedAt };
|
|
527
|
+
}
|
|
528
|
+
/** `sources`' writer, one writer per field like the others. Unconditional — the caller
|
|
529
|
+
* (`descry init`) decides whether overwriting is appropriate, not this primitive. */
|
|
530
|
+
export async function writeSources(repoPath, sources) {
|
|
531
|
+
const path = join(repoPath, CONFIG_RELATIVE_PATH);
|
|
532
|
+
let existing = {};
|
|
533
|
+
try {
|
|
534
|
+
existing = JSON.parse(await readFile(path, "utf8"));
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
void error;
|
|
538
|
+
existing = {};
|
|
539
|
+
}
|
|
540
|
+
const next = { ...existing, sources: [...sources] };
|
|
541
|
+
await mkdir(join(repoPath, ".descry"), { recursive: true });
|
|
542
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
543
|
+
return { path };
|
|
544
|
+
}
|
|
545
|
+
export class Session {
|
|
546
|
+
repoPath;
|
|
547
|
+
// Not `readonly` — `reloadConfig()` is the one caller-initiated exception to
|
|
548
|
+
// "configuration is read once per process" (UAT phase 5, §0 gap 2). Every
|
|
549
|
+
// other read of this field still sees whatever `reloadConfig()` last set,
|
|
550
|
+
// exactly as it did the config loaded at startup.
|
|
551
|
+
config;
|
|
552
|
+
#store = null;
|
|
553
|
+
#provider = null;
|
|
554
|
+
#browserSessions = null;
|
|
555
|
+
#browserDriverFactory = null;
|
|
556
|
+
/**
|
|
557
|
+
* Literal credential values a browser session on this connection was seeded
|
|
558
|
+
* with — B1.
|
|
559
|
+
*
|
|
560
|
+
* Held so the things this server *writes* can be checked against them. Exact
|
|
561
|
+
* values, never patterns, because `@descryy/core`'s redactor is
|
|
562
|
+
* literal-substring for the reason its own header gives: a "looks like a
|
|
563
|
+
* token" regex either destroys real data or misses a secret shaped unlike
|
|
564
|
+
* it. Per connection, in memory only, and compared against rather than
|
|
565
|
+
* printed at every call site that reads it.
|
|
566
|
+
*/
|
|
567
|
+
#browserSecrets = new Set();
|
|
568
|
+
#openGraph;
|
|
569
|
+
#calls = [];
|
|
570
|
+
#nextCallId = 1;
|
|
571
|
+
/**
|
|
572
|
+
* Where a `${credential:NAME}` step resolves its value, for a
|
|
573
|
+
* `browser_run_scenario` replay on this connection — RG-1(4) / lane
|
|
574
|
+
* "delivery". Mutable, not constructor-injected, and `undefined` by
|
|
575
|
+
* default: every existing `new Session(...)` call site (54 of them at
|
|
576
|
+
* last count) stays unchanged, and a caller that wants sign-in-by-profile
|
|
577
|
+
* sets it after construction, the same way `config` is mutated in place
|
|
578
|
+
* by `reloadConfig()`. `replayScenario` (`browser/scenario-runner.ts`)
|
|
579
|
+
* already accepts this exact type and already refuses a `${credential:...}`
|
|
580
|
+
* step by name when it is `undefined` — this field is what lets
|
|
581
|
+
* `browser-run-scenario.ts`'s tool actually have one to pass, which
|
|
582
|
+
* nothing did before.
|
|
583
|
+
*/
|
|
584
|
+
credentialStore = undefined;
|
|
585
|
+
/**
|
|
586
|
+
* Lane "map-freshness" item 3 — how long `server.ts`'s auto-refresh
|
|
587
|
+
* (`ensureFreshGraph`) will let a `behind` graph's refresh run before
|
|
588
|
+
* giving up and answering from the old graph instead, with the `behind`
|
|
589
|
+
* disclosure standing in place of a "refreshed" one. `undefined` (the
|
|
590
|
+
* default for every existing call site) means "use `AUTO_REFRESH_BUDGET_MS`"
|
|
591
|
+
* — mutable, same shape as `credentialStore` above. `0` skips the refresh
|
|
592
|
+
* outright, which is how a test reaches the answer-from-the-old-graph path
|
|
593
|
+
* deterministically: a near-zero timeout does not, because the budget only
|
|
594
|
+
* stops sources that check the abort signal (see `ensureFreshGraph`).
|
|
595
|
+
*/
|
|
596
|
+
autoRefreshBudgetMs = undefined;
|
|
597
|
+
constructor(repoPath, config, openGraph) {
|
|
598
|
+
this.repoPath = repoPath;
|
|
599
|
+
this.config = config;
|
|
600
|
+
this.#openGraph = openGraph ?? ((path) => openStore(path));
|
|
601
|
+
}
|
|
602
|
+
static async open(options) {
|
|
603
|
+
const repoPath = resolve(options.repoPath);
|
|
604
|
+
const config = options.config ?? (await loadConfig(repoPath));
|
|
605
|
+
return new Session(repoPath, config, options.openGraph);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Re-reads `.descry/config.json` from disk and replaces `config` in place —
|
|
609
|
+
* the explicit, caller-initiated reload UAT phase 5 (§0 gap 2) asked for.
|
|
610
|
+
*
|
|
611
|
+
* Deliberately not a file watcher: a watcher would re-run every trust check
|
|
612
|
+
* (`module-trust.ts`) on a specifier the instant a file the *scanned
|
|
613
|
+
* repository* controls changes, with no human in the loop — exactly the
|
|
614
|
+
* hazard those checks exist to gate. This method exists so a caller (a
|
|
615
|
+
* tool's own `reload` argument) can ask for the same effect on purpose,
|
|
616
|
+
* with an addressee for the trust decision that follows.
|
|
617
|
+
*
|
|
618
|
+
* Returns which top-level fields actually changed, using the identical
|
|
619
|
+
* comparison `checkConfigDrift`'s disclosure already makes, so "reload
|
|
620
|
+
* happened" and "reload changed something" stay distinguishable — a
|
|
621
|
+
* reload that finds nothing different is not a no-op that failed silently.
|
|
622
|
+
*/
|
|
623
|
+
async reloadConfig() {
|
|
624
|
+
const fresh = await loadConfig(this.repoPath);
|
|
625
|
+
const changed = driftedKeys(fresh, this.config);
|
|
626
|
+
this.config = fresh;
|
|
627
|
+
return { changed };
|
|
628
|
+
}
|
|
629
|
+
get graphPath() {
|
|
630
|
+
return isAbsolute(this.config.graph)
|
|
631
|
+
? this.config.graph
|
|
632
|
+
: join(this.repoPath, this.config.graph);
|
|
633
|
+
}
|
|
634
|
+
/** Open on first use; reuse thereafter. */
|
|
635
|
+
store() {
|
|
636
|
+
if (this.#store === null)
|
|
637
|
+
this.#store = this.#openGraph(this.graphPath);
|
|
638
|
+
return this.#store;
|
|
639
|
+
}
|
|
640
|
+
/** `analyze`'s own repair path — the only caller entitled to one (others refuse a
|
|
641
|
+
* version mismatch per DEC-017). Opens a short-lived driver directly, bypassing an
|
|
642
|
+
* injected test store; no-op there since an in-memory fixture is never stale. */
|
|
643
|
+
repairStaleGraph() {
|
|
644
|
+
if (this.#store !== null)
|
|
645
|
+
return { rebuilt: false };
|
|
646
|
+
const driver = new NodeSqliteDriver();
|
|
647
|
+
try {
|
|
648
|
+
driver.open(this.graphPath);
|
|
649
|
+
return rebuildStaleGraph(driver);
|
|
650
|
+
}
|
|
651
|
+
catch {
|
|
652
|
+
// Anything from opening (missing dir, corrupt file) is not this method's job —
|
|
653
|
+
// `store()` right after hits the same path via its own disclosed error handling.
|
|
654
|
+
return { rebuilt: false };
|
|
655
|
+
}
|
|
656
|
+
finally {
|
|
657
|
+
driver.close();
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
provider() {
|
|
661
|
+
if (this.#provider === null)
|
|
662
|
+
this.#provider = createStoreProvider(this.store().driver);
|
|
663
|
+
return this.#provider;
|
|
664
|
+
}
|
|
665
|
+
/** Repo root for an analysis run. Commit sha comes from git and is never defaulted
|
|
666
|
+
* when git is absent — every node/edge is stamped with it, and a placeholder would
|
|
667
|
+
* make the staleness stamp a lie nothing downstream could detect. */
|
|
668
|
+
async root() {
|
|
669
|
+
if (!(await isGitRepository(this.repoPath))) {
|
|
670
|
+
throw new ConfigError(`${this.repoPath} is not a git repository. Every node is stamped with the commit it was ` +
|
|
671
|
+
"read at, so there is nothing honest to record for a directory with no history.");
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
repo: this.config.repo,
|
|
675
|
+
...(this.config.workspace === undefined ? {} : { workspace: this.config.workspace }),
|
|
676
|
+
absolutePath: this.repoPath,
|
|
677
|
+
commitSha: await gitHead(this.repoPath),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/** Live browser sessions for this connection (`mcp-browser-tools.md` §4). Created
|
|
681
|
+
* lazily, same reason as the graph — most connections never touch a browser tool.
|
|
682
|
+
* Lives here, not a module-level map, so "per connection, never global" actually holds. */
|
|
683
|
+
browserSessions() {
|
|
684
|
+
if (this.#browserSessions === null)
|
|
685
|
+
this.#browserSessions = createBrowserSessionRegistry();
|
|
686
|
+
return this.#browserSessions;
|
|
687
|
+
}
|
|
688
|
+
/** The browser this connection drives, or a named reason there is none. Unavailable
|
|
689
|
+
* by default — `@descryy/runtime-browser` isn't installed yet — reported as a refusal
|
|
690
|
+
* with a remedy, never a silently-empty page (rule 7). */
|
|
691
|
+
browserBackend() {
|
|
692
|
+
if (this.#browserDriverFactory === null)
|
|
693
|
+
return BROWSER_BACKEND_NOT_INSTALLED;
|
|
694
|
+
return { available: true, createDriver: this.#browserDriverFactory };
|
|
695
|
+
}
|
|
696
|
+
/** Installs the driver this connection's browser tools use. A method, not a
|
|
697
|
+
* constructor arg — most sessions never touch a browser and shouldn't have to
|
|
698
|
+
* name one to be constructed. */
|
|
699
|
+
useBrowserDriver(driver) {
|
|
700
|
+
// One-driver form — every test that doesn't care about stack parsing wants this
|
|
701
|
+
// and shouldn't have to write a factory.
|
|
702
|
+
this.#browserDriverFactory = () => Promise.resolve(driver);
|
|
703
|
+
}
|
|
704
|
+
/** Installs a factory that builds a driver per session. A factory, not a driver,
|
|
705
|
+
* because the stack-trace parser (`browser_start_session`'s `stackParser`) is
|
|
706
|
+
* language-specific and named per call. */
|
|
707
|
+
useBrowserDriverFactory(factory) {
|
|
708
|
+
this.#browserDriverFactory = factory;
|
|
709
|
+
}
|
|
710
|
+
/** Record credential values a browser session was seeded with, so anything this server writes can refuse to carry them. */
|
|
711
|
+
noteBrowserSecrets(values) {
|
|
712
|
+
for (const value of values)
|
|
713
|
+
if (value.length > 0)
|
|
714
|
+
this.#browserSecrets.add(value);
|
|
715
|
+
}
|
|
716
|
+
/** The credential values this connection knows about. Compared against, never rendered. */
|
|
717
|
+
browserSecrets() {
|
|
718
|
+
return [...this.#browserSecrets];
|
|
719
|
+
}
|
|
720
|
+
/** Synchronous teardown, no I/O to wait on. A browser session can't close
|
|
721
|
+
* synchronously, so that reap lives in `dispose()` — this stays the method
|
|
722
|
+
* every existing no-browser caller keeps using unchanged. */
|
|
723
|
+
close() {
|
|
724
|
+
this.#browserSessions?.stop();
|
|
725
|
+
this.#store?.close();
|
|
726
|
+
this.#store = null;
|
|
727
|
+
this.#provider = null;
|
|
728
|
+
}
|
|
729
|
+
/** Full teardown, including any live browser — what a client disconnect must
|
|
730
|
+
* reach (§4). A leaked Chromium costs the developer's machine, not just a file
|
|
731
|
+
* descriptor. Awaits the reap first so the evidence flush has a live registry. */
|
|
732
|
+
async dispose() {
|
|
733
|
+
if (this.#browserSessions !== null)
|
|
734
|
+
await this.#browserSessions.reapAll("clientDisconnect");
|
|
735
|
+
this.close();
|
|
736
|
+
}
|
|
737
|
+
/** Mints the next call id without recording anything yet. `server.ts` calls this
|
|
738
|
+
* before render so the reply can carry `callId`, then records under the same id
|
|
739
|
+
* with `recordCallWithId` — one id used for both. */
|
|
740
|
+
allocateCallId() {
|
|
741
|
+
return `call-${this.#nextCallId++}`;
|
|
742
|
+
}
|
|
743
|
+
/** `verify_claim`'s fact boundary — called once per call with the exact
|
|
744
|
+
* `structuredContent` the host received. IDs are scoped to this process only, so a
|
|
745
|
+
* citation from a different run can't resolve (§2's G1 negative control). */
|
|
746
|
+
recordCall(tool, args, result) {
|
|
747
|
+
return this.recordCallWithId(this.allocateCallId(), tool, args, result);
|
|
748
|
+
}
|
|
749
|
+
/** `recordCall` under an id minted earlier by `allocateCallId`, not one minted here
|
|
750
|
+
* — so `recordedCall(id)` can find the exact id a caller read off the reply's
|
|
751
|
+
* `callId`, which didn't exist until after the envelope was built. */
|
|
752
|
+
recordCallWithId(id, tool, args, result) {
|
|
753
|
+
const call = { id, tool, args, result, at: Date.now() };
|
|
754
|
+
this.#calls.push(call);
|
|
755
|
+
return call;
|
|
756
|
+
}
|
|
757
|
+
/** All calls recorded this session, oldest first. */
|
|
758
|
+
recordedCalls() {
|
|
759
|
+
return this.#calls;
|
|
760
|
+
}
|
|
761
|
+
recordedCall(id) {
|
|
762
|
+
return this.#calls.find((c) => c.id === id);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* What this repository's last successful `observe_runtime` run saved, if any — the
|
|
767
|
+
* accessor a caller outside `observe-runtime.ts` should use rather than reaching into
|
|
768
|
+
* `session.config` directly, so the field it reads stays this file's own concern.
|
|
769
|
+
*
|
|
770
|
+
* Exported for the browser tools (lane "preflight-browser" / lane 5): a saved
|
|
771
|
+
* service's `readiness` can carry an `http` path and, if declared, a fixed `port` —
|
|
772
|
+
* enough to recover a URL to navigate to without requiring a fresh `observe_runtime`
|
|
773
|
+
* call first. Not wired into any browser tool from here — that composition belongs
|
|
774
|
+
* to the tools that own it.
|
|
775
|
+
*/
|
|
776
|
+
export function loadSavedStart(session) {
|
|
777
|
+
return session.config.savedStart;
|
|
778
|
+
}
|
|
779
|
+
//# sourceMappingURL=session.js.map
|