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.
Files changed (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -0,0 +1,91 @@
1
+ import { CONTEXT_GRAPH_RANKING_CONFIG, groundContextGraphNode } from '../query/index.js';
2
+ export const ATTENTION_FACT_TRUST_FLOOR = 0.6;
3
+ const MAX_HINT_LENGTH = 240;
4
+ const MAX_HINT_CITATIONS = 4;
5
+ export function isFactGradeAnchor(trust, freshness) {
6
+ return freshness === 'fresh' && trust >= ATTENTION_FACT_TRUST_FLOOR;
7
+ }
8
+ export function attentionHydrateHint(provenance, trust, freshness, risk, factGrade) {
9
+ const reasons = [];
10
+ if (!factGrade)
11
+ reasons.push('trust_action:hydrate_first');
12
+ if (freshness !== 'fresh')
13
+ reasons.push(`freshness:${freshness}`);
14
+ if (risk === 'protected' || risk === 'high')
15
+ reasons.push(`risk:${risk}`);
16
+ if (trust < ATTENTION_FACT_TRUST_FLOOR)
17
+ reasons.push(`trust:${trust.toFixed(2)}`);
18
+ const paths = [...new Set(provenance.map((ref) => ref.path).filter(Boolean))].slice(0, MAX_HINT_CITATIONS);
19
+ if (paths.length > 0)
20
+ reasons.push(`code_citations:${paths.join(',')}`);
21
+ const hint = reasons.join(';');
22
+ return hint ? hint.slice(0, MAX_HINT_LENGTH) : null;
23
+ }
24
+ function anchorOf(id, node, trust, freshness, risk, tokenCost, reasonPath, provenance) {
25
+ const factGrade = isFactGradeAnchor(trust, freshness);
26
+ const firstHash = provenance[0]?.hash ?? null;
27
+ return {
28
+ id,
29
+ claim_hash: factGrade ? node?.contentHash ?? firstHash : null,
30
+ source_hash: factGrade ? firstHash : null,
31
+ hydrate_hint: attentionHydrateHint(provenance, trust, freshness, risk, factGrade),
32
+ reason_path: reasonPath,
33
+ trust_score: Number(trust.toFixed(2)),
34
+ freshness,
35
+ token_cost: Math.max(0, Math.trunc(tokenCost)),
36
+ provenance
37
+ };
38
+ }
39
+ export function projectContextGraphAnchors(index, selected, limit) {
40
+ const anchors = [];
41
+ const seen = new Set();
42
+ for (const node of selected) {
43
+ if (anchors.length >= limit)
44
+ break;
45
+ if (seen.has(node.nodeId) || node.provenance.length === 0)
46
+ continue;
47
+ seen.add(node.nodeId);
48
+ anchors.push(anchorOf(node.nodeId, index.nodesById.get(node.nodeId), node.trust, node.freshness, node.risk, node.tokenCost, [...node.reasonPath], [...node.provenance]));
49
+ }
50
+ return anchors;
51
+ }
52
+ export function resolveContextGraphAnchorNode(index, anchorId) {
53
+ const direct = index.nodesById.get(anchorId);
54
+ if (direct)
55
+ return direct;
56
+ if (!anchorId.startsWith('code:'))
57
+ return null;
58
+ const rest = anchorId.slice('code:'.length);
59
+ const byId = index.nodesById.get(rest);
60
+ if (byId)
61
+ return byId;
62
+ for (const candidate of index.nodesByLabel.get(rest.toLowerCase()) ?? []) {
63
+ const node = index.nodesById.get(candidate);
64
+ if (node?.kind === 'module')
65
+ return node;
66
+ }
67
+ return null;
68
+ }
69
+ export function projectContextPackAnchors(index, anchors) {
70
+ const out = [];
71
+ for (const anchor of anchors) {
72
+ const node = resolveContextGraphAnchorNode(index, anchor.id);
73
+ if (!node) {
74
+ out.push({
75
+ id: anchor.id,
76
+ claim_hash: null,
77
+ source_hash: null,
78
+ hydrate_hint: anchor.hydrate_hint ?? 'trust_action:hydrate_first;graph:unresolved_anchor',
79
+ reason_path: [],
80
+ trust_score: 0,
81
+ freshness: 'unknown',
82
+ token_cost: 0,
83
+ provenance: []
84
+ });
85
+ continue;
86
+ }
87
+ const provenance = groundContextGraphNode(index, node, [], CONTEXT_GRAPH_RANKING_CONFIG);
88
+ out.push(anchorOf(anchor.id, node, node.trust, node.freshness, node.risk, node.tokenCost, [node.id], provenance));
89
+ }
90
+ return out;
91
+ }
@@ -0,0 +1,58 @@
1
+ import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../contracts.js';
2
+ import { contextGraphQueryProfile } from '../profiles.js';
3
+ import { queryContextGraphSnapshot } from '../query/index.js';
4
+ import { loadContextGraphIndex } from '../query/load.js';
5
+ import { projectContextGraphAnchors } from './anchors.js';
6
+ export const CONTEXT_GRAPH_ATTENTION_TOKEN_BUDGET = 2000;
7
+ function unavailable(reason, profile, tokenBudget, warnings, snapshotHash) {
8
+ return {
9
+ available: false,
10
+ reason,
11
+ anchors: [],
12
+ profile,
13
+ snapshotHash,
14
+ snapshotFreshness: null,
15
+ tokenCost: 0,
16
+ tokenBudget,
17
+ provenanceCoverage: 0,
18
+ warnings: [...warnings],
19
+ repairCommand: CONTEXT_GRAPH_REPAIR_COMMAND
20
+ };
21
+ }
22
+ export async function readContextGraphAttention(request, options = {}) {
23
+ const profile = contextGraphQueryProfile(request.profile).name;
24
+ const tokenBudget = Math.max(0, request.tokenBudget ?? CONTEXT_GRAPH_ATTENTION_TOKEN_BUDGET);
25
+ const limit = Math.max(0, Math.trunc(request.limit));
26
+ const question = String(request.query ?? '').trim();
27
+ if (!question || limit === 0)
28
+ return unavailable('empty_query', profile, tokenBudget, [], null);
29
+ const load = await loadContextGraphIndex(request.root, options);
30
+ if (!load.ok || !load.index) {
31
+ return unavailable(load.errorCode ?? 'context_graph_missing', profile, tokenBudget, load.errors, load.snapshotHash || null);
32
+ }
33
+ const result = queryContextGraphSnapshot(load.index, {
34
+ root: request.root,
35
+ query: question,
36
+ profile,
37
+ tokenBudget,
38
+ maxSelected: limit,
39
+ ...(request.risk === undefined ? {} : { risk: request.risk })
40
+ }, { snapshotFreshness: load.freshness, warnings: load.warnings });
41
+ const anchors = projectContextGraphAnchors(load.index, result.selected, limit);
42
+ if (anchors.length === 0) {
43
+ return unavailable('context_graph_no_match', profile, tokenBudget, result.warnings, result.snapshotHash);
44
+ }
45
+ return {
46
+ available: true,
47
+ reason: null,
48
+ anchors,
49
+ profile: result.profile,
50
+ snapshotHash: result.snapshotHash,
51
+ snapshotFreshness: result.snapshotFreshness,
52
+ tokenCost: anchors.reduce((sum, anchor) => sum + anchor.token_cost, 0),
53
+ tokenBudget,
54
+ provenanceCoverage: result.provenanceCoverage,
55
+ warnings: result.warnings,
56
+ repairCommand: CONTEXT_GRAPH_REPAIR_COMMAND
57
+ };
58
+ }
@@ -0,0 +1,62 @@
1
+ import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../contracts.js';
2
+ import { readSourceHashes } from '../compiler/freshness.js';
3
+ import { readContextGraphMeta } from '../store/snapshot-store.js';
4
+ import { loadContextGraphIndex } from '../query/load.js';
5
+ import { projectCodePackFromGraph } from './code-pack.js';
6
+ const MAX_FRESHNESS_SOURCES = 512;
7
+ function citedPaths(pack) {
8
+ const paths = new Set();
9
+ for (const entry of pack.entries) {
10
+ for (const citation of entry.citations) {
11
+ if (citation.path)
12
+ paths.add(citation.path);
13
+ if (paths.size >= MAX_FRESHNESS_SOURCES)
14
+ return [...paths];
15
+ }
16
+ }
17
+ return [...paths];
18
+ }
19
+ export async function buildWorkspaceCodePack(root, options = {}) {
20
+ const load = await loadContextGraphIndex(root, options);
21
+ if (!load.ok || !load.index) {
22
+ return {
23
+ ok: false,
24
+ pack: null,
25
+ errorCode: load.errorCode,
26
+ errors: load.errors,
27
+ warnings: load.warnings,
28
+ snapshotHash: load.snapshotHash,
29
+ snapshotFreshness: 'stale',
30
+ candidateCount: 0,
31
+ omittedForBudget: 0,
32
+ repairCommand: CONTEXT_GRAPH_REPAIR_COMMAND
33
+ };
34
+ }
35
+ const metaLoad = await readContextGraphMeta(root);
36
+ const gitHeadSha = metaLoad.status === 'ok' && metaLoad.meta ? metaLoad.meta.cacheKeyParts.head : null;
37
+ const base = {
38
+ tokenBudget: options.tokenBudget,
39
+ query: options.query,
40
+ profile: options.profile,
41
+ risk: options.risk,
42
+ maxEntries: options.maxEntries,
43
+ generatedAt: options.generatedAt,
44
+ gitHeadSha,
45
+ snapshotFreshness: load.freshness
46
+ };
47
+ const firstPass = projectCodePackFromGraph(root, load.index, base);
48
+ const observedSourceHashes = await readSourceHashes(root, citedPaths(firstPass.pack));
49
+ const projection = projectCodePackFromGraph(root, load.index, { ...base, observedSourceHashes });
50
+ return {
51
+ ok: true,
52
+ pack: projection.pack,
53
+ errorCode: null,
54
+ errors: [],
55
+ warnings: load.warnings,
56
+ snapshotHash: load.snapshotHash,
57
+ snapshotFreshness: load.freshness,
58
+ candidateCount: projection.candidateCount,
59
+ omittedForBudget: projection.omittedForBudget,
60
+ repairCommand: CONTEXT_GRAPH_REPAIR_COMMAND
61
+ };
62
+ }
@@ -0,0 +1,144 @@
1
+ import { sha256 } from '../../../fsx.js';
2
+ import { buildContextGraphIndex } from '../graph-index.js';
3
+ import { contextGraphQueryProfile } from '../profiles.js';
4
+ import { queryContextGraphSnapshot } from '../query/index.js';
5
+ import { DEFAULT_CODE_PACK_TOKEN_BUDGET, normalizeCodePackTokenBudget, CODE_PACK_SCHEMA } from './pack-contract.js';
6
+ import { codePackEntryId, describeContextGraphNode, estimateEntryTokenCost, projectedFreshness, projectedTrustScore } from './node-summary.js';
7
+ import { rankModuleCandidates, sortCandidates } from './module-view.js';
8
+ const MAX_PACK_ENTRIES = 64;
9
+ function isIndex(value) {
10
+ return typeof value.nodesById?.get === 'function';
11
+ }
12
+ function citationsFromProvenance(selected) {
13
+ const citations = [];
14
+ const seen = new Set();
15
+ for (const ref of selected.provenance) {
16
+ if (!ref.path || seen.has(ref.path))
17
+ continue;
18
+ seen.add(ref.path);
19
+ citations.push(ref.line === undefined ? { path: ref.path } : { path: ref.path, line: ref.line });
20
+ }
21
+ return citations;
22
+ }
23
+ function candidatesFromQuery(index, result) {
24
+ const candidates = [];
25
+ for (const selected of result.selected) {
26
+ const node = index.nodesById.get(selected.nodeId);
27
+ if (!node)
28
+ continue;
29
+ const citations = citationsFromProvenance(selected);
30
+ if (citations.length === 0)
31
+ continue;
32
+ candidates.push({
33
+ node,
34
+ text: describeContextGraphNode(index, node),
35
+ citations,
36
+ members: [node],
37
+ reasonPath: selected.reasonPath,
38
+ score: selected.score
39
+ });
40
+ }
41
+ return sortCandidates(candidates);
42
+ }
43
+ function combineFreshness(members, snapshotFreshness, observed) {
44
+ let verdict = null;
45
+ for (const member of members) {
46
+ if (!member.contentHash)
47
+ continue;
48
+ const memberVerdict = projectedFreshness(member, 'fresh', observed);
49
+ if (memberVerdict === 'stale') {
50
+ verdict = 'stale';
51
+ break;
52
+ }
53
+ if (memberVerdict === 'unknown')
54
+ verdict = 'unknown';
55
+ else if (verdict === null)
56
+ verdict = 'fresh';
57
+ }
58
+ const resolved = verdict ?? members[0]?.freshness ?? 'unknown';
59
+ return snapshotFreshness === 'stale' && resolved === 'fresh' ? 'stale' : resolved;
60
+ }
61
+ function entryOf(candidate, taken, snapshotFreshness, observed) {
62
+ if (candidate.citations.length === 0)
63
+ return null;
64
+ const freshness = combineFreshness(candidate.members, snapshotFreshness, observed);
65
+ return {
66
+ id: codePackEntryId(candidate.node, taken),
67
+ text: candidate.text,
68
+ citations: candidate.citations,
69
+ trust_score: projectedTrustScore(candidate.node, candidate.citations.length, freshness),
70
+ freshness,
71
+ token_cost: estimateEntryTokenCost(candidate.text)
72
+ };
73
+ }
74
+ export function computeCodePackIndexDigest(snapshotHash, entries) {
75
+ const stable = entries.map((entry) => [
76
+ entry.id,
77
+ entry.text,
78
+ entry.citations.map((citation) => `${citation.path}${citation.line === undefined ? '' : `:${citation.line}`}`),
79
+ entry.trust_score,
80
+ entry.freshness,
81
+ entry.token_cost
82
+ ]);
83
+ return sha256(JSON.stringify({ snapshotHash, entries: stable }));
84
+ }
85
+ export function isCodePackProjectionBoundToSnapshot(snapshotHash, pack) {
86
+ return pack.index_digest === computeCodePackIndexDigest(snapshotHash, pack.entries);
87
+ }
88
+ function countFileNodes(snapshot) {
89
+ let count = 0;
90
+ for (const node of snapshot.nodes)
91
+ if (node.kind === 'file')
92
+ count += 1;
93
+ return count;
94
+ }
95
+ export function projectCodePackFromGraph(root, source, options = {}) {
96
+ const index = isIndex(source) ? source : buildContextGraphIndex(source);
97
+ const tokenBudget = normalizeCodePackTokenBudget(options.tokenBudget ?? DEFAULT_CODE_PACK_TOKEN_BUDGET);
98
+ const profile = contextGraphQueryProfile(options.profile);
99
+ const risk = options.risk === 'high' ? 'high' : 'normal';
100
+ const snapshotFreshness = options.snapshotFreshness ?? 'fresh';
101
+ const maxEntries = Math.max(1, Math.min(options.maxEntries ?? MAX_PACK_ENTRIES, MAX_PACK_ENTRIES));
102
+ const question = String(options.query ?? '').trim();
103
+ let queryResult = null;
104
+ let candidates;
105
+ if (question) {
106
+ queryResult = queryContextGraphSnapshot(index, { root, query: question, profile: profile.name, tokenBudget, maxSelected: maxEntries, risk }, { snapshotFreshness });
107
+ candidates = candidatesFromQuery(index, queryResult);
108
+ }
109
+ else {
110
+ candidates = rankModuleCandidates(index, profile, risk);
111
+ }
112
+ const entries = [];
113
+ const taken = new Set();
114
+ let totalTokenCost = 0;
115
+ let omittedForBudget = 0;
116
+ for (const candidate of candidates) {
117
+ if (entries.length >= maxEntries)
118
+ break;
119
+ const entry = entryOf(candidate, taken, snapshotFreshness, options.observedSourceHashes);
120
+ if (!entry)
121
+ continue;
122
+ if (totalTokenCost + entry.token_cost > tokenBudget) {
123
+ omittedForBudget += 1;
124
+ continue;
125
+ }
126
+ taken.add(entry.id);
127
+ entries.push(entry);
128
+ totalTokenCost += entry.token_cost;
129
+ }
130
+ const pack = {
131
+ schema: CODE_PACK_SCHEMA,
132
+ generated_at: options.generatedAt ?? new Date().toISOString(),
133
+ git_head_sha: options.gitHeadSha ?? null,
134
+ source_file_count: countFileNodes(index.snapshot),
135
+ index_digest: computeCodePackIndexDigest(index.snapshot.snapshotHash, entries),
136
+ entries,
137
+ token_budget: tokenBudget,
138
+ total_token_cost: totalTokenCost
139
+ };
140
+ return { pack, query: queryResult, candidateCount: candidates.length, omittedForBudget };
141
+ }
142
+ export function buildCodePackFromGraph(root, source, options = {}) {
143
+ return projectCodePackFromGraph(root, source, options).pack;
144
+ }
@@ -0,0 +1,7 @@
1
+ export { CODE_PACK_SCHEMA, DEFAULT_CODE_PACK_TOKEN_BUDGET, normalizeCodePackTokenBudget } from './pack-contract.js';
2
+ export { buildCodePackFromGraph, computeCodePackIndexDigest, projectCodePackFromGraph } from './code-pack.js';
3
+ export { buildWorkspaceCodePack } from './code-pack-workspace.js';
4
+ export { rankModuleCandidates, sortCandidates } from './module-view.js';
5
+ export { codePackEntryId, describeContextGraphNode, estimateEntryTokenCost, projectedFreshness, projectedTrustScore } from './node-summary.js';
6
+ export { ATTENTION_FACT_TRUST_FLOOR, attentionHydrateHint, isFactGradeAnchor, projectContextGraphAnchors, projectContextPackAnchors, resolveContextGraphAnchorNode } from './anchors.js';
7
+ export { CONTEXT_GRAPH_ATTENTION_TOKEN_BUDGET, readContextGraphAttention } from './attention.js';
@@ -0,0 +1,140 @@
1
+ import { compareContextGraphIds } from '../ids.js';
2
+ import { outgoingEdges } from '../graph-index.js';
3
+ import { profileEdgeWeight } from '../profiles.js';
4
+ import { describeContextGraphNode } from './node-summary.js';
5
+ const RISK_RANK = { low: 0, medium: 1, high: 2, protected: 3 };
6
+ const MAX_CITATIONS = 8;
7
+ function riskBonus(node, risk) {
8
+ const rank = RISK_RANK[node.risk] ?? 0;
9
+ return risk === 'high' ? rank * 2 : rank * 0.75;
10
+ }
11
+ function metaNumber(node, key) {
12
+ const value = node.metadata[key];
13
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
14
+ }
15
+ function isExportedSymbol(node) {
16
+ return node.kind === 'symbol' && node.metadata.exported === true;
17
+ }
18
+ function pushCitation(into, seen, node) {
19
+ if (into.length >= MAX_CITATIONS)
20
+ return;
21
+ if (!node.path || seen.has(node.path))
22
+ return;
23
+ seen.add(node.path);
24
+ into.push(node.locator?.line === undefined ? { path: node.path } : { path: node.path, line: node.locator.line });
25
+ }
26
+ function ownerIndex(index, modules) {
27
+ const owner = new Map();
28
+ for (const module of modules) {
29
+ for (const edge of outgoingEdges(index, module.id)) {
30
+ if (edge.type !== 'contains')
31
+ continue;
32
+ if (!owner.has(edge.to))
33
+ owner.set(edge.to, module.id);
34
+ }
35
+ }
36
+ return owner;
37
+ }
38
+ function accumulateFile(index, accumulator, file, owner, profile) {
39
+ accumulator.members.push(file);
40
+ accumulator.fanIn += metaNumber(file, 'fanIn');
41
+ for (const edge of outgoingEdges(index, file.id)) {
42
+ const weight = profileEdgeWeight(profile, edge.type);
43
+ if (weight > 0)
44
+ accumulator.relationScore += weight;
45
+ const target = index.nodesById.get(edge.to);
46
+ if (!target)
47
+ continue;
48
+ if (isExportedSymbol(target))
49
+ accumulator.exports.push(target.label);
50
+ if (edge.type !== 'imports' && edge.type !== 'depends_on')
51
+ continue;
52
+ const ownerId = owner.get(target.id);
53
+ if (!ownerId || ownerId === accumulator.module.id)
54
+ continue;
55
+ const ownerNode = index.nodesById.get(ownerId);
56
+ if (ownerNode)
57
+ accumulator.dependsOn.push(ownerNode.label);
58
+ }
59
+ }
60
+ function candidateOf(index, accumulator, risk) {
61
+ const files = accumulator.members.filter((member) => member.id !== accumulator.module.id);
62
+ const citations = [];
63
+ const seen = new Set();
64
+ for (const file of files)
65
+ pushCitation(citations, seen, file);
66
+ if (citations.length === 0)
67
+ pushCitation(citations, seen, accumulator.module);
68
+ const text = describeContextGraphNode(index, accumulator.module, {
69
+ exports: accumulator.exports,
70
+ dependsOn: accumulator.dependsOn,
71
+ fileCount: files.length
72
+ });
73
+ const score = accumulator.relationScore
74
+ + 3 * Math.log2(1 + accumulator.fanIn)
75
+ + Math.log2(1 + accumulator.exports.length)
76
+ + riskBonus(accumulator.module, risk);
77
+ return {
78
+ node: accumulator.module,
79
+ text,
80
+ citations,
81
+ members: accumulator.members,
82
+ reasonPath: [accumulator.module.id, 'contains', `${files.length} file(s)`],
83
+ score: Number(score.toFixed(4))
84
+ };
85
+ }
86
+ export function rankModuleCandidates(index, profile, risk) {
87
+ const modules = [];
88
+ for (const node of index.snapshot.nodes)
89
+ if (node.kind === 'module')
90
+ modules.push(node);
91
+ if (modules.length === 0)
92
+ return rankFileCandidates(index, profile, risk);
93
+ const owner = ownerIndex(index, modules);
94
+ const candidates = [];
95
+ for (const module of modules) {
96
+ const accumulator = {
97
+ module,
98
+ members: [module],
99
+ exports: [],
100
+ dependsOn: [],
101
+ relationScore: 0,
102
+ fanIn: 0
103
+ };
104
+ for (const edge of outgoingEdges(index, module.id)) {
105
+ if (edge.type !== 'contains')
106
+ continue;
107
+ const file = index.nodesById.get(edge.to);
108
+ if (file)
109
+ accumulateFile(index, accumulator, file, owner, profile);
110
+ }
111
+ candidates.push(candidateOf(index, accumulator, risk));
112
+ }
113
+ return sortCandidates(candidates);
114
+ }
115
+ function rankFileCandidates(index, profile, risk) {
116
+ const candidates = [];
117
+ for (const node of index.snapshot.nodes) {
118
+ if (node.kind !== 'file' || !node.path)
119
+ continue;
120
+ let relationScore = 0;
121
+ for (const edge of outgoingEdges(index, node.id))
122
+ relationScore += profileEdgeWeight(profile, edge.type);
123
+ const citations = [];
124
+ pushCitation(citations, new Set(), node);
125
+ if (citations.length === 0)
126
+ continue;
127
+ candidates.push({
128
+ node,
129
+ text: describeContextGraphNode(index, node),
130
+ citations,
131
+ members: [node],
132
+ reasonPath: [node.id],
133
+ score: Number((relationScore + 3 * Math.log2(1 + metaNumber(node, 'fanIn')) + riskBonus(node, risk)).toFixed(4))
134
+ });
135
+ }
136
+ return sortCandidates(candidates);
137
+ }
138
+ export function sortCandidates(candidates) {
139
+ return candidates.sort((left, right) => right.score - left.score || compareContextGraphIds(left.node.id, right.node.id));
140
+ }
@@ -0,0 +1,118 @@
1
+ import { CONTEXT_GRAPH_MISSING_SOURCE_HASH } from '../compiler/freshness.js';
2
+ import { outgoingEdges } from '../graph-index.js';
3
+ const EXPORT_EDGE_TYPES = new Set(['defines', 'reexports']);
4
+ const DEPENDENCY_EDGE_TYPES = new Set(['imports', 'depends_on', 'routes_to']);
5
+ const MAX_LISTED = 5;
6
+ function metaString(metadata, key) {
7
+ const value = metadata[key];
8
+ return typeof value === 'string' && value ? value : null;
9
+ }
10
+ function metaNumber(metadata, key) {
11
+ const value = metadata[key];
12
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
13
+ }
14
+ function metaBoolean(metadata, key) {
15
+ return metadata[key] === true;
16
+ }
17
+ function compact(value) {
18
+ return value.replace(/\s+/g, ' ').trim();
19
+ }
20
+ function listOf(values) {
21
+ const unique = [...new Set(values.filter((value) => Boolean(value)))].sort();
22
+ if (unique.length === 0)
23
+ return '';
24
+ const shown = unique.slice(0, MAX_LISTED);
25
+ const rest = unique.length - shown.length;
26
+ return `${shown.join(', ')}${rest > 0 ? `, and ${rest} more` : ''}`;
27
+ }
28
+ function relatedLabels(index, nodeId, types, exportedOnly) {
29
+ const labels = [];
30
+ for (const edge of outgoingEdges(index, nodeId)) {
31
+ if (!types.has(edge.type))
32
+ continue;
33
+ const target = index.nodesById.get(edge.to);
34
+ if (!target)
35
+ continue;
36
+ if (exportedOnly && target.kind === 'symbol' && !metaBoolean(target.metadata, 'exported'))
37
+ continue;
38
+ labels.push(target.label);
39
+ }
40
+ return labels;
41
+ }
42
+ function headline(node, extras) {
43
+ const where = node.path ? ` at ${node.path}` : '';
44
+ const line = node.locator?.line !== undefined ? `:${node.locator.line}` : '';
45
+ switch (node.kind) {
46
+ case 'file': {
47
+ const language = metaString(node.metadata, 'language') ?? 'source';
48
+ const lines = metaNumber(node.metadata, 'lines');
49
+ const fanIn = metaNumber(node.metadata, 'fanIn') ?? 0;
50
+ const size = lines === null ? '' : `${lines} lines, `;
51
+ return `${node.label} is a ${language} file${where} (${size}fan-in ${fanIn}, ${node.risk} risk).`;
52
+ }
53
+ case 'symbol': {
54
+ const symbolKind = metaString(node.metadata, 'symbolKind') ?? 'symbol';
55
+ const exported = metaBoolean(node.metadata, 'exported') ? 'exported ' : 'internal ';
56
+ return `${node.label} is an ${exported}${symbolKind}${where}${line}.`;
57
+ }
58
+ case 'module': {
59
+ const dir = metaString(node.metadata, 'dir') ?? node.path ?? node.label;
60
+ const fileCount = extras.fileCount ?? metaNumber(node.metadata, 'fileCount') ?? 0;
61
+ return `${node.label} is a module at ${dir} (${fileCount} file${fileCount === 1 ? '' : 's'}, ${node.risk} risk).`;
62
+ }
63
+ case 'test':
64
+ return `${node.label} is a test${where}.`;
65
+ case 'gate':
66
+ return `${node.label} is a ${node.risk} gate${where}.`;
67
+ case 'command':
68
+ return `${node.label} is a CLI command declared${where}.`;
69
+ default:
70
+ return `${node.label} is a ${node.kind} node${where}${line}.`;
71
+ }
72
+ }
73
+ export function describeContextGraphNode(index, node, extras = {}) {
74
+ const exports = extras.exports ?? relatedLabels(index, node.id, EXPORT_EDGE_TYPES, true);
75
+ const dependsOn = extras.dependsOn ?? relatedLabels(index, node.id, DEPENDENCY_EDGE_TYPES, false);
76
+ const exportsPart = exports.length > 0 ? `Key exports: ${listOf(exports)}.` : 'It has no exported surface in the graph.';
77
+ const dependsPart = dependsOn.length > 0 ? `Depends on: ${listOf(dependsOn)}.` : 'It has no recorded outbound dependency.';
78
+ return compact(`${headline(node, extras)} ${exportsPart} ${dependsPart}`);
79
+ }
80
+ export function estimateEntryTokenCost(text) {
81
+ return Math.max(1, Math.ceil(text.length / 4));
82
+ }
83
+ export function codePackEntryId(node, taken) {
84
+ const preferred = node.kind === 'module' ? `code:${node.label}` : `code:${node.id}`;
85
+ if (!taken.has(preferred))
86
+ return preferred;
87
+ const fallback = `code:${node.id}`;
88
+ return taken.has(fallback) ? `${fallback}#${node.kind}` : fallback;
89
+ }
90
+ export function projectedFreshness(node, snapshotFreshness, observedHashes) {
91
+ let verdict = node.freshness;
92
+ const observed = node.path === undefined ? undefined : observedHashes?.[node.path];
93
+ if (observed !== undefined) {
94
+ if (observed === CONTEXT_GRAPH_MISSING_SOURCE_HASH)
95
+ verdict = 'stale';
96
+ else if (node.contentHash)
97
+ verdict = observed === node.contentHash ? 'fresh' : 'stale';
98
+ else
99
+ verdict = 'unknown';
100
+ }
101
+ if (snapshotFreshness === 'stale' && verdict === 'fresh')
102
+ return 'stale';
103
+ return verdict;
104
+ }
105
+ export function projectedTrustScore(node, provenanceCount, freshness) {
106
+ let score = Math.max(0, Math.min(1, node.trust));
107
+ if (provenanceCount === 0)
108
+ return 0;
109
+ if (provenanceCount === 1)
110
+ score -= 0.05;
111
+ if (freshness === 'stale')
112
+ score -= 0.3;
113
+ else if (freshness === 'unknown')
114
+ score -= 0.15;
115
+ if (node.risk === 'high')
116
+ score -= 0.1;
117
+ return Math.max(0, Math.min(1, Number(score.toFixed(2))));
118
+ }
@@ -0,0 +1,6 @@
1
+ export const CODE_PACK_SCHEMA = 'sks.code-pack.v1';
2
+ export const DEFAULT_CODE_PACK_TOKEN_BUDGET = 8000;
3
+ export function normalizeCodePackTokenBudget(value) {
4
+ const parsed = Number(value);
5
+ return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : DEFAULT_CODE_PACK_TOKEN_BUDGET;
6
+ }