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,357 @@
1
+ """Recursive context retrieval technique.
2
+
3
+ Implements a strategy where the agent progressively deepens context
4
+ by following references found in initial results. Useful for understanding
5
+ complex code flows that span multiple files.
6
+
7
+ Strategy:
8
+ 1. Start with a seed query (file path, symbol, etc.)
9
+ 2. Retrieve initial context
10
+ 3. Extract references/imports from the context
11
+ 4. Recursively retrieve context for those references
12
+ 5. Stop at a depth/token budget limit
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import re
18
+ from collections import deque
19
+ from dataclasses import dataclass, field
20
+ from typing import Protocol
21
+
22
+ from attocode_intel._internal.integrations.utilities.token_estimate import estimate_tokens as _estimate_tokens
23
+
24
+
25
+ class ContentProvider(Protocol):
26
+ """Protocol for retrieving file content."""
27
+
28
+ def get_content(self, file_path: str) -> str | None: ...
29
+
30
+
31
+ @dataclass(slots=True)
32
+ class ContextNode:
33
+ """A node in the recursive context tree."""
34
+
35
+ file_path: str
36
+ content: str
37
+ depth: int
38
+ references: list[str] = field(default_factory=list)
39
+ estimated_tokens: int = 0
40
+ is_truncated: bool = False
41
+
42
+ def __post_init__(self) -> None:
43
+ if self.estimated_tokens == 0 and self.content:
44
+ self.estimated_tokens = _estimate_tokens(self.content)
45
+
46
+
47
+ @dataclass(slots=True)
48
+ class RecursiveContextResult:
49
+ """Result of recursive context retrieval."""
50
+
51
+ nodes: list[ContextNode]
52
+ total_tokens: int
53
+ max_depth_reached: int
54
+ files_visited: int
55
+ budget_exhausted: bool = False
56
+
57
+ @property
58
+ def content(self) -> str:
59
+ """Combined content from all nodes."""
60
+ parts: list[str] = []
61
+ for node in self.nodes:
62
+ header = f"--- {node.file_path} (depth {node.depth}) ---"
63
+ parts.append(f"{header}\n{node.content}")
64
+ return "\n\n".join(parts)
65
+
66
+
67
+ def extract_python_references(content: str, current_file: str = "") -> list[str]:
68
+ """Extract file references from Python import statements.
69
+
70
+ Returns a list of potential file paths derived from imports.
71
+ """
72
+ references: list[str] = []
73
+
74
+ # from X import Y
75
+ for match in re.finditer(r"from\s+([\w.]+)\s+import", content):
76
+ module = match.group(1)
77
+ # Convert module path to file path
78
+ path = module.replace(".", "/") + ".py"
79
+ references.append(path)
80
+
81
+ # import X or import X, Y, Z
82
+ for match in re.finditer(r"^import\s+([\w.,\s]+)", content, re.MULTILINE):
83
+ modules_str = match.group(1)
84
+ for module in modules_str.split(","):
85
+ module = module.strip()
86
+ if module and re.fullmatch(r"[\w.]+", module):
87
+ path = module.replace(".", "/") + ".py"
88
+ references.append(path)
89
+
90
+ return references
91
+
92
+
93
+ def extract_js_references(content: str, current_file: str = "") -> list[str]:
94
+ """Extract file references from JS/TS import statements.
95
+
96
+ Returns a list of potential file paths.
97
+ """
98
+ references: list[str] = []
99
+
100
+ # import ... from 'path'
101
+ for match in re.finditer(r"""from\s+['"]([^'"]+)['"]""", content):
102
+ ref = match.group(1)
103
+ if ref.startswith("."):
104
+ # Relative import
105
+ if not ref.endswith((".js", ".ts", ".tsx", ".jsx")):
106
+ references.append(ref + ".ts")
107
+ references.append(ref + ".js")
108
+ else:
109
+ references.append(ref)
110
+
111
+ # export { X } from './y' (barrel re-exports)
112
+ for match in re.finditer(r"""export\s+\{[^}]*\}\s+from\s+['"]([^'"]+)['"]""", content):
113
+ ref = match.group(1)
114
+ if ref.startswith("."):
115
+ if not ref.endswith((".js", ".ts", ".tsx", ".jsx")):
116
+ references.append(ref + ".ts")
117
+ references.append(ref + ".js")
118
+ else:
119
+ references.append(ref)
120
+
121
+ # require('path')
122
+ for match in re.finditer(r"""require\s*\(\s*['"]([^'"]+)['"]\s*\)""", content):
123
+ ref = match.group(1)
124
+ if ref.startswith("."):
125
+ references.append(ref + ".js")
126
+
127
+ return references
128
+
129
+
130
+ def extract_references(content: str, file_path: str) -> list[str]:
131
+ """Extract file references based on file type."""
132
+ if file_path.endswith(".py"):
133
+ return extract_python_references(content, file_path)
134
+ elif file_path.endswith((".js", ".ts", ".tsx", ".jsx")):
135
+ return extract_js_references(content, file_path)
136
+ return []
137
+
138
+
139
+ class RecursiveContextRetriever:
140
+ """Retrieves context by recursively following references.
141
+
142
+ Starting from a seed file, follows imports and references
143
+ up to a configurable depth and token budget.
144
+
145
+ Args:
146
+ provider: Content provider for reading files.
147
+ max_depth: Maximum recursion depth (default 3).
148
+ token_budget: Maximum total tokens across all files.
149
+ max_files: Maximum number of files to visit.
150
+ """
151
+
152
+ def __init__(
153
+ self,
154
+ provider: ContentProvider,
155
+ *,
156
+ max_depth: int = 3,
157
+ token_budget: int = 50_000,
158
+ max_files: int = 20,
159
+ ) -> None:
160
+ self._provider = provider
161
+ self._max_depth = max_depth
162
+ self._token_budget = token_budget
163
+ self._max_files = max_files
164
+
165
+ def retrieve(
166
+ self,
167
+ seed_files: list[str],
168
+ *,
169
+ max_content_per_file: int = 10_000,
170
+ ) -> RecursiveContextResult:
171
+ """Recursively retrieve context starting from seed files.
172
+
173
+ Args:
174
+ seed_files: Initial files to start from.
175
+ max_content_per_file: Maximum characters per file content.
176
+
177
+ Returns:
178
+ A :class:`RecursiveContextResult` with all gathered context.
179
+ """
180
+ visited: set[str] = set()
181
+ nodes: list[ContextNode] = []
182
+ total_tokens = 0
183
+ max_depth_reached = 0
184
+
185
+ # BFS queue: (file_path, depth)
186
+ queue: deque[tuple[str, int]] = deque((f, 0) for f in seed_files)
187
+
188
+ while queue:
189
+ file_path, depth = queue.popleft()
190
+
191
+ # Skip if already visited
192
+ if file_path in visited:
193
+ continue
194
+
195
+ # Check limits
196
+ if depth > self._max_depth:
197
+ continue
198
+ if len(visited) >= self._max_files:
199
+ break
200
+ if total_tokens >= self._token_budget:
201
+ break
202
+
203
+ visited.add(file_path)
204
+
205
+ content = self._provider.get_content(file_path)
206
+ if content is None:
207
+ continue
208
+
209
+ is_truncated = False
210
+ if len(content) > max_content_per_file:
211
+ content = content[:max_content_per_file]
212
+ is_truncated = True
213
+
214
+ # Check if adding this would exceed budget
215
+ est_tokens = _estimate_tokens(content)
216
+ if total_tokens + est_tokens > self._token_budget:
217
+ # Try to fit with truncation
218
+ remaining = self._token_budget - total_tokens
219
+ chars = remaining * 4
220
+ if chars < 200:
221
+ break
222
+ content = content[:chars]
223
+ is_truncated = True
224
+ est_tokens = _estimate_tokens(content)
225
+
226
+ # Extract references for next depth
227
+ references = extract_references(content, file_path)
228
+
229
+ node = ContextNode(
230
+ file_path=file_path,
231
+ content=content,
232
+ depth=depth,
233
+ references=references,
234
+ estimated_tokens=est_tokens,
235
+ is_truncated=is_truncated,
236
+ )
237
+ nodes.append(node)
238
+ total_tokens += est_tokens
239
+ max_depth_reached = max(max_depth_reached, depth)
240
+
241
+ # Enqueue references for next depth
242
+ for ref in references:
243
+ if ref not in visited:
244
+ queue.append((ref, depth + 1))
245
+
246
+ return RecursiveContextResult(
247
+ nodes=nodes,
248
+ total_tokens=total_tokens,
249
+ max_depth_reached=max_depth_reached,
250
+ files_visited=len(visited),
251
+ budget_exhausted=total_tokens >= self._token_budget,
252
+ )
253
+
254
+ def retrieve_with_priority(
255
+ self,
256
+ seed_files: list[str],
257
+ priority_files: list[str] | None = None,
258
+ *,
259
+ max_content_per_file: int = 10_000,
260
+ ) -> RecursiveContextResult:
261
+ """Retrieve with priority files getting budget preference.
262
+
263
+ Priority files are loaded first and given more token budget.
264
+ Remaining budget is used for recursive exploration.
265
+ """
266
+ _priority = set(priority_files or [])
267
+ visited: set[str] = set()
268
+ nodes: list[ContextNode] = []
269
+ total_tokens = 0
270
+ max_depth_reached = 0
271
+
272
+ # Phase 1: Load priority files first (use 50% of budget)
273
+ priority_budget = self._token_budget // 2
274
+ for file_path in (priority_files or []):
275
+ if file_path in visited:
276
+ continue
277
+ content = self._provider.get_content(file_path)
278
+ if content is None:
279
+ continue
280
+ visited.add(file_path)
281
+ is_truncated = False
282
+ if len(content) > max_content_per_file:
283
+ content = content[:max_content_per_file]
284
+ is_truncated = True
285
+ est_tokens = _estimate_tokens(content)
286
+ if total_tokens + est_tokens > priority_budget:
287
+ remaining = priority_budget - total_tokens
288
+ chars = remaining * 4
289
+ if chars < 200:
290
+ break
291
+ content = content[:chars]
292
+ is_truncated = True
293
+ est_tokens = _estimate_tokens(content)
294
+ references = extract_references(content, file_path)
295
+ nodes.append(ContextNode(
296
+ file_path=file_path,
297
+ content=content,
298
+ depth=0,
299
+ references=references,
300
+ estimated_tokens=est_tokens,
301
+ is_truncated=is_truncated,
302
+ ))
303
+ total_tokens += est_tokens
304
+
305
+ # Phase 2: BFS from seeds with remaining budget
306
+ queue: deque[tuple[str, int]] = deque(
307
+ (f, 0) for f in seed_files if f not in visited
308
+ )
309
+ # Also enqueue references from priority files
310
+ for node in nodes:
311
+ for ref in node.references:
312
+ if ref not in visited:
313
+ queue.append((ref, 1))
314
+
315
+ while queue and total_tokens < self._token_budget and len(visited) < self._max_files:
316
+ file_path, depth = queue.popleft()
317
+ if file_path in visited or depth > self._max_depth:
318
+ continue
319
+ visited.add(file_path)
320
+ content = self._provider.get_content(file_path)
321
+ if content is None:
322
+ continue
323
+ is_truncated = False
324
+ if len(content) > max_content_per_file:
325
+ content = content[:max_content_per_file]
326
+ is_truncated = True
327
+ est_tokens = _estimate_tokens(content)
328
+ if total_tokens + est_tokens > self._token_budget:
329
+ remaining = self._token_budget - total_tokens
330
+ chars = remaining * 4
331
+ if chars < 200:
332
+ break
333
+ content = content[:chars]
334
+ is_truncated = True
335
+ est_tokens = _estimate_tokens(content)
336
+ references = extract_references(content, file_path)
337
+ nodes.append(ContextNode(
338
+ file_path=file_path,
339
+ content=content,
340
+ depth=depth,
341
+ references=references,
342
+ estimated_tokens=est_tokens,
343
+ is_truncated=is_truncated,
344
+ ))
345
+ total_tokens += est_tokens
346
+ max_depth_reached = max(max_depth_reached, depth)
347
+ for ref in references:
348
+ if ref not in visited:
349
+ queue.append((ref, depth + 1))
350
+
351
+ return RecursiveContextResult(
352
+ nodes=nodes,
353
+ total_tokens=total_tokens,
354
+ max_depth_reached=max_depth_reached,
355
+ files_visited=len(visited),
356
+ budget_exhausted=total_tokens >= self._token_budget,
357
+ )
@@ -0,0 +1 @@
1
+ """Internal code intelligence support; not a public API."""
@@ -0,0 +1,289 @@
1
+ """Agent event types.
2
+
3
+ Comprehensive event system covering lifecycle, execution, multi-agent,
4
+ policy, context, resilience, insight, and subagent categories.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable
10
+ from dataclasses import dataclass
11
+ from enum import StrEnum
12
+ from typing import Any
13
+
14
+ # Shared callback type for simple event listeners used across the codebase.
15
+ SimpleEventListener = Callable[[str, dict[str, Any]], None]
16
+
17
+
18
+ class SimpleEventEmitter:
19
+ """Mixin: subscribe/emit ``(event, data)`` events. Listener errors are swallowed."""
20
+
21
+ _listeners: list[SimpleEventListener]
22
+
23
+ def __init__(self) -> None:
24
+ self._listeners = []
25
+
26
+ def on(self, listener: SimpleEventListener) -> Callable[[], None]:
27
+ self._listeners.append(listener)
28
+
29
+ def unsubscribe() -> None:
30
+ if listener in self._listeners:
31
+ self._listeners.remove(listener)
32
+
33
+ return unsubscribe
34
+
35
+ def _emit(self, event: str, data: dict[str, Any]) -> None:
36
+ for listener in self._listeners:
37
+ try:
38
+ listener(event, data)
39
+ except Exception:
40
+ pass
41
+
42
+
43
+ class EventType(StrEnum):
44
+ """Types of agent events.
45
+
46
+ Organized into categories:
47
+ - Lifecycle: start, complete, error, shutdown
48
+ - Execution: iteration, tool.*, llm.*
49
+ - Budget: budget.*
50
+ - Context: compaction.*, context.*
51
+ - Multi-agent: subagent.*, swarm.*
52
+ - Policy: policy.*, permission.*
53
+ - Session: session.*
54
+ - Mode: mode.*
55
+ - Plan: plan.*
56
+ - Quality: quality.*, learning.*
57
+ - Resilience: resilience.*, circuit_breaker.*
58
+ - MCP: mcp.*
59
+ - Undo: undo.*
60
+ - Insight: insight.*
61
+ """
62
+
63
+ # --- Lifecycle ---
64
+ START = "start"
65
+ COMPLETE = "complete"
66
+ ERROR = "error"
67
+ SHUTDOWN = "shutdown"
68
+ STATUS = "status" # General status update (e.g. "Indexing codebase...")
69
+
70
+ # --- Execution core ---
71
+ ITERATION = "iteration"
72
+ THINKING = "thinking"
73
+ RESPONSE = "response"
74
+
75
+ # --- Tool events ---
76
+ TOOL_START = "tool.start"
77
+ TOOL_COMPLETE = "tool.complete"
78
+ TOOL_ERROR = "tool.error"
79
+ TOOL_APPROVAL_REQUESTED = "tool.approval.requested"
80
+ TOOL_APPROVAL_GRANTED = "tool.approval.granted"
81
+ TOOL_APPROVAL_DENIED = "tool.approval.denied"
82
+ TOOL_BLOCKED = "tool.blocked"
83
+ TOOL_COERCED = "tool.coerced"
84
+ TOOL_CALL_MISMATCH = "tool.call.mismatch"
85
+ SUSPICIOUS_TOOL_MARKUP = "tool.markup.suspicious"
86
+ LOOP_GUARD_ACTIVATED = "tool.loop_guard.activated"
87
+
88
+ # --- LLM events ---
89
+ LLM_START = "llm.start"
90
+ LLM_COMPLETE = "llm.complete"
91
+ LLM_ERROR = "llm.error"
92
+ LLM_RETRY = "llm.retry"
93
+ LLM_STREAM_START = "llm.stream.start"
94
+ LLM_STREAM_CHUNK = "llm.stream.chunk"
95
+ LLM_STREAM_END = "llm.stream.end"
96
+
97
+ # --- Budget events ---
98
+ BUDGET_WARNING = "budget.warning"
99
+ BUDGET_EXHAUSTED = "budget.exhausted"
100
+ BUDGET_CHECK = "budget.check"
101
+ BUDGET_EXTENSION_REQUESTED = "budget.extension.requested"
102
+ BUDGET_EXTENSION_GRANTED = "budget.extension.granted"
103
+ BUDGET_EXTENSION_DENIED = "budget.extension.denied"
104
+
105
+ # --- Context / Compaction events ---
106
+ COMPACTION = "compaction"
107
+ COMPACTION_START = "compaction.start"
108
+ COMPACTION_COMPLETE = "compaction.complete"
109
+ COMPACTION_ERROR = "compaction.error"
110
+ CONTEXT_FRESH_REFRESH = "context.fresh_refresh"
111
+ CONTEXT_OVERFLOW = "context.overflow"
112
+ CONTEXT_BREAKDOWN = "context.breakdown"
113
+
114
+ # --- Session events ---
115
+ SESSION_CREATED = "session.created"
116
+ SESSION_LOADED = "session.loaded"
117
+ SESSION_SAVED = "session.saved"
118
+ SESSION_RESUMED = "session.resumed"
119
+ SESSION_RESUME_REJECTED = "session.resume.rejected"
120
+ SESSION_FORKED = "session.forked"
121
+ SESSION_CHECKPOINT = "session.checkpoint"
122
+
123
+ # --- Mode events ---
124
+ MODE_CHANGED = "mode.changed"
125
+ MODE_WRITE_INTERCEPTED = "mode.write.intercepted"
126
+
127
+ # --- Plan events ---
128
+ PLAN_CREATED = "plan.created"
129
+ PLAN_STEP_START = "plan.step.start"
130
+ PLAN_STEP_COMPLETE = "plan.step.complete"
131
+ PLAN_APPROVED = "plan.approved"
132
+ PLAN_REJECTED = "plan.rejected"
133
+ PLAN_UPDATED = "plan.updated"
134
+
135
+ # --- Subagent events ---
136
+ SUBAGENT_SPAWN = "subagent.spawn"
137
+ SUBAGENT_COMPLETE = "subagent.complete"
138
+ SUBAGENT_ERROR = "subagent.error"
139
+ SUBAGENT_ITERATION = "subagent.iteration"
140
+ SUBAGENT_PHASE = "subagent.phase"
141
+ SUBAGENT_WRAPUP_STARTED = "subagent.wrapup.started"
142
+ SUBAGENT_WRAPUP_COMPLETED = "subagent.wrapup.completed"
143
+ SUBAGENT_TIMEOUT_HARD_KILL = "subagent.timeout.hard_kill"
144
+
145
+ # --- Swarm events ---
146
+ SWARM_START = "swarm.start"
147
+ SWARM_COMPLETE = "swarm.complete"
148
+ SWARM_TASK_QUEUED = "swarm.task.queued"
149
+ SWARM_TASK_START = "swarm.task.start"
150
+ SWARM_TASK_COMPLETE = "swarm.task.complete"
151
+ SWARM_TASK_FAILED = "swarm.task.failed"
152
+ SWARM_WAVE_START = "swarm.wave.start"
153
+ SWARM_WAVE_COMPLETE = "swarm.wave.complete"
154
+ SWARM_WORKER_ASSIGNED = "swarm.worker.assigned"
155
+ SWARM_WORKER_RELEASED = "swarm.worker.released"
156
+
157
+ # --- Policy events ---
158
+ POLICY_EVALUATION = "policy.evaluation"
159
+ POLICY_OVERRIDE = "policy.override"
160
+ PERMISSION_GRANTED = "permission.granted"
161
+ PERMISSION_DENIED = "permission.denied"
162
+ PERMISSION_REMEMBERED = "permission.remembered"
163
+
164
+ # --- Quality events ---
165
+ QUALITY_CHECK = "quality.check"
166
+ QUALITY_SCORE = "quality.score"
167
+ LEARNING_PROPOSED = "learning.proposed"
168
+ LEARNING_APPROVED = "learning.approved"
169
+ LEARNING_REJECTED = "learning.rejected"
170
+ HEALTH_CHECK = "health.check"
171
+
172
+ # --- Resilience events ---
173
+ RESILIENCE_RETRY = "resilience.retry"
174
+ RESILIENCE_FALLBACK = "resilience.fallback"
175
+ CIRCUIT_BREAKER_OPEN = "circuit_breaker.open"
176
+ CIRCUIT_BREAKER_HALF_OPEN = "circuit_breaker.half_open"
177
+ CIRCUIT_BREAKER_CLOSED = "circuit_breaker.closed"
178
+
179
+ # --- MCP events ---
180
+ MCP_CONNECT = "mcp.connect"
181
+ MCP_DISCONNECT = "mcp.disconnect"
182
+ MCP_TOOL_DISCOVERED = "mcp.tool.discovered"
183
+ MCP_TOOL_CALL = "mcp.tool.call"
184
+ MCP_TOOL_ERROR = "mcp.tool.error"
185
+
186
+ # --- Undo events ---
187
+ UNDO_TRACKED = "undo.tracked"
188
+ UNDO_EXECUTED = "undo.executed"
189
+ UNDO_FAILED = "undo.failed"
190
+
191
+ # --- Insight / Loop detection ---
192
+ INSIGHT_DOOM_LOOP = "insight.doom_loop"
193
+ INSIGHT_SATURATION = "insight.saturation"
194
+ INSIGHT_NUDGE = "insight.nudge"
195
+ INSIGHT_PHASE_CHANGE = "insight.phase_change"
196
+
197
+
198
+ class EventCategory(StrEnum):
199
+ """Categories for event grouping."""
200
+
201
+ LIFECYCLE = "lifecycle"
202
+ EXECUTION = "execution"
203
+ TOOL = "tool"
204
+ LLM = "llm"
205
+ BUDGET = "budget"
206
+ CONTEXT = "context"
207
+ SESSION = "session"
208
+ MODE = "mode"
209
+ PLAN = "plan"
210
+ SUBAGENT = "subagent"
211
+ SWARM = "swarm"
212
+ POLICY = "policy"
213
+ QUALITY = "quality"
214
+ RESILIENCE = "resilience"
215
+ MCP = "mcp"
216
+ UNDO = "undo"
217
+ INSIGHT = "insight"
218
+
219
+
220
+ # Map events to categories
221
+ EVENT_CATEGORIES: dict[EventType, EventCategory] = {}
222
+ for _evt in EventType:
223
+ _name = _evt.value
224
+ if _name in ("start", "complete", "error", "shutdown"):
225
+ EVENT_CATEGORIES[_evt] = EventCategory.LIFECYCLE
226
+ elif _name.startswith("tool."):
227
+ EVENT_CATEGORIES[_evt] = EventCategory.TOOL
228
+ elif _name.startswith("llm."):
229
+ EVENT_CATEGORIES[_evt] = EventCategory.LLM
230
+ elif _name.startswith("budget."):
231
+ EVENT_CATEGORIES[_evt] = EventCategory.BUDGET
232
+ elif _name.startswith(("compaction", "context.")):
233
+ EVENT_CATEGORIES[_evt] = EventCategory.CONTEXT
234
+ elif _name.startswith("session."):
235
+ EVENT_CATEGORIES[_evt] = EventCategory.SESSION
236
+ elif _name.startswith("mode."):
237
+ EVENT_CATEGORIES[_evt] = EventCategory.MODE
238
+ elif _name.startswith("plan."):
239
+ EVENT_CATEGORIES[_evt] = EventCategory.PLAN
240
+ elif _name.startswith("subagent."):
241
+ EVENT_CATEGORIES[_evt] = EventCategory.SUBAGENT
242
+ elif _name.startswith("swarm."):
243
+ EVENT_CATEGORIES[_evt] = EventCategory.SWARM
244
+ elif _name.startswith(("policy.", "permission.")):
245
+ EVENT_CATEGORIES[_evt] = EventCategory.POLICY
246
+ elif _name.startswith(("quality.", "learning.", "health.")):
247
+ EVENT_CATEGORIES[_evt] = EventCategory.QUALITY
248
+ elif _name.startswith(("resilience.", "circuit_breaker.")):
249
+ EVENT_CATEGORIES[_evt] = EventCategory.RESILIENCE
250
+ elif _name.startswith("mcp."):
251
+ EVENT_CATEGORIES[_evt] = EventCategory.MCP
252
+ elif _name.startswith("undo."):
253
+ EVENT_CATEGORIES[_evt] = EventCategory.UNDO
254
+ elif _name.startswith("insight."):
255
+ EVENT_CATEGORIES[_evt] = EventCategory.INSIGHT
256
+ else:
257
+ EVENT_CATEGORIES[_evt] = EventCategory.EXECUTION
258
+
259
+
260
+ def get_event_category(event_type: EventType) -> EventCategory:
261
+ """Get the category for an event type."""
262
+ return EVENT_CATEGORIES.get(event_type, EventCategory.EXECUTION)
263
+
264
+
265
+ @dataclass(slots=True)
266
+ class AgentEvent:
267
+ """An event emitted during agent execution."""
268
+
269
+ type: EventType
270
+ task: str | None = None
271
+ tool: str | None = None
272
+ args: dict[str, Any] | None = None
273
+ result: str | None = None
274
+ error: str | None = None
275
+ tokens: int | None = None
276
+ cost: float | None = None
277
+ iteration: int | None = None
278
+ metadata: dict[str, Any] | None = None
279
+ # Extended fields for rich events
280
+ session_id: str | None = None
281
+ agent_id: str | None = None
282
+ parent_id: str | None = None
283
+ duration_ms: float | None = None
284
+ timestamp: float | None = None
285
+
286
+ @property
287
+ def category(self) -> EventCategory:
288
+ """Get the category of this event."""
289
+ return get_event_category(self.type)
@@ -0,0 +1,15 @@
1
+ """Shared severity levels for findings, security patterns, etc."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import StrEnum
6
+
7
+
8
+ class Severity(StrEnum):
9
+ """Finding severity level."""
10
+
11
+ CRITICAL = "critical"
12
+ HIGH = "high"
13
+ MEDIUM = "medium"
14
+ LOW = "low"
15
+ INFO = "info"