attocode-code-intel 0.1.0__py3-none-any.whl

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 (345) hide show
  1. attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
  2. attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
  3. attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
  4. attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
  5. attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. attocode_intel/GUIDELINES.md +432 -0
  7. attocode_intel/__init__.py +52 -0
  8. attocode_intel/_internal/__init__.py +1 -0
  9. attocode_intel/_internal/errors/__init__.py +126 -0
  10. attocode_intel/_internal/integrations/__init__.py +1 -0
  11. attocode_intel/_internal/integrations/context/__init__.py +1 -0
  12. attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
  13. attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
  14. attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
  15. attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
  16. attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
  17. attocode_intel/_internal/integrations/context/cas.py +466 -0
  18. attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
  19. attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
  20. attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
  21. attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
  22. attocode_intel/_internal/integrations/context/cross_references.py +616 -0
  23. attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
  24. attocode_intel/_internal/integrations/context/embeddings.py +282 -0
  25. attocode_intel/_internal/integrations/context/frecency.py +427 -0
  26. attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
  27. attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
  28. attocode_intel/_internal/integrations/context/hydration.py +90 -0
  29. attocode_intel/_internal/integrations/context/index_repair.py +87 -0
  30. attocode_intel/_internal/integrations/context/index_store.py +586 -0
  31. attocode_intel/_internal/integrations/context/memory_store.py +602 -0
  32. attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
  33. attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
  34. attocode_intel/_internal/integrations/context/query_history.py +417 -0
  35. attocode_intel/_internal/integrations/context/reranker.py +351 -0
  36. attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
  37. attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
  38. attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
  39. attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
  40. attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
  41. attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
  42. attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
  43. attocode_intel/_internal/integrations/context/vector_store.py +795 -0
  44. attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
  45. attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
  46. attocode_intel/_internal/integrations/lsp/client.py +1140 -0
  47. attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
  48. attocode_intel/_internal/integrations/mcp/client.py +253 -0
  49. attocode_intel/_internal/integrations/security/__init__.py +1 -0
  50. attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
  51. attocode_intel/_internal/integrations/security/dataflow.py +522 -0
  52. attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
  53. attocode_intel/_internal/integrations/security/matcher.py +44 -0
  54. attocode_intel/_internal/integrations/security/patterns.py +724 -0
  55. attocode_intel/_internal/integrations/security/scanner.py +351 -0
  56. attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
  57. attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
  58. attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
  59. attocode_intel/_internal/tricks/__init__.py +1 -0
  60. attocode_intel/_internal/tricks/recursive_context.py +357 -0
  61. attocode_intel/_internal/types/__init__.py +1 -0
  62. attocode_intel/_internal/types/events.py +289 -0
  63. attocode_intel/_internal/types/severity.py +15 -0
  64. attocode_intel/_shared.py +341 -0
  65. attocode_intel/analysis_coverage.py +56 -0
  66. attocode_intel/api/__init__.py +1 -0
  67. attocode_intel/api/access.py +32 -0
  68. attocode_intel/api/app.py +247 -0
  69. attocode_intel/api/auth/__init__.py +128 -0
  70. attocode_intel/api/auth/api_keys.py +61 -0
  71. attocode_intel/api/auth/context.py +34 -0
  72. attocode_intel/api/auth/jwt.py +66 -0
  73. attocode_intel/api/auth/oauth.py +146 -0
  74. attocode_intel/api/auth/passwords.py +15 -0
  75. attocode_intel/api/deps.py +351 -0
  76. attocode_intel/api/knowledge_bridge.py +13 -0
  77. attocode_intel/api/mcp_transport.py +51 -0
  78. attocode_intel/api/middleware.py +411 -0
  79. attocode_intel/api/models.py +698 -0
  80. attocode_intel/api/providers/__init__.py +1 -0
  81. attocode_intel/api/providers/db_provider.py +1106 -0
  82. attocode_intel/api/providers/local_provider.py +476 -0
  83. attocode_intel/api/providers/remote_provider.py +740 -0
  84. attocode_intel/api/repository_source.py +21 -0
  85. attocode_intel/api/routes/__init__.py +1 -0
  86. attocode_intel/api/routes/_pin_helper.py +158 -0
  87. attocode_intel/api/routes/activity.py +148 -0
  88. attocode_intel/api/routes/adr.py +102 -0
  89. attocode_intel/api/routes/analysis.py +515 -0
  90. attocode_intel/api/routes/api_keys.py +170 -0
  91. attocode_intel/api/routes/auth.py +428 -0
  92. attocode_intel/api/routes/branches.py +262 -0
  93. attocode_intel/api/routes/cross_repo_search.py +198 -0
  94. attocode_intel/api/routes/embeddings.py +386 -0
  95. attocode_intel/api/routes/files.py +132 -0
  96. attocode_intel/api/routes/files_v2.py +388 -0
  97. attocode_intel/api/routes/gc.py +322 -0
  98. attocode_intel/api/routes/git_v2.py +433 -0
  99. attocode_intel/api/routes/graph.py +174 -0
  100. attocode_intel/api/routes/graph_viz.py +160 -0
  101. attocode_intel/api/routes/health.py +62 -0
  102. attocode_intel/api/routes/history.py +175 -0
  103. attocode_intel/api/routes/jobs.py +84 -0
  104. attocode_intel/api/routes/learning.py +205 -0
  105. attocode_intel/api/routes/lsp.py +272 -0
  106. attocode_intel/api/routes/notify.py +623 -0
  107. attocode_intel/api/routes/operations.py +27 -0
  108. attocode_intel/api/routes/orgs.py +924 -0
  109. attocode_intel/api/routes/preferences.py +100 -0
  110. attocode_intel/api/routes/presence.py +42 -0
  111. attocode_intel/api/routes/projects.py +116 -0
  112. attocode_intel/api/routes/repos.py +47 -0
  113. attocode_intel/api/routes/search.py +180 -0
  114. attocode_intel/api/routes/snapshots.py +614 -0
  115. attocode_intel/api/routes/webhooks.py +313 -0
  116. attocode_intel/api/routes/websocket.py +116 -0
  117. attocode_intel/api/static/graph.html +239 -0
  118. attocode_intel/api/utils.py +96 -0
  119. attocode_intel/artifacts/__init__.py +45 -0
  120. attocode_intel/artifacts/hashing.py +230 -0
  121. attocode_intel/artifacts/provenance.py +190 -0
  122. attocode_intel/artifacts/retrieval_pin.py +159 -0
  123. attocode_intel/audit.py +59 -0
  124. attocode_intel/bug_finder.py +206 -0
  125. attocode_intel/bundle.py +80 -0
  126. attocode_intel/catalog.py +242 -0
  127. attocode_intel/change_review.py +75 -0
  128. attocode_intel/cli.py +3062 -0
  129. attocode_intel/community.py +63 -0
  130. attocode_intel/confidence/__init__.py +144 -0
  131. attocode_intel/confidence/jev.py +234 -0
  132. attocode_intel/confidence/llm.py +330 -0
  133. attocode_intel/confidence/redact.py +44 -0
  134. attocode_intel/confidence/settings.py +74 -0
  135. attocode_intel/config.py +256 -0
  136. attocode_intel/crypto.py +31 -0
  137. attocode_intel/db/__init__.py +7 -0
  138. attocode_intel/db/base.py +33 -0
  139. attocode_intel/db/engine.py +61 -0
  140. attocode_intel/db/models.py +529 -0
  141. attocode_intel/entrypoint.py +185 -0
  142. attocode_intel/focused_evidence.py +130 -0
  143. attocode_intel/freshness.py +154 -0
  144. attocode_intel/gateway.py +734 -0
  145. attocode_intel/git/__init__.py +7 -0
  146. attocode_intel/git/credentials.py +75 -0
  147. attocode_intel/git/manager.py +493 -0
  148. attocode_intel/git/models.py +96 -0
  149. attocode_intel/git/storage.py +98 -0
  150. attocode_intel/graph_query_parser.py +761 -0
  151. attocode_intel/helpers.py +712 -0
  152. attocode_intel/indexing/__init__.py +29 -0
  153. attocode_intel/indexing/debouncer.py +136 -0
  154. attocode_intel/indexing/delta_indexer.py +151 -0
  155. attocode_intel/indexing/full_indexer.py +323 -0
  156. attocode_intel/indexing/incremental.py +240 -0
  157. attocode_intel/indexing/parser.py +403 -0
  158. attocode_intel/indexing/summarizer.py +264 -0
  159. attocode_intel/installer.py +1896 -0
  160. attocode_intel/knowledge.py +269 -0
  161. attocode_intel/local_knowledge.py +77 -0
  162. attocode_intel/migrations/alembic.ini +36 -0
  163. attocode_intel/migrations/env.py +71 -0
  164. attocode_intel/migrations/script.py.mako +27 -0
  165. attocode_intel/migrations/versions/001_initial_schema.py +219 -0
  166. attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
  167. attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
  168. attocode_intel/migrations/versions/004_audit_events.py +38 -0
  169. attocode_intel/migrations/versions/005_user_preferences.py +30 -0
  170. attocode_intel/migrations/versions/006_google_oauth.py +27 -0
  171. attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
  172. attocode_intel/migrations/versions/008_symbol_references.py +37 -0
  173. attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
  174. attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
  175. attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
  176. attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
  177. attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
  178. attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
  179. attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
  180. attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
  181. attocode_intel/migrations/versions/017_provenance_table.py +102 -0
  182. attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
  183. attocode_intel/migrations/versions/019_symref_caller.py +42 -0
  184. attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
  185. attocode_intel/onboarding.py +258 -0
  186. attocode_intel/output.py +178 -0
  187. attocode_intel/precision.py +133 -0
  188. attocode_intel/presence.py +160 -0
  189. attocode_intel/probe.py +110 -0
  190. attocode_intel/project_dir.py +97 -0
  191. attocode_intel/pubsub.py +113 -0
  192. attocode_intel/query_ranking.py +242 -0
  193. attocode_intel/readiness.py +972 -0
  194. attocode_intel/redis.py +51 -0
  195. attocode_intel/reference_pages.py +60 -0
  196. attocode_intel/remote.py +131 -0
  197. attocode_intel/repo_ranker.py +246 -0
  198. attocode_intel/request_context.py +82 -0
  199. attocode_intel/rules/__init__.py +28 -0
  200. attocode_intel/rules/ci.py +320 -0
  201. attocode_intel/rules/combinators.py +293 -0
  202. attocode_intel/rules/enricher.py +127 -0
  203. attocode_intel/rules/evolution.py +452 -0
  204. attocode_intel/rules/executor.py +531 -0
  205. attocode_intel/rules/filters/__init__.py +5 -0
  206. attocode_intel/rules/filters/pipeline.py +151 -0
  207. attocode_intel/rules/formatter.py +198 -0
  208. attocode_intel/rules/hygiene.py +242 -0
  209. attocode_intel/rules/importers/__init__.py +1 -0
  210. attocode_intel/rules/importers/semgrep.py +382 -0
  211. attocode_intel/rules/loader.py +292 -0
  212. attocode_intel/rules/marketplace.py +375 -0
  213. attocode_intel/rules/metavar.py +181 -0
  214. attocode_intel/rules/model.py +317 -0
  215. attocode_intel/rules/packs/__init__.py +1 -0
  216. attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
  217. attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
  218. attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
  219. attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
  220. attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
  221. attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
  222. attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
  223. attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
  224. attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
  225. attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
  226. attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
  227. attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
  228. attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
  229. attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
  230. attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
  231. attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
  232. attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
  233. attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
  234. attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
  235. attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
  236. attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
  237. attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
  238. attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
  239. attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
  240. attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
  241. attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
  242. attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
  243. attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
  244. attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
  245. attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
  246. attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
  247. attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
  248. attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
  249. attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
  250. attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
  251. attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
  252. attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
  253. attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
  254. attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
  255. attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
  256. attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
  257. attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
  258. attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
  259. attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
  260. attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
  261. attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
  262. attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
  263. attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
  264. attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
  265. attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
  266. attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
  267. attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
  268. attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
  269. attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
  270. attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
  271. attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
  272. attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
  273. attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
  274. attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
  275. attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
  276. attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
  277. attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
  278. attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
  279. attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
  280. attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
  281. attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
  282. attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
  283. attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
  284. attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
  285. attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
  286. attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
  287. attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
  288. attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
  289. attocode_intel/rules/packs/pack_loader.py +244 -0
  290. attocode_intel/rules/plugins/__init__.py +1 -0
  291. attocode_intel/rules/plugins/plugin_loader.py +132 -0
  292. attocode_intel/rules/profiling.py +332 -0
  293. attocode_intel/rules/registry.py +213 -0
  294. attocode_intel/rules/sarif.py +187 -0
  295. attocode_intel/rules/synthesis.py +420 -0
  296. attocode_intel/rules/testing.py +326 -0
  297. attocode_intel/server.py +734 -0
  298. attocode_intel/service.py +3416 -0
  299. attocode_intel/sharing.py +57 -0
  300. attocode_intel/storage/__init__.py +6 -0
  301. attocode_intel/storage/branch_overlay.py +352 -0
  302. attocode_intel/storage/content_store.py +220 -0
  303. attocode_intel/storage/dependency_store.py +143 -0
  304. attocode_intel/storage/diff_engine.py +187 -0
  305. attocode_intel/storage/embedding_store.py +594 -0
  306. attocode_intel/storage/provenance_store.py +137 -0
  307. attocode_intel/storage/security_scanner_db.py +164 -0
  308. attocode_intel/storage/symbol_store.py +160 -0
  309. attocode_intel/structural_evidence.py +144 -0
  310. attocode_intel/symbol_inspection.py +141 -0
  311. attocode_intel/symbol_links.py +167 -0
  312. attocode_intel/telemetry.py +42 -0
  313. attocode_intel/test_ranking.py +73 -0
  314. attocode_intel/testing/__init__.py +43 -0
  315. attocode_intel/testing/fixtures.py +122 -0
  316. attocode_intel/testing/helpers.py +75 -0
  317. attocode_intel/testing/mocks.py +99 -0
  318. attocode_intel/tools/__init__.py +2 -0
  319. attocode_intel/tools/adr_tools.py +628 -0
  320. attocode_intel/tools/analysis_tools.py +382 -0
  321. attocode_intel/tools/composite_tools.py +695 -0
  322. attocode_intel/tools/cross_mode_tools.py +173 -0
  323. attocode_intel/tools/dead_code_tools.py +582 -0
  324. attocode_intel/tools/distill_tools.py +353 -0
  325. attocode_intel/tools/frecency_tools.py +220 -0
  326. attocode_intel/tools/fuzzy_tools.py +255 -0
  327. attocode_intel/tools/history_tools.py +486 -0
  328. attocode_intel/tools/learning_tools.py +222 -0
  329. attocode_intel/tools/lsp_tools.py +218 -0
  330. attocode_intel/tools/maintenance_tools.py +1662 -0
  331. attocode_intel/tools/navigation_tools.py +220 -0
  332. attocode_intel/tools/overlay_tools.py +540 -0
  333. attocode_intel/tools/pin_store.py +432 -0
  334. attocode_intel/tools/pin_tools.py +266 -0
  335. attocode_intel/tools/query_constraints_tools.py +262 -0
  336. attocode_intel/tools/query_history_tools.py +158 -0
  337. attocode_intel/tools/readiness_tools.py +44 -0
  338. attocode_intel/tools/rule_tools.py +992 -0
  339. attocode_intel/tools/search_tools.py +770 -0
  340. attocode_intel/tools/snapshot_tools.py +624 -0
  341. attocode_intel/workers/__init__.py +1 -0
  342. attocode_intel/workers/job_utils.py +111 -0
  343. attocode_intel/workers/jobs.py +573 -0
  344. attocode_intel/workers/run.py +30 -0
  345. attocode_intel/workers/settings.py +61 -0
@@ -0,0 +1,96 @@
1
+ """Shared utilities for the HTTP API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import TYPE_CHECKING
7
+
8
+ from fastapi import HTTPException
9
+ from sqlalchemy import select
10
+
11
+ if TYPE_CHECKING:
12
+ import uuid
13
+
14
+ from sqlalchemy.ext.asyncio import AsyncSession
15
+
16
+ from attocode_intel.api.auth.context import AuthContext
17
+ from attocode_intel.db.models import Repository
18
+
19
+ # Language detection by extension
20
+ LANG_MAP: dict[str, str] = {
21
+ ".py": "python", ".js": "javascript", ".ts": "typescript",
22
+ ".tsx": "typescriptreact", ".jsx": "javascriptreact",
23
+ ".rs": "rust", ".go": "go", ".java": "java", ".rb": "ruby",
24
+ ".c": "c", ".cpp": "cpp", ".h": "c", ".hpp": "cpp", ".metal": "cpp",
25
+ ".cs": "csharp", ".swift": "swift", ".kt": "kotlin",
26
+ ".sh": "shell", ".bash": "shell", ".zsh": "shell",
27
+ ".json": "json", ".yaml": "yaml", ".yml": "yaml",
28
+ ".toml": "toml", ".xml": "xml", ".html": "html", ".css": "css",
29
+ ".sql": "sql", ".md": "markdown", ".txt": "plaintext",
30
+ ".dockerfile": "dockerfile",
31
+ }
32
+
33
+ # Max file size for content retrieval (5 MB)
34
+ MAX_FILE_SIZE = 5 * 1024 * 1024
35
+
36
+
37
+ def detect_language(path: str) -> str:
38
+ """Detect language from file extension."""
39
+ _, ext = os.path.splitext(path)
40
+ ext = ext.lower()
41
+ if ext in LANG_MAP:
42
+ return LANG_MAP[ext]
43
+ basename = os.path.basename(path).lower()
44
+ if basename == "dockerfile":
45
+ return "dockerfile"
46
+ if basename == "makefile":
47
+ return "makefile"
48
+ return ""
49
+
50
+
51
+ def is_binary(data: bytes) -> bool:
52
+ """Heuristic binary detection: check for null bytes in first 8KB."""
53
+ return b"\x00" in data[:8192]
54
+
55
+
56
+ async def get_repo_by_id(
57
+ project_id: uuid.UUID,
58
+ session: AsyncSession,
59
+ auth: AuthContext | None = None,
60
+ ) -> Repository:
61
+ """Fetch a repository by ID, enforcing optional org isolation.
62
+
63
+ Returns 404 when the repo doesn't exist, or when ``auth.org_id`` is set
64
+ and does not match the repo's org.
65
+ """
66
+ from attocode_intel.db.models import Repository as _Repository
67
+
68
+ result = await session.execute(select(_Repository).where(_Repository.id == project_id))
69
+ repo = result.scalar_one_or_none()
70
+ if repo is None:
71
+ raise HTTPException(status_code=404, detail="Repository not found")
72
+ if auth and auth.org_id and repo.org_id != auth.org_id:
73
+ raise HTTPException(status_code=404, detail="Repository not found")
74
+ return repo
75
+
76
+
77
+ async def get_repo_by_org(
78
+ org_id: uuid.UUID,
79
+ repo_id: uuid.UUID,
80
+ session: AsyncSession,
81
+ ) -> Repository:
82
+ """Fetch a repository scoped to a specific org.
83
+
84
+ Returns 404 when the (org_id, repo_id) tuple doesn't match a repo.
85
+ """
86
+ from attocode_intel.db.models import Repository as _Repository
87
+
88
+ result = await session.execute(
89
+ select(_Repository).where(
90
+ _Repository.id == repo_id, _Repository.org_id == org_id,
91
+ )
92
+ )
93
+ repo = result.scalar_one_or_none()
94
+ if repo is None:
95
+ raise HTTPException(status_code=404, detail="Repository not found")
96
+ return repo
@@ -0,0 +1,45 @@
1
+ """Shared code-intel reproducibility primitives.
2
+
3
+ This package holds substrate shared by both the local stdio MCP side and the
4
+ server HTTP side:
5
+
6
+ - ``hashing``: canonical ``action_hash`` computation for derived artifacts.
7
+ - ``provenance``: ``Provenance`` dataclass recording how an artifact was produced.
8
+ - ``retrieval_pin``: ``RetrievalPin`` format + per-store manifest-hash helpers
9
+ used to make ranked-result tools deterministic.
10
+
11
+ Everything here is pure Python with stdlib-only imports so it can be imported
12
+ from either the local ``attocode.integrations.context.*`` modules or the
13
+ server ``attocode_intel.storage.*`` modules without pulling in SQL,
14
+ FastAPI, or FastMCP dependencies.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from .hashing import (
20
+ ARTIFACT_TYPES,
21
+ canonical_json,
22
+ compute_action_hash,
23
+ compute_config_digest,
24
+ sha256_file,
25
+ )
26
+ from .provenance import LEGACY_INDEXER_NAME, Provenance, legacy_provenance
27
+ from .retrieval_pin import (
28
+ RetrievalPin,
29
+ compute_store_hash,
30
+ make_pin_id,
31
+ )
32
+
33
+ __all__ = [
34
+ "ARTIFACT_TYPES",
35
+ "LEGACY_INDEXER_NAME",
36
+ "Provenance",
37
+ "RetrievalPin",
38
+ "canonical_json",
39
+ "compute_action_hash",
40
+ "compute_config_digest",
41
+ "compute_store_hash",
42
+ "legacy_provenance",
43
+ "make_pin_id",
44
+ "sha256_file",
45
+ ]
@@ -0,0 +1,230 @@
1
+ """Canonical action-hash computation for derived code-intel artifacts.
2
+
3
+ Given (artifact_type, indexer identity, input blob OID, config), produce a
4
+ deterministic SHA-256 that is byte-identical across machines, processes, and
5
+ the stdio-vs-HTTP split.
6
+
7
+ Any tool that caches or ships a derived artifact should key it by this hash.
8
+ The formula is frozen — changing it silently would invalidate every cached
9
+ artifact across the fleet, so callers should bump the ``v1`` suffix and write
10
+ a migration.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import hashlib
16
+ import json
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ if TYPE_CHECKING:
20
+ import os
21
+ from collections.abc import Mapping
22
+
23
+ # Version tag baked into every action_hash. Bump only in a coordinated
24
+ # migration that also rebuilds the caches that depend on it.
25
+ ACTION_HASH_SCHEMA = "atto.artifact.v1"
26
+
27
+ # Allowed artifact types. Keeping this closed lets us catch typos at the
28
+ # boundary and gives us a reason to fail loudly on unknown derived artifacts
29
+ # rather than silently producing "works but not shared" hashes.
30
+ ARTIFACT_TYPES: frozenset[str] = frozenset(
31
+ {
32
+ "symbols",
33
+ "references",
34
+ "embedding",
35
+ "ast",
36
+ "deps",
37
+ "repo_map_entry",
38
+ "stack_graph",
39
+ "conventions",
40
+ "learning_ref",
41
+ # Aliases for server-side content_type values:
42
+ "symbol_set_v1",
43
+ "dep_graph_v1",
44
+ "embedding_blob_v1",
45
+ "stack_graph_partial_v1",
46
+ "repo_map_slice_v1",
47
+ "conventions_v1",
48
+ }
49
+ )
50
+
51
+ # Whitelist of config keys that contribute to `config_digest` per artifact
52
+ # type. Anything not in the whitelist is intentionally *excluded* from the
53
+ # hash — it's meta (e.g. user preferences, display options) that doesn't
54
+ # affect the derived output.
55
+ CONFIG_SUBSET: Mapping[str, tuple[str, ...]] = {
56
+ "symbols": (
57
+ "language",
58
+ "parser_kind",
59
+ "parser_version",
60
+ "extract_signatures",
61
+ "extract_docstrings",
62
+ ),
63
+ "symbol_set_v1": (
64
+ "language",
65
+ "parser_kind",
66
+ "parser_version",
67
+ "extract_signatures",
68
+ "extract_docstrings",
69
+ ),
70
+ "references": (
71
+ "language",
72
+ "parser_kind",
73
+ "parser_version",
74
+ ),
75
+ "embedding": (
76
+ "model_name",
77
+ "model_version",
78
+ "dimension",
79
+ "normalize",
80
+ "chunker",
81
+ "chunk_size",
82
+ "chunk_overlap",
83
+ "preprocessor_version",
84
+ ),
85
+ "embedding_blob_v1": (
86
+ "model_name",
87
+ "model_version",
88
+ "dimension",
89
+ "normalize",
90
+ "chunker",
91
+ "chunk_size",
92
+ "chunk_overlap",
93
+ "preprocessor_version",
94
+ ),
95
+ "ast": (
96
+ "language",
97
+ "parser_kind",
98
+ "parser_version",
99
+ ),
100
+ "deps": (
101
+ "language",
102
+ "resolver_strategy",
103
+ "resolver_version",
104
+ "follow_dynamic_imports",
105
+ ),
106
+ "dep_graph_v1": (
107
+ "language",
108
+ "resolver_strategy",
109
+ "resolver_version",
110
+ "follow_dynamic_imports",
111
+ ),
112
+ "repo_map_entry": (
113
+ "indexer_version",
114
+ "slice_root",
115
+ "max_depth",
116
+ "ranker_version",
117
+ "max_tokens",
118
+ ),
119
+ "repo_map_slice_v1": (
120
+ "indexer_version",
121
+ "slice_root",
122
+ "max_depth",
123
+ "ranker_version",
124
+ "max_tokens",
125
+ ),
126
+ "stack_graph": (
127
+ "language",
128
+ "sg_grammar_version",
129
+ "rules_version",
130
+ ),
131
+ "stack_graph_partial_v1": (
132
+ "language",
133
+ "sg_grammar_version",
134
+ "rules_version",
135
+ ),
136
+ "conventions": (
137
+ "rule_pack_version",
138
+ ),
139
+ "conventions_v1": (
140
+ "rule_pack_version",
141
+ ),
142
+ "learning_ref": ("schema_version",),
143
+ }
144
+
145
+
146
+ def sha256_file(path: str | os.PathLike[str]) -> str:
147
+ """Compute SHA-256 hex digest of a file, reading in 64 KiB chunks."""
148
+ digest = hashlib.sha256()
149
+ with open(path, "rb") as fh:
150
+ for chunk in iter(lambda: fh.read(65536), b""):
151
+ digest.update(chunk)
152
+ return digest.hexdigest()
153
+
154
+
155
+ def canonical_json(obj: Any) -> bytes:
156
+ """Stable JSON encoding for hashing.
157
+
158
+ Sorts dict keys, disables non-ASCII escaping (so all bytes are stable
159
+ regardless of locale), no whitespace, no NaN/Infinity. The resulting
160
+ bytes are a canonical form — two processes on different machines
161
+ always produce the same output for equivalent Python objects.
162
+ """
163
+ return json.dumps(
164
+ obj,
165
+ sort_keys=True,
166
+ ensure_ascii=True,
167
+ separators=(",", ":"),
168
+ allow_nan=False,
169
+ ).encode("ascii")
170
+
171
+
172
+ def compute_config_digest(artifact_type: str, config: Mapping[str, Any]) -> str:
173
+ """Hash a config dict down to a hex digest, using only the whitelisted keys.
174
+
175
+ Unknown keys are *silently ignored* (not an error) because callers legitimately
176
+ pass richer config objects than the hash cares about. Keys in the whitelist
177
+ that are missing from ``config`` default to ``None`` — so adding a new
178
+ config knob is a non-breaking change only when it starts as ``None``.
179
+ """
180
+ if artifact_type not in ARTIFACT_TYPES:
181
+ raise ValueError(
182
+ f"unknown artifact_type {artifact_type!r}; allowed: {sorted(ARTIFACT_TYPES)}"
183
+ )
184
+ allowed = CONFIG_SUBSET.get(artifact_type, ())
185
+ subset = {key: config.get(key) for key in allowed}
186
+ return hashlib.sha256(canonical_json(subset)).hexdigest()
187
+
188
+
189
+ def compute_action_hash(
190
+ artifact_type: str,
191
+ *,
192
+ indexer_name: str,
193
+ indexer_version: str,
194
+ input_blob_oid: str,
195
+ config: Mapping[str, Any],
196
+ input_tree_oid: str | None = None,
197
+ ) -> str:
198
+ """Compute the canonical action hash for a derived artifact.
199
+
200
+ Formula::
201
+
202
+ sha256(
203
+ "atto.artifact.v1" || "\\n" ||
204
+ "type=" || artifact_type || "\\n" ||
205
+ "indexer=" || indexer_name || "\\n" ||
206
+ "iversion=" || indexer_version || "\\n" ||
207
+ "input=" || input_blob_oid || "\\n" ||
208
+ "tree=" || input_tree_oid || "\\n" ||
209
+ "config=" || config_digest || "\\n"
210
+ )
211
+
212
+ The delimiter is ``\\n`` and values are passed through unchanged — that's
213
+ fine because every field is either a short identifier or an already-hex
214
+ digest, so there is no delimiter ambiguity in practice.
215
+ """
216
+ if artifact_type not in ARTIFACT_TYPES:
217
+ raise ValueError(
218
+ f"unknown artifact_type {artifact_type!r}; allowed: {sorted(ARTIFACT_TYPES)}"
219
+ )
220
+ config_digest = compute_config_digest(artifact_type, config)
221
+ payload = (
222
+ f"{ACTION_HASH_SCHEMA}\n"
223
+ f"type={artifact_type}\n"
224
+ f"indexer={indexer_name}\n"
225
+ f"iversion={indexer_version}\n"
226
+ f"input={input_blob_oid}\n"
227
+ f"tree={input_tree_oid or ''}\n"
228
+ f"config={config_digest}\n"
229
+ )
230
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
@@ -0,0 +1,190 @@
1
+ """Provenance records describe how a derived code-intel artifact was produced.
2
+
3
+ Every symbol set, embedding, repo-map slice, etc. should carry a ``Provenance``
4
+ record so we can answer:
5
+
6
+ - Which model + version produced this embedding?
7
+ - Which indexer + version extracted these symbols?
8
+ - What blob (and optional tree) was the input?
9
+ - When, and on what host, was it produced?
10
+
11
+ This lets us detect staleness (indexer bumped, model swapped) and provides
12
+ the audit trail for retrieval determinism.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import socket
19
+ import time
20
+ from dataclasses import asdict, dataclass
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Mapping
25
+
26
+ # Sentinel indexer_name used by the one-shot migration that backfills
27
+ # provenance onto pre-existing rows. Searching for this string is an easy
28
+ # way to find everything that still needs a real indexer pass.
29
+ LEGACY_INDEXER_NAME = "legacy-pre-v2"
30
+
31
+ PROVENANCE_SCHEMA_VERSION = 1
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class Provenance:
36
+ """A record of how one derived artifact was produced.
37
+
38
+ Frozen + slotted so it's cheap to build many of these during an index
39
+ run and so accidental mutation is a TypeError.
40
+ """
41
+
42
+ schema_version: int
43
+ artifact_type: str
44
+ action_hash: str
45
+ input_blob_oid: str
46
+ indexer_name: str
47
+ indexer_version: str
48
+ producer_host: str
49
+ produced_at: float
50
+ # Embeddings-specific; None for other artifact types.
51
+ model_name: str | None = None
52
+ model_version: str | None = None
53
+ dimension: int | None = None
54
+ preprocessing_digest: str | None = None
55
+ # Optional contextual IDs — server-side sets these, local side leaves None.
56
+ producer_service: str = "attocode-local"
57
+ producer_user_id: str | None = None
58
+ producer_job_id: str | None = None
59
+ input_tree_oid: str | None = None
60
+ # Original config subset that went into the action_hash, as canonical JSON
61
+ # text — kept as a string so the dataclass stays hashable/frozen.
62
+ config_json: str = "{}"
63
+
64
+ # ------------------------------------------------------------------
65
+ # Constructors
66
+ # ------------------------------------------------------------------
67
+
68
+ @classmethod
69
+ def create(
70
+ cls,
71
+ *,
72
+ artifact_type: str,
73
+ action_hash: str,
74
+ input_blob_oid: str,
75
+ indexer_name: str,
76
+ indexer_version: str,
77
+ config: Mapping[str, Any] | None = None,
78
+ model_name: str | None = None,
79
+ model_version: str | None = None,
80
+ dimension: int | None = None,
81
+ preprocessing_digest: str | None = None,
82
+ producer_service: str = "attocode-local",
83
+ producer_user_id: str | None = None,
84
+ producer_job_id: str | None = None,
85
+ input_tree_oid: str | None = None,
86
+ ) -> Provenance:
87
+ """Build a fresh provenance record with ``produced_at`` set to now."""
88
+ config_json = json.dumps(
89
+ dict(config or {}),
90
+ sort_keys=True,
91
+ ensure_ascii=True,
92
+ separators=(",", ":"),
93
+ )
94
+ return cls(
95
+ schema_version=PROVENANCE_SCHEMA_VERSION,
96
+ artifact_type=artifact_type,
97
+ action_hash=action_hash,
98
+ input_blob_oid=input_blob_oid,
99
+ indexer_name=indexer_name,
100
+ indexer_version=indexer_version,
101
+ producer_host=socket.gethostname(),
102
+ produced_at=time.time(),
103
+ model_name=model_name,
104
+ model_version=model_version,
105
+ dimension=dimension,
106
+ preprocessing_digest=preprocessing_digest,
107
+ producer_service=producer_service,
108
+ producer_user_id=producer_user_id,
109
+ producer_job_id=producer_job_id,
110
+ input_tree_oid=input_tree_oid,
111
+ config_json=config_json,
112
+ )
113
+
114
+ # ------------------------------------------------------------------
115
+ # Serialization
116
+ # ------------------------------------------------------------------
117
+
118
+ def to_dict(self) -> dict[str, Any]:
119
+ """Return a plain dict suitable for JSON / JSONB storage."""
120
+ return asdict(self)
121
+
122
+ def to_json(self) -> str:
123
+ """Canonical JSON text — stable for hashing."""
124
+ return json.dumps(
125
+ self.to_dict(),
126
+ sort_keys=True,
127
+ ensure_ascii=True,
128
+ separators=(",", ":"),
129
+ )
130
+
131
+ @classmethod
132
+ def from_dict(cls, data: Mapping[str, Any]) -> Provenance:
133
+ """Rebuild a Provenance from its dict form (tolerant of missing fields)."""
134
+ # Copy known fields; unknown keys are silently dropped. Missing
135
+ # required fields get sensible defaults matching a "legacy" marker
136
+ # so we can still roundtrip rows that were inserted with a very old
137
+ # schema.
138
+ return cls(
139
+ schema_version=int(data.get("schema_version", PROVENANCE_SCHEMA_VERSION)),
140
+ artifact_type=str(data.get("artifact_type", "unknown")),
141
+ action_hash=str(data.get("action_hash", "")),
142
+ input_blob_oid=str(data.get("input_blob_oid", "")),
143
+ indexer_name=str(data.get("indexer_name", LEGACY_INDEXER_NAME)),
144
+ indexer_version=str(data.get("indexer_version", "0")),
145
+ producer_host=str(data.get("producer_host", "unknown")),
146
+ produced_at=float(data.get("produced_at", 0.0)),
147
+ model_name=data.get("model_name"),
148
+ model_version=data.get("model_version"),
149
+ dimension=data.get("dimension"),
150
+ preprocessing_digest=data.get("preprocessing_digest"),
151
+ producer_service=str(data.get("producer_service", "attocode-local")),
152
+ producer_user_id=data.get("producer_user_id"),
153
+ producer_job_id=data.get("producer_job_id"),
154
+ input_tree_oid=data.get("input_tree_oid"),
155
+ config_json=str(data.get("config_json", "{}")),
156
+ )
157
+
158
+
159
+ def legacy_provenance(
160
+ artifact_type: str,
161
+ *,
162
+ input_blob_oid: str = "",
163
+ produced_at: float | None = None,
164
+ action_hash: str = "",
165
+ ) -> Provenance:
166
+ """Build a sentinel provenance record for the one-shot backfill.
167
+
168
+ Use this when you have existing rows in a store that pre-date provenance
169
+ tracking. Downstream tools can filter on ``indexer_name == LEGACY_INDEXER_NAME``
170
+ to find rows that still need a real re-index pass.
171
+ """
172
+ return Provenance(
173
+ schema_version=PROVENANCE_SCHEMA_VERSION,
174
+ artifact_type=artifact_type,
175
+ action_hash=action_hash,
176
+ input_blob_oid=input_blob_oid,
177
+ indexer_name=LEGACY_INDEXER_NAME,
178
+ indexer_version="0",
179
+ producer_host=socket.gethostname(),
180
+ produced_at=produced_at if produced_at is not None else time.time(),
181
+ model_name=None,
182
+ model_version=None,
183
+ dimension=None,
184
+ preprocessing_digest=None,
185
+ producer_service="attocode-backfill",
186
+ producer_user_id=None,
187
+ producer_job_id=None,
188
+ input_tree_oid=None,
189
+ config_json="{}",
190
+ )