sneakoscope 7.3.0 → 7.6.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/README.md +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { CONTEXT_GRAPH_QUERY_SCHEMA, emptyContextGraphQueryResult } from '../query-types.js';
|
|
2
|
+
import { CONTEXT_GRAPH_TRAVERSAL_CAPS, contextGraphQueryProfile } from '../profiles.js';
|
|
3
|
+
import { isWorkspaceRelativePosixPath } from '../paths.js';
|
|
4
|
+
import { explainContextGraphCandidates, toContextGraphSelectedNode, contextGraphProvenanceCoverage } from './explain.js';
|
|
5
|
+
import { loadContextGraphIndex } from './load.js';
|
|
6
|
+
import { packContextGraphSelection } from './pack.js';
|
|
7
|
+
import { isHighRiskScope, rankContextGraphCandidates } from './rank.js';
|
|
8
|
+
import { CONTEXT_GRAPH_RANKING_CONFIG } from './ranking-config.js';
|
|
9
|
+
import { resolveContextGraphSeeds } from './seeds.js';
|
|
10
|
+
import { traverseContextGraph } from './traverse.js';
|
|
11
|
+
function add(into, reason, count) {
|
|
12
|
+
if (count <= 0)
|
|
13
|
+
return;
|
|
14
|
+
into[reason] = (into[reason] ?? 0) + count;
|
|
15
|
+
}
|
|
16
|
+
function usableFocusPaths(request) {
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const focus of request.focusPaths ?? []) {
|
|
19
|
+
const normalized = String(focus ?? '').replace(/^\.\//, '').replace(/\/+$/, '');
|
|
20
|
+
if (normalized && isWorkspaceRelativePosixPath(normalized) && !out.includes(normalized))
|
|
21
|
+
out.push(normalized);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
export function queryContextGraphSnapshot(index, request, options = {}) {
|
|
26
|
+
const clock = options.clock ?? Date.now;
|
|
27
|
+
const startedAt = clock();
|
|
28
|
+
const config = options.config ?? CONTEXT_GRAPH_RANKING_CONFIG;
|
|
29
|
+
const profile = contextGraphQueryProfile(request.profile);
|
|
30
|
+
const caps = CONTEXT_GRAPH_TRAVERSAL_CAPS;
|
|
31
|
+
const tokenBudget = Math.max(0, request.tokenBudget ?? caps.defaultTokenBudget);
|
|
32
|
+
const maxSelected = Math.max(0, Math.min(request.maxSelected ?? caps.maxSelectedNodes, caps.maxSelectedNodes));
|
|
33
|
+
const timeoutMs = Math.max(0, request.timeoutMs ?? caps.queryTimeoutMs);
|
|
34
|
+
const deadline = timeoutMs > 0 ? startedAt + timeoutMs : null;
|
|
35
|
+
const warnings = [...(options.warnings ?? [])];
|
|
36
|
+
const resolution = resolveContextGraphSeeds({ index, request, config, maxSeeds: caps.maxSeeds });
|
|
37
|
+
if (resolution.unknownProvidedSeeds > 0) {
|
|
38
|
+
warnings.push(`${resolution.unknownProvidedSeeds} caller-supplied seed(s) are not present in this snapshot`);
|
|
39
|
+
}
|
|
40
|
+
if (resolution.scanBudgetExhausted) {
|
|
41
|
+
warnings.push('lexical seeding stopped at its scan budget; results may be incomplete');
|
|
42
|
+
}
|
|
43
|
+
if (resolution.seeds.length === 0) {
|
|
44
|
+
warnings.push('no graph node matched this query; nothing was selected and no text fallback was attempted');
|
|
45
|
+
}
|
|
46
|
+
const focusPaths = usableFocusPaths(request);
|
|
47
|
+
const highRisk = isHighRiskScope(index, resolution.seeds, request);
|
|
48
|
+
const maxDepth = highRisk ? profile.maxDepthHighRisk : profile.maxDepth;
|
|
49
|
+
const traversal = traverseContextGraph({
|
|
50
|
+
index,
|
|
51
|
+
seeds: resolution.seeds,
|
|
52
|
+
profile,
|
|
53
|
+
maxDepth,
|
|
54
|
+
maxVisitedNodes: caps.maxVisitedNodes,
|
|
55
|
+
maxVisitedEdges: caps.maxVisitedEdges,
|
|
56
|
+
config,
|
|
57
|
+
focusPaths,
|
|
58
|
+
deadline
|
|
59
|
+
});
|
|
60
|
+
const seedsByNode = new Map(resolution.seeds.map((seed) => [seed.nodeId, seed]));
|
|
61
|
+
const ranked = rankContextGraphCandidates({
|
|
62
|
+
index,
|
|
63
|
+
states: traversal.states,
|
|
64
|
+
seedsByNode,
|
|
65
|
+
request,
|
|
66
|
+
config,
|
|
67
|
+
focusActive: focusPaths.length > 0
|
|
68
|
+
});
|
|
69
|
+
if (ranked.focusExcluded > 0) {
|
|
70
|
+
warnings.push(`${ranked.focusExcluded} reachable node(s) were outside the requested focus paths`);
|
|
71
|
+
}
|
|
72
|
+
const explainResult = explainContextGraphCandidates({
|
|
73
|
+
index,
|
|
74
|
+
states: traversal.states,
|
|
75
|
+
candidates: ranked.candidates,
|
|
76
|
+
config
|
|
77
|
+
});
|
|
78
|
+
const packed = packContextGraphSelection({
|
|
79
|
+
explained: explainResult.explained,
|
|
80
|
+
tokenBudget,
|
|
81
|
+
maxSelected,
|
|
82
|
+
profile: profile.name,
|
|
83
|
+
highRisk,
|
|
84
|
+
config
|
|
85
|
+
});
|
|
86
|
+
warnings.push(...packed.warnings);
|
|
87
|
+
const omissionReasons = { ...packed.omissions };
|
|
88
|
+
add(omissionReasons, 'stale_node', ranked.staleExcluded);
|
|
89
|
+
add(omissionReasons, 'invalidated_proof', ranked.invalidatedExcluded);
|
|
90
|
+
add(omissionReasons, 'redundant_sibling', ranked.redundantExcluded);
|
|
91
|
+
add(omissionReasons, 'no_provenance', explainResult.ungrounded);
|
|
92
|
+
add(omissionReasons, 'depth_limit', traversal.depthLimited);
|
|
93
|
+
add(omissionReasons, 'visit_cap', (traversal.nodeCapHit ? 1 : 0) + resolution.droppedSeeds);
|
|
94
|
+
add(omissionReasons, 'edge_cap', traversal.edgeCapHit ? 1 : 0);
|
|
95
|
+
add(omissionReasons, 'timeout', traversal.timedOut ? 1 : 0);
|
|
96
|
+
const selected = packed.selected.map(toContextGraphSelectedNode);
|
|
97
|
+
const truncated = packed.truncated || traversal.nodeCapHit || traversal.edgeCapHit || traversal.timedOut || resolution.droppedSeeds > 0;
|
|
98
|
+
return {
|
|
99
|
+
schema: CONTEXT_GRAPH_QUERY_SCHEMA,
|
|
100
|
+
ok: true,
|
|
101
|
+
snapshotHash: index.snapshot.snapshotHash,
|
|
102
|
+
snapshotFreshness: options.snapshotFreshness ?? 'fresh',
|
|
103
|
+
profile: profile.name,
|
|
104
|
+
seeds: resolution.seeds,
|
|
105
|
+
seedCount: resolution.seeds.length,
|
|
106
|
+
visitedNodes: traversal.visitedNodes,
|
|
107
|
+
visitedEdges: traversal.visitedEdges,
|
|
108
|
+
selected,
|
|
109
|
+
selectedNodes: selected.length,
|
|
110
|
+
explanationPathCount: selected.filter((node) => node.explanation.length > 0).length,
|
|
111
|
+
provenanceCoverage: contextGraphProvenanceCoverage(selected),
|
|
112
|
+
staleExcluded: ranked.staleExcluded,
|
|
113
|
+
invalidatedExcluded: ranked.invalidatedExcluded,
|
|
114
|
+
tokenCost: packed.tokenCost,
|
|
115
|
+
tokenBudget,
|
|
116
|
+
truncated,
|
|
117
|
+
timeout: traversal.timedOut,
|
|
118
|
+
omissionReasons,
|
|
119
|
+
warnings,
|
|
120
|
+
errors: [],
|
|
121
|
+
durationMs: Math.max(0, clock() - startedAt),
|
|
122
|
+
processSpawns: 0
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function failedResult(profile, snapshotHash, errors, warnings, tokenBudget, durationMs) {
|
|
126
|
+
const base = emptyContextGraphQueryResult(snapshotHash, profile, [...errors]);
|
|
127
|
+
return { ...base, warnings: [...warnings], tokenBudget, durationMs };
|
|
128
|
+
}
|
|
129
|
+
export async function queryContextGraph(request, options = {}) {
|
|
130
|
+
const clock = options.clock ?? Date.now;
|
|
131
|
+
const startedAt = clock();
|
|
132
|
+
const profileName = contextGraphQueryProfile(request.profile).name;
|
|
133
|
+
const tokenBudget = Math.max(0, request.tokenBudget ?? CONTEXT_GRAPH_TRAVERSAL_CAPS.defaultTokenBudget);
|
|
134
|
+
if (options.index) {
|
|
135
|
+
return queryContextGraphSnapshot(options.index, request, options);
|
|
136
|
+
}
|
|
137
|
+
const load = await loadContextGraphIndex(request.root, options);
|
|
138
|
+
if (!load.ok || !load.index) {
|
|
139
|
+
return failedResult(profileName, load.snapshotHash, load.errors, load.warnings, tokenBudget, Math.max(0, clock() - startedAt));
|
|
140
|
+
}
|
|
141
|
+
const snapshotOptions = {
|
|
142
|
+
...(options.config === undefined ? {} : { config: options.config }),
|
|
143
|
+
...(options.clock === undefined ? {} : { clock: options.clock }),
|
|
144
|
+
snapshotFreshness: load.freshness,
|
|
145
|
+
warnings: [...load.warnings, ...(options.warnings ?? [])]
|
|
146
|
+
};
|
|
147
|
+
return queryContextGraphSnapshot(load.index, request, snapshotOptions);
|
|
148
|
+
}
|
|
149
|
+
export function contextGraphSearchMeta(result) {
|
|
150
|
+
const omissionReasons = {};
|
|
151
|
+
for (const [reason, count] of Object.entries(result.omissionReasons)) {
|
|
152
|
+
if (typeof count === 'number' && count > 0)
|
|
153
|
+
omissionReasons[reason] = count;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
snapshotHash: result.snapshotHash,
|
|
157
|
+
snapshotFreshness: result.snapshotFreshness,
|
|
158
|
+
profile: result.profile,
|
|
159
|
+
seedCount: result.seedCount,
|
|
160
|
+
visitedNodes: result.visitedNodes,
|
|
161
|
+
selectedNodes: result.selectedNodes,
|
|
162
|
+
explanationPathCount: result.explanationPathCount,
|
|
163
|
+
provenanceCoverage: result.provenanceCoverage,
|
|
164
|
+
staleExcluded: result.staleExcluded,
|
|
165
|
+
invalidatedExcluded: result.invalidatedExcluded,
|
|
166
|
+
tokenCost: result.tokenCost,
|
|
167
|
+
tokenBudget: result.tokenBudget,
|
|
168
|
+
omissionReasons
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export { CONTEXT_GRAPH_RANKING_CONFIG, CONTEXT_GRAPH_RANKING_SCHEMA, CONTEXT_GRAPH_EXACT_SEED_CONFIDENCES, isExactContextGraphSeedConfidence } from './ranking-config.js';
|
|
172
|
+
export { normalizeContextGraphQuery, resolveContextGraphSeeds, seedConfidenceFor } from './seeds.js';
|
|
173
|
+
export { traverseContextGraph, contextGraphNodePath, isUnderFocusPath } from './traverse.js';
|
|
174
|
+
export { rankContextGraphCandidates, contextGraphGroupKey, isInvalidatedContextGraphNode, isHighRiskScope } from './rank.js';
|
|
175
|
+
export { explainContextGraphCandidates, groundContextGraphNode, toContextGraphSelectedNode, contextGraphProvenanceCoverage } from './explain.js';
|
|
176
|
+
export { packContextGraphSelection } from './pack.js';
|
|
177
|
+
export { loadContextGraphIndex, contextGraphRepairHint } from './load.js';
|
|
178
|
+
export { CONTEXT_GRAPH_SNAPSHOT_CACHE_MAX_ENTRIES, cacheContextGraphIndex, clearContextGraphSnapshotCache, contextGraphSnapshotCacheStats, contextGraphWorkspaceKey, getCachedContextGraphIndex } from './snapshot-cache.js';
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { CONTEXT_GRAPH_CORRUPT_ERROR, CONTEXT_GRAPH_MISSING_ERROR, CONTEXT_GRAPH_REPAIR_COMMAND, CONTEXT_GRAPH_SCHEMA_REVISION, CONTEXT_GRAPH_STALE_ERROR } from '../contracts.js';
|
|
2
|
+
import { readSourceHashes } from '../compiler/freshness.js';
|
|
3
|
+
import { buildContextGraphIndex } from '../graph-index.js';
|
|
4
|
+
import { readContextGraphMeta, readContextGraphSnapshot } from '../store/snapshot-store.js';
|
|
5
|
+
import { cacheContextGraphIndex, getCachedContextGraphIndex } from './snapshot-cache.js';
|
|
6
|
+
const DEFAULT_MAX_VERIFIED_SOURCES = 4000;
|
|
7
|
+
export function contextGraphRepairHint() {
|
|
8
|
+
return `Run \`${CONTEXT_GRAPH_REPAIR_COMMAND}\` to rebuild the context graph.`;
|
|
9
|
+
}
|
|
10
|
+
function failure(code, detail, snapshotHash = '') {
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
index: null,
|
|
14
|
+
snapshotHash,
|
|
15
|
+
errorCode: code,
|
|
16
|
+
errors: [code, detail, contextGraphRepairHint()],
|
|
17
|
+
warnings: [],
|
|
18
|
+
cacheHit: false,
|
|
19
|
+
freshness: 'stale'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function statusFailure(status) {
|
|
23
|
+
if (status.status === 'fresh')
|
|
24
|
+
return null;
|
|
25
|
+
const code = status.status === 'missing'
|
|
26
|
+
? CONTEXT_GRAPH_MISSING_ERROR
|
|
27
|
+
: status.status === 'corrupt'
|
|
28
|
+
? CONTEXT_GRAPH_CORRUPT_ERROR
|
|
29
|
+
: CONTEXT_GRAPH_STALE_ERROR;
|
|
30
|
+
const declared = status.reasons ?? [];
|
|
31
|
+
const reasons = declared.length > 0 ? ` (${declared.join(', ')})` : '';
|
|
32
|
+
return failure(code, `the stored context graph is ${status.status}${reasons}`, status.snapshotHash ?? '');
|
|
33
|
+
}
|
|
34
|
+
async function sourcesChanged(root, inputHashes, limit) {
|
|
35
|
+
const recorded = Object.keys(inputHashes).sort().slice(0, Math.max(0, limit));
|
|
36
|
+
if (recorded.length === 0)
|
|
37
|
+
return false;
|
|
38
|
+
const observed = await readSourceHashes(root, recorded);
|
|
39
|
+
for (const file of recorded) {
|
|
40
|
+
if (observed[file] !== inputHashes[file])
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
export async function loadContextGraphIndex(root, options = {}) {
|
|
46
|
+
const supplied = options.status;
|
|
47
|
+
if (supplied) {
|
|
48
|
+
const blocked = statusFailure(supplied);
|
|
49
|
+
if (blocked && !(supplied.status === 'stale' && options.allowStale === true))
|
|
50
|
+
return blocked;
|
|
51
|
+
}
|
|
52
|
+
const metaLoad = await readContextGraphMeta(root);
|
|
53
|
+
if (metaLoad.status === 'missing')
|
|
54
|
+
return failure(CONTEXT_GRAPH_MISSING_ERROR, 'the context graph has not been built');
|
|
55
|
+
if (metaLoad.status !== 'ok' || !metaLoad.meta) {
|
|
56
|
+
return failure(CONTEXT_GRAPH_CORRUPT_ERROR, 'the context graph metadata is unreadable');
|
|
57
|
+
}
|
|
58
|
+
const meta = metaLoad.meta;
|
|
59
|
+
const warnings = [];
|
|
60
|
+
if (options.verifySources === true) {
|
|
61
|
+
const changed = await sourcesChanged(root, meta.inputHashes ?? {}, options.maxVerifiedSources ?? DEFAULT_MAX_VERIFIED_SOURCES);
|
|
62
|
+
if (changed && options.allowStale !== true) {
|
|
63
|
+
return failure(CONTEXT_GRAPH_STALE_ERROR, 'a recorded source no longer hashes to its stored value', meta.snapshotHash);
|
|
64
|
+
}
|
|
65
|
+
if (changed)
|
|
66
|
+
warnings.push('answering over a stale context graph on request');
|
|
67
|
+
}
|
|
68
|
+
else if (!supplied) {
|
|
69
|
+
warnings.push(`context graph freshness was not verified by this query; ${contextGraphRepairHint()}`);
|
|
70
|
+
}
|
|
71
|
+
const useCache = options.cache !== false;
|
|
72
|
+
if (useCache) {
|
|
73
|
+
const cached = getCachedContextGraphIndex(root, meta.snapshotHash);
|
|
74
|
+
if (cached) {
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
index: cached,
|
|
78
|
+
snapshotHash: meta.snapshotHash,
|
|
79
|
+
errorCode: null,
|
|
80
|
+
errors: [],
|
|
81
|
+
warnings,
|
|
82
|
+
cacheHit: true,
|
|
83
|
+
freshness: supplied?.status === 'stale' ? 'stale' : 'fresh'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const snapshotLoad = await readContextGraphSnapshot(root);
|
|
88
|
+
if (snapshotLoad.status === 'missing') {
|
|
89
|
+
return failure(CONTEXT_GRAPH_MISSING_ERROR, 'the context graph snapshot is absent while its metadata is present');
|
|
90
|
+
}
|
|
91
|
+
if (snapshotLoad.status !== 'ok' || !snapshotLoad.snapshot) {
|
|
92
|
+
return failure(CONTEXT_GRAPH_CORRUPT_ERROR, 'the context graph snapshot failed structural validation');
|
|
93
|
+
}
|
|
94
|
+
const snapshot = snapshotLoad.snapshot;
|
|
95
|
+
if (snapshot.snapshotHash !== meta.snapshotHash
|
|
96
|
+
|| snapshot.nodeCount !== meta.nodeCount
|
|
97
|
+
|| snapshot.edgeCount !== meta.edgeCount) {
|
|
98
|
+
return failure(CONTEXT_GRAPH_CORRUPT_ERROR, 'the snapshot and its metadata describe different graphs', snapshot.snapshotHash);
|
|
99
|
+
}
|
|
100
|
+
if (snapshot.schemaRevision !== CONTEXT_GRAPH_SCHEMA_REVISION) {
|
|
101
|
+
return failure(CONTEXT_GRAPH_STALE_ERROR, 'the snapshot was written by a different graph schema revision', snapshot.snapshotHash);
|
|
102
|
+
}
|
|
103
|
+
const index = buildContextGraphIndex(snapshot);
|
|
104
|
+
if (useCache)
|
|
105
|
+
cacheContextGraphIndex(root, snapshot.snapshotHash, index);
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
index,
|
|
109
|
+
snapshotHash: snapshot.snapshotHash,
|
|
110
|
+
errorCode: null,
|
|
111
|
+
errors: [],
|
|
112
|
+
warnings,
|
|
113
|
+
cacheHit: false,
|
|
114
|
+
freshness: supplied?.status === 'stale' ? 'stale' : 'fresh'
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { compareContextGraphIds } from '../ids.js';
|
|
2
|
+
function bump(into, reason) {
|
|
3
|
+
into[reason] = (into[reason] ?? 0) + 1;
|
|
4
|
+
}
|
|
5
|
+
function isProtectedGate(entry) {
|
|
6
|
+
return entry.candidate.node.risk === 'protected';
|
|
7
|
+
}
|
|
8
|
+
function isImplementationEvidence(entry) {
|
|
9
|
+
const kind = entry.candidate.node.kind;
|
|
10
|
+
return kind === 'test' || kind === 'gate';
|
|
11
|
+
}
|
|
12
|
+
function byScoreThenId(left, right) {
|
|
13
|
+
if (left.candidate.score !== right.candidate.score)
|
|
14
|
+
return right.candidate.score - left.candidate.score;
|
|
15
|
+
return compareContextGraphIds(left.candidate.node.id, right.candidate.node.id);
|
|
16
|
+
}
|
|
17
|
+
class Packer {
|
|
18
|
+
budget;
|
|
19
|
+
limit;
|
|
20
|
+
chosen = new Map();
|
|
21
|
+
perGroup = new Map();
|
|
22
|
+
omissions = {};
|
|
23
|
+
spent = 0;
|
|
24
|
+
constructor(budget, limit) {
|
|
25
|
+
this.budget = budget;
|
|
26
|
+
this.limit = limit;
|
|
27
|
+
}
|
|
28
|
+
has(entry) {
|
|
29
|
+
return this.chosen.has(entry.candidate.node.id);
|
|
30
|
+
}
|
|
31
|
+
take(entry) {
|
|
32
|
+
if (this.chosen.size >= this.limit) {
|
|
33
|
+
bump(this.omissions, 'max_selected');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const cost = entry.candidate.tokenCost;
|
|
37
|
+
if (this.spent + cost > this.budget) {
|
|
38
|
+
bump(this.omissions, 'token_budget');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
this.chosen.set(entry.candidate.node.id, entry);
|
|
42
|
+
this.spent += cost;
|
|
43
|
+
const group = entry.candidate.groupKey;
|
|
44
|
+
this.perGroup.set(group, (this.perGroup.get(group) ?? 0) + 1);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
groupCount(entry) {
|
|
48
|
+
return this.perGroup.get(entry.candidate.groupKey) ?? 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function packContextGraphSelection(input) {
|
|
52
|
+
const { explained, tokenBudget, maxSelected, profile, highRisk, config } = input;
|
|
53
|
+
const ordered = [...explained].sort(byScoreThenId);
|
|
54
|
+
const packer = new Packer(Math.max(0, tokenBudget), Math.max(0, maxSelected));
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const exactSeeds = ordered.filter((entry) => entry.candidate.exactSeed);
|
|
57
|
+
const protectedGates = ordered.filter(isProtectedGate);
|
|
58
|
+
const implementationEvidence = ordered.filter(isImplementationEvidence);
|
|
59
|
+
const reserved = [];
|
|
60
|
+
if (highRisk) {
|
|
61
|
+
for (const gate of protectedGates.slice(0, config.protectedGateReserveSlots))
|
|
62
|
+
reserved.push(gate);
|
|
63
|
+
}
|
|
64
|
+
const firstExactSeed = exactSeeds[0];
|
|
65
|
+
if (firstExactSeed)
|
|
66
|
+
reserved.push(firstExactSeed);
|
|
67
|
+
if (profile === 'implementation') {
|
|
68
|
+
for (const evidence of implementationEvidence.slice(0, config.testOrGateReserveSlots))
|
|
69
|
+
reserved.push(evidence);
|
|
70
|
+
}
|
|
71
|
+
for (const entry of reserved) {
|
|
72
|
+
if (packer.has(entry))
|
|
73
|
+
continue;
|
|
74
|
+
packer.take(entry);
|
|
75
|
+
}
|
|
76
|
+
const slotAllowance = Math.max(config.minGroupSlots, Math.ceil(Math.max(0, maxSelected) * config.moduleShareCap));
|
|
77
|
+
const distinctGroups = new Set(ordered.map((entry) => entry.candidate.groupKey)).size;
|
|
78
|
+
const diversityApplies = distinctGroups > 1;
|
|
79
|
+
for (const entry of ordered) {
|
|
80
|
+
if (packer.has(entry))
|
|
81
|
+
continue;
|
|
82
|
+
if (diversityApplies && packer.groupCount(entry) >= slotAllowance) {
|
|
83
|
+
bump(packer.omissions, 'redundant_sibling');
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
packer.take(entry);
|
|
87
|
+
}
|
|
88
|
+
const selected = [...packer.chosen.values()].sort(byScoreThenId);
|
|
89
|
+
const selectedIds = new Set(selected.map((entry) => entry.candidate.node.id));
|
|
90
|
+
const protectedSelected = protectedGates.filter((entry) => selectedIds.has(entry.candidate.node.id)).length;
|
|
91
|
+
const guarantees = {
|
|
92
|
+
exactSeedSelected: selected.some((entry) => entry.candidate.exactSeed),
|
|
93
|
+
exactSeedAvailable: exactSeeds.length > 0,
|
|
94
|
+
protectedGatesReachable: protectedGates.length,
|
|
95
|
+
protectedGatesSelected: protectedSelected,
|
|
96
|
+
implementationEvidenceReachable: implementationEvidence.length > 0,
|
|
97
|
+
implementationEvidenceSelected: implementationEvidence.some((entry) => selectedIds.has(entry.candidate.node.id))
|
|
98
|
+
};
|
|
99
|
+
if (guarantees.exactSeedAvailable && !guarantees.exactSeedSelected) {
|
|
100
|
+
warnings.push('exact seed did not fit the token budget; widen tokenBudget or narrow the query');
|
|
101
|
+
}
|
|
102
|
+
if (highRisk && guarantees.protectedGatesReachable > guarantees.protectedGatesSelected) {
|
|
103
|
+
warnings.push('a reachable protected gate did not fit the token budget on a high-risk query');
|
|
104
|
+
}
|
|
105
|
+
if (profile === 'implementation' && guarantees.implementationEvidenceReachable && !guarantees.implementationEvidenceSelected) {
|
|
106
|
+
warnings.push('no reachable test or gate fit the token budget on an implementation query');
|
|
107
|
+
}
|
|
108
|
+
const truncated = (packer.omissions.token_budget ?? 0) > 0 || (packer.omissions.max_selected ?? 0) > 0;
|
|
109
|
+
return {
|
|
110
|
+
selected,
|
|
111
|
+
tokenCost: packer.spent,
|
|
112
|
+
omissions: packer.omissions,
|
|
113
|
+
truncated,
|
|
114
|
+
warnings,
|
|
115
|
+
guarantees
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { compareContextGraphIds, contextGraphPathFromId } from '../ids.js';
|
|
2
|
+
import { isExactContextGraphSeedConfidence } from './ranking-config.js';
|
|
3
|
+
export function contextGraphGroupKey(node) {
|
|
4
|
+
const nodePath = node.path ?? contextGraphPathFromId(node.id);
|
|
5
|
+
if (nodePath) {
|
|
6
|
+
const cut = nodePath.lastIndexOf('/');
|
|
7
|
+
return cut > 0 ? nodePath.slice(0, cut) : '.';
|
|
8
|
+
}
|
|
9
|
+
const module = node.metadata.module;
|
|
10
|
+
if (typeof module === 'string' && module)
|
|
11
|
+
return `module/${module}`;
|
|
12
|
+
return `kind/${node.kind}`;
|
|
13
|
+
}
|
|
14
|
+
export function isInvalidatedContextGraphNode(index, node) {
|
|
15
|
+
const metadata = node.metadata;
|
|
16
|
+
if (node.kind === 'proof') {
|
|
17
|
+
if (metadata.reusable === false)
|
|
18
|
+
return true;
|
|
19
|
+
if (metadata.corrupt === true)
|
|
20
|
+
return true;
|
|
21
|
+
if (metadata.expired === true)
|
|
22
|
+
return true;
|
|
23
|
+
const reasons = metadata.invalidation_reason_count;
|
|
24
|
+
if (typeof reasons === 'number' && reasons > 0)
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
for (const edgeId of index.outgoing.get(node.id) ?? []) {
|
|
28
|
+
if (index.edgesById.get(edgeId)?.type === 'invalidates')
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function evidenceSourceCount(index, node, state) {
|
|
34
|
+
let count = 0;
|
|
35
|
+
if (node.path && node.contentHash)
|
|
36
|
+
count += 1;
|
|
37
|
+
if (state.parentEdgeId)
|
|
38
|
+
count += 1;
|
|
39
|
+
if ((index.outgoing.get(node.id)?.length ?? 0) > 0 || (index.incoming.get(node.id)?.length ?? 0) > 0)
|
|
40
|
+
count += 1;
|
|
41
|
+
return count;
|
|
42
|
+
}
|
|
43
|
+
export function isHighRiskScope(index, seeds, request) {
|
|
44
|
+
if (request.risk === 'high')
|
|
45
|
+
return true;
|
|
46
|
+
for (const seed of seeds) {
|
|
47
|
+
if (index.nodesById.get(seed.nodeId)?.risk === 'protected')
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function baseScore(node, state, seed, input, invalidated, tokenCost, evidence) {
|
|
53
|
+
const config = input.config;
|
|
54
|
+
const exact = seed !== null && isExactContextGraphSeedConfidence(seed.confidence);
|
|
55
|
+
const seedBonus = seed === null ? 0 : exact ? config.exactSeedBonus : config.lexicalSeedBonus;
|
|
56
|
+
const riskScale = input.request.risk === 'high' ? config.highRiskRelevanceMultiplier : 1;
|
|
57
|
+
const evidenceBonus = Math.min(config.evidenceCoverageCap, config.evidenceCoverageBonus * evidence);
|
|
58
|
+
const stalePenalty = node.freshness === 'stale' ? config.stalePenalty : 0;
|
|
59
|
+
const invalidatedPenalty = invalidated ? config.invalidatedPenalty : 0;
|
|
60
|
+
const tokenPenalty = Math.min(config.tokenCostPenaltyCap, config.tokenCostPenaltyPerToken * tokenCost);
|
|
61
|
+
return (seedBonus
|
|
62
|
+
+ state.weight
|
|
63
|
+
+ config.trustBonus * node.trust
|
|
64
|
+
+ config.freshnessBonus[node.freshness]
|
|
65
|
+
+ config.riskRelevanceBonus[node.risk] * riskScale
|
|
66
|
+
+ evidenceBonus
|
|
67
|
+
- stalePenalty
|
|
68
|
+
- invalidatedPenalty
|
|
69
|
+
- tokenPenalty);
|
|
70
|
+
}
|
|
71
|
+
function byScoreThenId(left, right) {
|
|
72
|
+
if (left.score !== right.score)
|
|
73
|
+
return right.score - left.score;
|
|
74
|
+
return compareContextGraphIds(left.node.id, right.node.id);
|
|
75
|
+
}
|
|
76
|
+
export function rankContextGraphCandidates(input) {
|
|
77
|
+
const { index, states, seedsByNode, config, focusActive } = input;
|
|
78
|
+
const candidates = [];
|
|
79
|
+
const excludedNodeIds = [];
|
|
80
|
+
let staleExcluded = 0;
|
|
81
|
+
let invalidatedExcluded = 0;
|
|
82
|
+
let focusExcluded = 0;
|
|
83
|
+
for (const state of states.values()) {
|
|
84
|
+
const node = index.nodesById.get(state.nodeId);
|
|
85
|
+
if (!node)
|
|
86
|
+
continue;
|
|
87
|
+
if (focusActive && !state.focusMatched) {
|
|
88
|
+
focusExcluded += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const invalidated = isInvalidatedContextGraphNode(index, node);
|
|
92
|
+
if (node.freshness === 'stale') {
|
|
93
|
+
staleExcluded += 1;
|
|
94
|
+
excludedNodeIds.push(node.id);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (invalidated) {
|
|
98
|
+
invalidatedExcluded += 1;
|
|
99
|
+
excludedNodeIds.push(node.id);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const seed = seedsByNode.get(state.nodeId) ?? null;
|
|
103
|
+
const tokenCost = Math.max(config.minTokenCost, node.tokenCost);
|
|
104
|
+
const evidence = evidenceSourceCount(index, node, state);
|
|
105
|
+
candidates.push({
|
|
106
|
+
node,
|
|
107
|
+
state,
|
|
108
|
+
seed,
|
|
109
|
+
exactSeed: seed !== null && isExactContextGraphSeedConfidence(seed.confidence),
|
|
110
|
+
groupKey: contextGraphGroupKey(node),
|
|
111
|
+
tokenCost,
|
|
112
|
+
evidenceSources: evidence,
|
|
113
|
+
score: baseScore(node, state, seed, input, invalidated, tokenCost, evidence),
|
|
114
|
+
redundancyPenalty: 0
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
candidates.sort(byScoreThenId);
|
|
118
|
+
const perGroup = new Map();
|
|
119
|
+
const kept = [];
|
|
120
|
+
let redundantExcluded = 0;
|
|
121
|
+
for (const candidate of candidates) {
|
|
122
|
+
const prior = perGroup.get(candidate.groupKey) ?? 0;
|
|
123
|
+
if (prior >= config.maxRedundancyGroupMembers && !candidate.exactSeed) {
|
|
124
|
+
redundantExcluded += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
perGroup.set(candidate.groupKey, prior + 1);
|
|
128
|
+
const penalty = Math.min(config.redundancyPenaltyCap, config.redundancyPenalty * prior);
|
|
129
|
+
candidate.redundancyPenalty = penalty;
|
|
130
|
+
candidate.score -= penalty;
|
|
131
|
+
kept.push(candidate);
|
|
132
|
+
}
|
|
133
|
+
kept.sort(byScoreThenId);
|
|
134
|
+
return {
|
|
135
|
+
candidates: kept,
|
|
136
|
+
staleExcluded,
|
|
137
|
+
invalidatedExcluded,
|
|
138
|
+
redundantExcluded,
|
|
139
|
+
focusExcluded,
|
|
140
|
+
excludedNodeIds: excludedNodeIds.sort(compareContextGraphIds)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export const CONTEXT_GRAPH_RANKING_SCHEMA = 'sks.context-graph-ranking.v1';
|
|
2
|
+
export const CONTEXT_GRAPH_EXACT_SEED_CONFIDENCES = [
|
|
3
|
+
'exact_definition',
|
|
4
|
+
'exact_reference',
|
|
5
|
+
'manifest',
|
|
6
|
+
'file_path'
|
|
7
|
+
];
|
|
8
|
+
export const CONTEXT_GRAPH_RANKING_CONFIG = {
|
|
9
|
+
schema: CONTEXT_GRAPH_RANKING_SCHEMA,
|
|
10
|
+
seedConfidenceScore: {
|
|
11
|
+
exact_definition: 6,
|
|
12
|
+
exact_reference: 5,
|
|
13
|
+
manifest: 5,
|
|
14
|
+
syntactic_reference: 3.5,
|
|
15
|
+
file_path: 4,
|
|
16
|
+
text_candidate: 1.5
|
|
17
|
+
},
|
|
18
|
+
exactSeedBonus: 4,
|
|
19
|
+
lexicalSeedBonus: 0.5,
|
|
20
|
+
providedSeedScoreCeiling: 8,
|
|
21
|
+
edgeConfidenceMultiplier: {
|
|
22
|
+
exact: 1,
|
|
23
|
+
manifest: 0.95,
|
|
24
|
+
syntactic: 0.85,
|
|
25
|
+
observed: 0.7,
|
|
26
|
+
derived: 0.5
|
|
27
|
+
},
|
|
28
|
+
depthDecay: 0.6,
|
|
29
|
+
reverseEdgeMultiplier: 0.85,
|
|
30
|
+
trustBonus: 1.5,
|
|
31
|
+
freshnessBonus: { fresh: 0.75, unknown: 0, stale: -0.5 },
|
|
32
|
+
riskRelevanceBonus: { low: 0, medium: 0.25, high: 0.75, protected: 1.5 },
|
|
33
|
+
highRiskRelevanceMultiplier: 2,
|
|
34
|
+
evidenceCoverageBonus: 0.35,
|
|
35
|
+
evidenceCoverageCap: 1.05,
|
|
36
|
+
stalePenalty: 2.5,
|
|
37
|
+
invalidatedPenalty: 4,
|
|
38
|
+
redundancyPenalty: 0.6,
|
|
39
|
+
redundancyPenaltyCap: 3,
|
|
40
|
+
tokenCostPenaltyPerToken: 0.0015,
|
|
41
|
+
tokenCostPenaltyCap: 1.5,
|
|
42
|
+
minExactSeeds: 3,
|
|
43
|
+
maxLexicalSeeds: 8,
|
|
44
|
+
lexicalScanBudget: 50000,
|
|
45
|
+
lexicalMinTokenLength: 4,
|
|
46
|
+
lexicalMatchScore: 1.5,
|
|
47
|
+
maxQueryTokens: 24,
|
|
48
|
+
maxSeedsPerToken: 6,
|
|
49
|
+
timeoutCheckInterval: 512,
|
|
50
|
+
moduleShareCap: 0.4,
|
|
51
|
+
minGroupSlots: 2,
|
|
52
|
+
maxRedundancyGroupMembers: 12,
|
|
53
|
+
protectedGateReserveSlots: 8,
|
|
54
|
+
testOrGateReserveSlots: 1,
|
|
55
|
+
maxProvenancePerNode: 6,
|
|
56
|
+
minTokenCost: 1
|
|
57
|
+
};
|
|
58
|
+
const EXACT_SEED_CONFIDENCE_SET = new Set(CONTEXT_GRAPH_EXACT_SEED_CONFIDENCES);
|
|
59
|
+
export function isExactContextGraphSeedConfidence(value) {
|
|
60
|
+
return EXACT_SEED_CONFIDENCE_SET.has(value);
|
|
61
|
+
}
|
|
62
|
+
export function contextGraphSeedConfidenceScore(config, confidence) {
|
|
63
|
+
return config.seedConfidenceScore[confidence];
|
|
64
|
+
}
|