@jaguilar87/gaia 5.0.0-rc1

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 (609) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +26 -0
  3. package/ARCHITECTURE.md +335 -0
  4. package/CHANGELOG.md +1212 -0
  5. package/CODE_OF_CONDUCT.md +11 -0
  6. package/CONTRIBUTING.md +146 -0
  7. package/INSTALL.md +436 -0
  8. package/LICENSE +21 -0
  9. package/README.md +222 -0
  10. package/SECURITY.md +47 -0
  11. package/agents/README.md +78 -0
  12. package/agents/cloud-troubleshooter.md +73 -0
  13. package/agents/developer.md +65 -0
  14. package/agents/gaia-operator.md +64 -0
  15. package/agents/gaia-orchestrator.md +237 -0
  16. package/agents/gaia-planner.md +53 -0
  17. package/agents/gaia-system.md +70 -0
  18. package/agents/gitops-operator.md +61 -0
  19. package/agents/terraform-architect.md +63 -0
  20. package/bin/README.md +106 -0
  21. package/bin/cli/__init__.py +1 -0
  22. package/bin/cli/approvals.py +740 -0
  23. package/bin/cli/cleanup.py +562 -0
  24. package/bin/cli/context.py +283 -0
  25. package/bin/cli/doctor.py +628 -0
  26. package/bin/cli/history.py +305 -0
  27. package/bin/cli/memory.py +464 -0
  28. package/bin/cli/metrics.py +1068 -0
  29. package/bin/cli/plans.py +515 -0
  30. package/bin/cli/status.py +302 -0
  31. package/bin/cli/update.py +382 -0
  32. package/bin/gaia +112 -0
  33. package/bin/gaia-cleanup.js +531 -0
  34. package/bin/gaia-doctor.js +635 -0
  35. package/bin/gaia-evidence +126 -0
  36. package/bin/gaia-history.js +251 -0
  37. package/bin/gaia-metrics.js +1278 -0
  38. package/bin/gaia-review.js +269 -0
  39. package/bin/gaia-scan +44 -0
  40. package/bin/gaia-scan.py +589 -0
  41. package/bin/gaia-skills-diagnose.js +929 -0
  42. package/bin/gaia-status.js +278 -0
  43. package/bin/gaia-uninstall.js +111 -0
  44. package/bin/gaia-update.js +816 -0
  45. package/bin/pre-publish-validate.js +610 -0
  46. package/bin/python-detect.js +60 -0
  47. package/commands/README.md +64 -0
  48. package/commands/gaia.md +37 -0
  49. package/commands/scan-project.md +67 -0
  50. package/config/README.md +71 -0
  51. package/config/cloud/aws.json +134 -0
  52. package/config/cloud/gcp.json +139 -0
  53. package/config/context-contracts.json +158 -0
  54. package/config/crons-schema.md +81 -0
  55. package/config/git_standards.json +72 -0
  56. package/config/surface-routing.json +421 -0
  57. package/config/universal-rules.json +102 -0
  58. package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
  59. package/dist/gaia-ops/README.md +80 -0
  60. package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
  61. package/dist/gaia-ops/agents/developer.md +65 -0
  62. package/dist/gaia-ops/agents/gaia-operator.md +64 -0
  63. package/dist/gaia-ops/agents/gaia-orchestrator.md +237 -0
  64. package/dist/gaia-ops/agents/gaia-planner.md +53 -0
  65. package/dist/gaia-ops/agents/gaia-system.md +70 -0
  66. package/dist/gaia-ops/agents/gitops-operator.md +61 -0
  67. package/dist/gaia-ops/agents/terraform-architect.md +63 -0
  68. package/dist/gaia-ops/commands/gaia.md +37 -0
  69. package/dist/gaia-ops/config/README.md +71 -0
  70. package/dist/gaia-ops/config/cloud/aws.json +134 -0
  71. package/dist/gaia-ops/config/cloud/gcp.json +139 -0
  72. package/dist/gaia-ops/config/context-contracts.json +158 -0
  73. package/dist/gaia-ops/config/crons-schema.md +81 -0
  74. package/dist/gaia-ops/config/git_standards.json +72 -0
  75. package/dist/gaia-ops/config/surface-routing.json +421 -0
  76. package/dist/gaia-ops/config/universal-rules.json +102 -0
  77. package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
  78. package/dist/gaia-ops/hooks/adapters/base.py +219 -0
  79. package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
  80. package/dist/gaia-ops/hooks/adapters/claude_code.py +1890 -0
  81. package/dist/gaia-ops/hooks/adapters/types.py +194 -0
  82. package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
  83. package/dist/gaia-ops/hooks/hooks.json +163 -0
  84. package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
  85. package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
  86. package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
  87. package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
  88. package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +120 -0
  89. package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -0
  90. package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
  91. package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
  92. package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
  93. package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
  94. package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
  95. package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
  96. package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
  97. package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +611 -0
  98. package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
  99. package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
  100. package/dist/gaia-ops/hooks/modules/context/agentic_loop_detector.py +165 -0
  101. package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
  102. package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
  103. package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
  104. package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
  105. package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -0
  106. package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
  107. package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
  108. package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
  109. package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
  110. package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
  111. package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +577 -0
  112. package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
  113. package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
  114. package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
  115. package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
  116. package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
  117. package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
  118. package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
  119. package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -0
  120. package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
  121. package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
  122. package/dist/gaia-ops/hooks/modules/security/__init__.py +120 -0
  123. package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
  124. package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
  125. package/dist/gaia-ops/hooks/modules/security/approval_grants.py +1638 -0
  126. package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
  127. package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
  128. package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
  129. package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
  130. package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
  131. package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
  132. package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
  133. package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
  134. package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
  135. package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
  136. package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
  137. package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -0
  138. package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
  139. package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
  140. package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +174 -0
  141. package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
  142. package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +160 -0
  143. package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
  144. package/dist/gaia-ops/hooks/modules/session/session_registry.py +232 -0
  145. package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
  146. package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
  147. package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  148. package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
  149. package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
  150. package/dist/gaia-ops/hooks/modules/tools/stage_decomposer.py +315 -0
  151. package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -0
  152. package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
  153. package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
  154. package/dist/gaia-ops/hooks/post_compact.py +43 -0
  155. package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
  156. package/dist/gaia-ops/hooks/pre_compact.py +60 -0
  157. package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
  158. package/dist/gaia-ops/hooks/session_start.py +81 -0
  159. package/dist/gaia-ops/hooks/stop_hook.py +82 -0
  160. package/dist/gaia-ops/hooks/subagent_start.py +71 -0
  161. package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
  162. package/dist/gaia-ops/hooks/task_completed.py +70 -0
  163. package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
  164. package/dist/gaia-ops/settings.json +72 -0
  165. package/dist/gaia-ops/skills/README.md +154 -0
  166. package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
  167. package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
  168. package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
  169. package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
  170. package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
  171. package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
  172. package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
  173. package/dist/gaia-ops/skills/brief-spec/SKILL.md +182 -0
  174. package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
  175. package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
  176. package/dist/gaia-ops/skills/context-updater/SKILL.md +87 -0
  177. package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
  178. package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
  179. package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
  180. package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
  181. package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
  182. package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
  183. package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
  184. package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
  185. package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
  186. package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
  187. package/dist/gaia-ops/skills/gaia-release/SKILL.md +82 -0
  188. package/dist/gaia-ops/skills/gaia-release/reference.md +102 -0
  189. package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
  190. package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
  191. package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
  192. package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
  193. package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
  194. package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
  195. package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
  196. package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
  197. package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
  198. package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
  199. package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
  200. package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
  201. package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
  202. package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
  203. package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
  204. package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
  205. package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
  206. package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
  207. package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
  208. package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
  209. package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
  210. package/dist/gaia-ops/skills/reference.md +135 -0
  211. package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
  212. package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
  213. package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
  214. package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
  215. package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
  216. package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -0
  217. package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
  218. package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
  219. package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
  220. package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
  221. package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
  222. package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
  223. package/dist/gaia-ops/tools/__init__.py +9 -0
  224. package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
  225. package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
  226. package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -0
  227. package/dist/gaia-ops/tools/context/README.md +132 -0
  228. package/dist/gaia-ops/tools/context/__init__.py +42 -0
  229. package/dist/gaia-ops/tools/context/_paths.py +20 -0
  230. package/dist/gaia-ops/tools/context/context_provider.py +721 -0
  231. package/dist/gaia-ops/tools/context/context_section_reader.py +342 -0
  232. package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
  233. package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
  234. package/dist/gaia-ops/tools/context/surface_router.py +278 -0
  235. package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
  236. package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
  237. package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  238. package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  239. package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  240. package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  241. package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
  242. package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  243. package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
  244. package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
  245. package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
  246. package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
  247. package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
  248. package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
  249. package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +264 -0
  250. package/dist/gaia-ops/tools/memory/README.md +0 -0
  251. package/dist/gaia-ops/tools/memory/__init__.py +20 -0
  252. package/dist/gaia-ops/tools/memory/backfill_fts5.py +107 -0
  253. package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
  254. package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
  255. package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
  256. package/dist/gaia-ops/tools/memory/paths.py +102 -0
  257. package/dist/gaia-ops/tools/memory/scoring.py +193 -0
  258. package/dist/gaia-ops/tools/memory/search_store.py +360 -0
  259. package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
  260. package/dist/gaia-ops/tools/review/__init__.py +1 -0
  261. package/dist/gaia-ops/tools/review/review_engine.py +157 -0
  262. package/dist/gaia-ops/tools/scan/__init__.py +35 -0
  263. package/dist/gaia-ops/tools/scan/config.py +247 -0
  264. package/dist/gaia-ops/tools/scan/merge.py +212 -0
  265. package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
  266. package/dist/gaia-ops/tools/scan/registry.py +127 -0
  267. package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
  268. package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
  269. package/dist/gaia-ops/tools/scan/scanners/environment.py +349 -0
  270. package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
  271. package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
  272. package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
  273. package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
  274. package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
  275. package/dist/gaia-ops/tools/scan/setup.py +686 -0
  276. package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
  277. package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
  278. package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
  279. package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
  280. package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
  281. package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
  282. package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
  283. package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
  284. package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
  285. package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
  286. package/dist/gaia-ops/tools/scan/ui.py +624 -0
  287. package/dist/gaia-ops/tools/scan/verify.py +270 -0
  288. package/dist/gaia-ops/tools/scan/walk.py +118 -0
  289. package/dist/gaia-ops/tools/scan/workspace.py +85 -0
  290. package/dist/gaia-ops/tools/validation/README.md +244 -0
  291. package/dist/gaia-ops/tools/validation/__init__.py +17 -0
  292. package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
  293. package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
  294. package/dist/gaia-security/.claude-plugin/plugin.json +24 -0
  295. package/dist/gaia-security/README.md +90 -0
  296. package/dist/gaia-security/config/universal-rules.json +102 -0
  297. package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
  298. package/dist/gaia-security/hooks/adapters/base.py +219 -0
  299. package/dist/gaia-security/hooks/adapters/channel.py +17 -0
  300. package/dist/gaia-security/hooks/adapters/claude_code.py +1890 -0
  301. package/dist/gaia-security/hooks/adapters/types.py +194 -0
  302. package/dist/gaia-security/hooks/adapters/utils.py +25 -0
  303. package/dist/gaia-security/hooks/hooks.json +84 -0
  304. package/dist/gaia-security/hooks/modules/__init__.py +15 -0
  305. package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
  306. package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
  307. package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
  308. package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +120 -0
  309. package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -0
  310. package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
  311. package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
  312. package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
  313. package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
  314. package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
  315. package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
  316. package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
  317. package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +611 -0
  318. package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
  319. package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
  320. package/dist/gaia-security/hooks/modules/context/agentic_loop_detector.py +165 -0
  321. package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
  322. package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
  323. package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
  324. package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
  325. package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -0
  326. package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
  327. package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
  328. package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
  329. package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
  330. package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
  331. package/dist/gaia-security/hooks/modules/core/plugin_setup.py +577 -0
  332. package/dist/gaia-security/hooks/modules/core/state.py +179 -0
  333. package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
  334. package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
  335. package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
  336. package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
  337. package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
  338. package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
  339. package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -0
  340. package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
  341. package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
  342. package/dist/gaia-security/hooks/modules/security/__init__.py +120 -0
  343. package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
  344. package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
  345. package/dist/gaia-security/hooks/modules/security/approval_grants.py +1638 -0
  346. package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
  347. package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
  348. package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
  349. package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
  350. package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
  351. package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
  352. package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
  353. package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
  354. package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
  355. package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
  356. package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
  357. package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -0
  358. package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
  359. package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
  360. package/dist/gaia-security/hooks/modules/session/pending_scanner.py +174 -0
  361. package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
  362. package/dist/gaia-security/hooks/modules/session/session_event_injector.py +160 -0
  363. package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
  364. package/dist/gaia-security/hooks/modules/session/session_registry.py +232 -0
  365. package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
  366. package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
  367. package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  368. package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
  369. package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
  370. package/dist/gaia-security/hooks/modules/tools/stage_decomposer.py +315 -0
  371. package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -0
  372. package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
  373. package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
  374. package/dist/gaia-security/hooks/post_tool_use.py +54 -0
  375. package/dist/gaia-security/hooks/pre_tool_use.py +413 -0
  376. package/dist/gaia-security/hooks/session_start.py +81 -0
  377. package/dist/gaia-security/hooks/stop_hook.py +82 -0
  378. package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
  379. package/dist/gaia-security/settings.json +58 -0
  380. package/git-hooks/commit-msg +41 -0
  381. package/hooks/README.md +100 -0
  382. package/hooks/adapters/__init__.py +52 -0
  383. package/hooks/adapters/base.py +219 -0
  384. package/hooks/adapters/channel.py +17 -0
  385. package/hooks/adapters/claude_code.py +1890 -0
  386. package/hooks/adapters/types.py +194 -0
  387. package/hooks/adapters/utils.py +25 -0
  388. package/hooks/elicitation_result.py +179 -0
  389. package/hooks/hooks.json +84 -0
  390. package/hooks/modules/README.md +189 -0
  391. package/hooks/modules/__init__.py +15 -0
  392. package/hooks/modules/agents/__init__.py +29 -0
  393. package/hooks/modules/agents/contract_validator.py +647 -0
  394. package/hooks/modules/agents/response_contract.py +496 -0
  395. package/hooks/modules/agents/skill_injection_verifier.py +120 -0
  396. package/hooks/modules/agents/state_tracker.py +267 -0
  397. package/hooks/modules/agents/task_info_builder.py +74 -0
  398. package/hooks/modules/agents/transcript_analyzer.py +458 -0
  399. package/hooks/modules/agents/transcript_reader.py +152 -0
  400. package/hooks/modules/audit/__init__.py +28 -0
  401. package/hooks/modules/audit/event_detector.py +168 -0
  402. package/hooks/modules/audit/logger.py +131 -0
  403. package/hooks/modules/audit/metrics.py +134 -0
  404. package/hooks/modules/audit/workflow_auditor.py +611 -0
  405. package/hooks/modules/audit/workflow_recorder.py +296 -0
  406. package/hooks/modules/context/__init__.py +11 -0
  407. package/hooks/modules/context/agentic_loop_detector.py +165 -0
  408. package/hooks/modules/context/anchor_tracker.py +317 -0
  409. package/hooks/modules/context/compact_context_builder.py +218 -0
  410. package/hooks/modules/context/context_freshness.py +145 -0
  411. package/hooks/modules/context/context_injector.py +558 -0
  412. package/hooks/modules/context/context_writer.py +530 -0
  413. package/hooks/modules/context/contracts_loader.py +161 -0
  414. package/hooks/modules/core/__init__.py +40 -0
  415. package/hooks/modules/core/hook_entry.py +78 -0
  416. package/hooks/modules/core/paths.py +160 -0
  417. package/hooks/modules/core/plugin_mode.py +149 -0
  418. package/hooks/modules/core/plugin_setup.py +577 -0
  419. package/hooks/modules/core/state.py +179 -0
  420. package/hooks/modules/core/stdin.py +24 -0
  421. package/hooks/modules/events/__init__.py +1 -0
  422. package/hooks/modules/events/event_writer.py +210 -0
  423. package/hooks/modules/evidence/__init__.py +34 -0
  424. package/hooks/modules/evidence/assertions.py +137 -0
  425. package/hooks/modules/evidence/index_writer.py +57 -0
  426. package/hooks/modules/evidence/loader.py +126 -0
  427. package/hooks/modules/evidence/runner.py +241 -0
  428. package/hooks/modules/memory/__init__.py +8 -0
  429. package/hooks/modules/memory/episode_writer.py +216 -0
  430. package/hooks/modules/orchestrator/__init__.py +1 -0
  431. package/hooks/modules/orchestrator/delegate_mode.py +122 -0
  432. package/hooks/modules/scanning/__init__.py +8 -0
  433. package/hooks/modules/scanning/scan_trigger.py +84 -0
  434. package/hooks/modules/security/__init__.py +120 -0
  435. package/hooks/modules/security/approval_cleanup.py +87 -0
  436. package/hooks/modules/security/approval_constants.py +23 -0
  437. package/hooks/modules/security/approval_grants.py +1638 -0
  438. package/hooks/modules/security/approval_messages.py +71 -0
  439. package/hooks/modules/security/approval_scopes.py +222 -0
  440. package/hooks/modules/security/blocked_commands.py +595 -0
  441. package/hooks/modules/security/blocked_message_formatter.py +87 -0
  442. package/hooks/modules/security/command_semantics.py +181 -0
  443. package/hooks/modules/security/composition_rules.py +547 -0
  444. package/hooks/modules/security/flag_classifiers.py +873 -0
  445. package/hooks/modules/security/gitops_validator.py +179 -0
  446. package/hooks/modules/security/mutative_verbs.py +1131 -0
  447. package/hooks/modules/security/network_hosts.py +481 -0
  448. package/hooks/modules/security/prompt_validator.py +40 -0
  449. package/hooks/modules/security/shell_unwrapper.py +165 -0
  450. package/hooks/modules/security/tiers.py +196 -0
  451. package/hooks/modules/session/__init__.py +10 -0
  452. package/hooks/modules/session/pending_scanner.py +174 -0
  453. package/hooks/modules/session/session_context_writer.py +100 -0
  454. package/hooks/modules/session/session_event_injector.py +160 -0
  455. package/hooks/modules/session/session_manager.py +31 -0
  456. package/hooks/modules/session/session_registry.py +232 -0
  457. package/hooks/modules/tools/__init__.py +29 -0
  458. package/hooks/modules/tools/bash_validator.py +1008 -0
  459. package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
  460. package/hooks/modules/tools/hook_response.py +55 -0
  461. package/hooks/modules/tools/shell_parser.py +227 -0
  462. package/hooks/modules/tools/stage_decomposer.py +315 -0
  463. package/hooks/modules/tools/task_validator.py +294 -0
  464. package/hooks/modules/validation/__init__.py +23 -0
  465. package/hooks/modules/validation/commit_validator.py +380 -0
  466. package/hooks/post_compact.py +43 -0
  467. package/hooks/post_tool_use.py +54 -0
  468. package/hooks/pre_compact.py +60 -0
  469. package/hooks/pre_tool_use.py +413 -0
  470. package/hooks/session_start.py +81 -0
  471. package/hooks/stop_hook.py +82 -0
  472. package/hooks/subagent_start.py +71 -0
  473. package/hooks/subagent_stop.py +295 -0
  474. package/hooks/task_completed.py +70 -0
  475. package/hooks/user_prompt_submit.py +246 -0
  476. package/index.js +83 -0
  477. package/package.json +99 -0
  478. package/pyproject.toml +32 -0
  479. package/skills/README.md +154 -0
  480. package/skills/agent-protocol/SKILL.md +93 -0
  481. package/skills/agent-protocol/examples.md +223 -0
  482. package/skills/agent-response/SKILL.md +69 -0
  483. package/skills/agentic-loop/SKILL.md +80 -0
  484. package/skills/agentic-loop/reference.md +378 -0
  485. package/skills/blog-writing/SKILL.md +98 -0
  486. package/skills/blog-writing/reference.md +130 -0
  487. package/skills/brief-spec/SKILL.md +182 -0
  488. package/skills/command-execution/SKILL.md +64 -0
  489. package/skills/command-execution/reference.md +83 -0
  490. package/skills/context-updater/SKILL.md +87 -0
  491. package/skills/context-updater/examples.md +71 -0
  492. package/skills/developer-patterns/SKILL.md +50 -0
  493. package/skills/developer-patterns/reference.md +112 -0
  494. package/skills/execution/SKILL.md +99 -0
  495. package/skills/fast-queries/SKILL.md +43 -0
  496. package/skills/gaia-compact/SKILL.md +74 -0
  497. package/skills/gaia-patterns/SKILL.md +108 -0
  498. package/skills/gaia-patterns/reference.md +395 -0
  499. package/skills/gaia-planner/SKILL.md +37 -0
  500. package/skills/gaia-planner/reference.md +107 -0
  501. package/skills/gaia-release/SKILL.md +82 -0
  502. package/skills/gaia-release/reference.md +102 -0
  503. package/skills/gaia-self-check/SKILL.md +114 -0
  504. package/skills/gaia-self-check/reference.md +453 -0
  505. package/skills/gaia-verify/SKILL.md +77 -0
  506. package/skills/gaia-verify/reference.md +80 -0
  507. package/skills/git-conventions/SKILL.md +47 -0
  508. package/skills/gitops-patterns/SKILL.md +60 -0
  509. package/skills/gitops-patterns/reference.md +183 -0
  510. package/skills/gmail-policy/SKILL.md +200 -0
  511. package/skills/gmail-policy/reference.md +150 -0
  512. package/skills/gmail-triage/SKILL.md +100 -0
  513. package/skills/gws-setup/SKILL.md +99 -0
  514. package/skills/gws-setup/reference.md +73 -0
  515. package/skills/investigation/SKILL.md +100 -0
  516. package/skills/memory-curation/SKILL.md +83 -0
  517. package/skills/memory-search/SKILL.md +88 -0
  518. package/skills/orchestrator-approval/SKILL.md +160 -0
  519. package/skills/orchestrator-approval/reference.md +174 -0
  520. package/skills/pending-approvals/SKILL.md +72 -0
  521. package/skills/pending-approvals/reference.md +214 -0
  522. package/skills/readme-writing/SKILL.md +71 -0
  523. package/skills/readme-writing/reference.md +188 -0
  524. package/skills/reference.md +135 -0
  525. package/skills/request-approval/SKILL.md +140 -0
  526. package/skills/request-approval/examples.md +140 -0
  527. package/skills/request-approval/reference.md +57 -0
  528. package/skills/schedule-task/SKILL.md +64 -0
  529. package/skills/schedule-task/reference.md +233 -0
  530. package/skills/security-tiers/SKILL.md +141 -0
  531. package/skills/security-tiers/destructive-commands-reference.md +623 -0
  532. package/skills/security-tiers/reference.md +39 -0
  533. package/skills/skill-creation/SKILL.md +92 -0
  534. package/skills/skill-creation/reference.md +29 -0
  535. package/skills/terraform-patterns/SKILL.md +89 -0
  536. package/skills/terraform-patterns/reference.md +93 -0
  537. package/templates/README.md +69 -0
  538. package/templates/managed-settings.template.json +43 -0
  539. package/tools/__init__.py +9 -0
  540. package/tools/agentic-loop/decide-status.py +210 -0
  541. package/tools/agentic-loop/parse-metric.py +106 -0
  542. package/tools/agentic-loop/record-iteration.py +221 -0
  543. package/tools/context/README.md +132 -0
  544. package/tools/context/__init__.py +42 -0
  545. package/tools/context/_paths.py +20 -0
  546. package/tools/context/context_provider.py +721 -0
  547. package/tools/context/context_section_reader.py +342 -0
  548. package/tools/context/deep_merge.py +159 -0
  549. package/tools/context/pending_updates.py +760 -0
  550. package/tools/context/surface_router.py +278 -0
  551. package/tools/fast-queries/README.md +65 -0
  552. package/tools/fast-queries/__init__.py +30 -0
  553. package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
  554. package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
  555. package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
  556. package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
  557. package/tools/fast-queries/run_triage.sh +59 -0
  558. package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
  559. package/tools/gaia_simulator/__init__.py +33 -0
  560. package/tools/gaia_simulator/cli.py +354 -0
  561. package/tools/gaia_simulator/extractor.py +457 -0
  562. package/tools/gaia_simulator/reporter.py +258 -0
  563. package/tools/gaia_simulator/routing_simulator.py +334 -0
  564. package/tools/gaia_simulator/runner.py +539 -0
  565. package/tools/gaia_simulator/skills_mapper.py +264 -0
  566. package/tools/memory/README.md +0 -0
  567. package/tools/memory/__init__.py +20 -0
  568. package/tools/memory/backfill_fts5.py +107 -0
  569. package/tools/memory/conflict_detector.py +295 -0
  570. package/tools/memory/episodic.py +1210 -0
  571. package/tools/memory/git_invalidator.py +262 -0
  572. package/tools/memory/paths.py +102 -0
  573. package/tools/memory/scoring.py +193 -0
  574. package/tools/memory/search_store.py +360 -0
  575. package/tools/persist_transcript_analysis.py +85 -0
  576. package/tools/review/__init__.py +1 -0
  577. package/tools/review/review_engine.py +157 -0
  578. package/tools/scan/__init__.py +35 -0
  579. package/tools/scan/config.py +247 -0
  580. package/tools/scan/merge.py +212 -0
  581. package/tools/scan/orchestrator.py +549 -0
  582. package/tools/scan/registry.py +127 -0
  583. package/tools/scan/scanners/__init__.py +18 -0
  584. package/tools/scan/scanners/base.py +137 -0
  585. package/tools/scan/scanners/environment.py +349 -0
  586. package/tools/scan/scanners/git.py +570 -0
  587. package/tools/scan/scanners/infrastructure.py +875 -0
  588. package/tools/scan/scanners/orchestration.py +600 -0
  589. package/tools/scan/scanners/stack.py +1085 -0
  590. package/tools/scan/scanners/tools.py +260 -0
  591. package/tools/scan/setup.py +686 -0
  592. package/tools/scan/tests/__init__.py +1 -0
  593. package/tools/scan/tests/conftest.py +796 -0
  594. package/tools/scan/tests/test_environment.py +323 -0
  595. package/tools/scan/tests/test_git.py +419 -0
  596. package/tools/scan/tests/test_infrastructure.py +382 -0
  597. package/tools/scan/tests/test_integration.py +920 -0
  598. package/tools/scan/tests/test_merge.py +269 -0
  599. package/tools/scan/tests/test_orchestration.py +304 -0
  600. package/tools/scan/tests/test_stack.py +604 -0
  601. package/tools/scan/tests/test_tools.py +349 -0
  602. package/tools/scan/ui.py +624 -0
  603. package/tools/scan/verify.py +270 -0
  604. package/tools/scan/walk.py +118 -0
  605. package/tools/scan/workspace.py +85 -0
  606. package/tools/validation/README.md +244 -0
  607. package/tools/validation/__init__.py +17 -0
  608. package/tools/validation/approval_gate.py +321 -0
  609. package/tools/validation/validate_skills.py +189 -0
@@ -0,0 +1,611 @@
1
+ """
2
+ Workflow auditing: anomaly detection and Gaia analysis signaling.
3
+
4
+ Renamed from anomaly_detector.py and expanded with additional anomaly types.
5
+
6
+ Provides:
7
+ - audit(): Full anomaly detection suite -> list of anomaly dicts
8
+ - signal_gaia_analysis(): Create flag file for Gaia analysis
9
+ """
10
+
11
+ import json
12
+ import logging
13
+ import re
14
+ from collections import deque
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional
18
+
19
+ from ..agents.transcript_analyzer import TranscriptAnalysis
20
+ from .workflow_recorder import get_workflow_memory_dir
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Transcript-analysis check helpers (T009)
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ def _check_investigation_skip(
31
+ analysis: TranscriptAnalysis,
32
+ ) -> Optional[Dict[str, str]]:
33
+ """Warning if the agent's first tool call was Bash (skipped investigation)."""
34
+ if analysis.first_tool_name == "Bash":
35
+ return {
36
+ "type": "investigation_skip",
37
+ "severity": "warning",
38
+ "message": (
39
+ "Agent's first tool call was Bash instead of a "
40
+ "read-only investigation tool (Read/Glob/Grep)"
41
+ ),
42
+ }
43
+ return None
44
+
45
+
46
+ def _check_context_ignored(
47
+ analysis: TranscriptAnalysis,
48
+ ) -> Optional[Dict[str, str]]:
49
+ """Warning if the first tool call does not reference project-context paths."""
50
+ if not analysis.tool_sequence:
51
+ return None
52
+ first_call = analysis.tool_sequence[0]
53
+ args_str = json.dumps(first_call.arguments)
54
+ # Look for any project-context path references
55
+ context_indicators = [
56
+ "project-context",
57
+ ".claude/",
58
+ "CLAUDE.md",
59
+ "context-contracts",
60
+ ]
61
+ if not any(indicator in args_str for indicator in context_indicators):
62
+ return {
63
+ "type": "context_ignored",
64
+ "severity": "warning",
65
+ "message": (
66
+ "First tool call does not reference any project-context "
67
+ "paths — agent may have ignored injected context"
68
+ ),
69
+ }
70
+ return None
71
+
72
+
73
+ def _check_context_update_missing(
74
+ analysis: TranscriptAnalysis,
75
+ agent_output: str,
76
+ ) -> Optional[Dict[str, str]]:
77
+ """Info if context-updater skill was injected but no CONTEXT_UPDATE emitted."""
78
+ if "context-updater" in analysis.skills_injected:
79
+ if "CONTEXT_UPDATE" not in agent_output:
80
+ return {
81
+ "type": "context_update_missing",
82
+ "severity": "info",
83
+ "message": (
84
+ "context-updater skill was injected but agent did not "
85
+ "emit a CONTEXT_UPDATE block"
86
+ ),
87
+ }
88
+ return None
89
+
90
+
91
+ def _check_excessive_tool_calls(
92
+ analysis: TranscriptAnalysis,
93
+ ) -> Optional[Dict[str, str]]:
94
+ """Warning if tool_call_count exceeds 75."""
95
+ if analysis.tool_call_count > 75:
96
+ return {
97
+ "type": "excessive_tool_calls",
98
+ "severity": "warning",
99
+ "message": (
100
+ f"Agent made {analysis.tool_call_count} tool calls "
101
+ f"(threshold: 75) — may indicate inefficient exploration"
102
+ ),
103
+ }
104
+ return None
105
+
106
+
107
+ def _check_token_budget(
108
+ analysis: TranscriptAnalysis,
109
+ ) -> Optional[Dict[str, str]]:
110
+ """Info if cache_creation_tokens exceeds 200000."""
111
+ if analysis.cache_creation_tokens > 200000:
112
+ return {
113
+ "type": "token_budget",
114
+ "severity": "info",
115
+ "message": (
116
+ f"Cache creation tokens ({analysis.cache_creation_tokens}) "
117
+ f"exceeded 200,000 — large context was created"
118
+ ),
119
+ }
120
+ return None
121
+
122
+
123
+ def _check_pipe_retroactive(
124
+ analysis: TranscriptAnalysis,
125
+ ) -> List[Dict[str, str]]:
126
+ """Warning per pipe command detected in transcript."""
127
+ results: List[Dict[str, str]] = []
128
+ for cmd in analysis.pipe_commands:
129
+ # Truncate long commands for readability
130
+ display_cmd = cmd[:120] + "..." if len(cmd) > 120 else cmd
131
+ results.append({
132
+ "type": "pipe_retroactive",
133
+ "severity": "warning",
134
+ "message": f"Pipe command detected in transcript: {display_cmd}",
135
+ })
136
+ return results
137
+
138
+
139
+ def _check_model_mismatch(
140
+ analysis: TranscriptAnalysis,
141
+ metrics: Dict[str, Any],
142
+ ) -> Optional[Dict[str, str]]:
143
+ """Info if transcript model differs from agent definition model."""
144
+ definition_model = ""
145
+ snapshot = metrics.get("default_skills_snapshot")
146
+ if isinstance(snapshot, dict):
147
+ definition_model = snapshot.get("model", "")
148
+ if (
149
+ analysis.model
150
+ and definition_model
151
+ and analysis.model != definition_model
152
+ ):
153
+ return {
154
+ "type": "model_mismatch",
155
+ "severity": "info",
156
+ "message": (
157
+ f"Transcript model ({analysis.model}) differs from "
158
+ f"agent definition model ({definition_model})"
159
+ ),
160
+ }
161
+ return None
162
+
163
+
164
+ def _check_skill_order(
165
+ analysis: TranscriptAnalysis,
166
+ metrics: Dict[str, Any],
167
+ ) -> Optional[Dict[str, str]]:
168
+ """Info if skills were injected in an unexpected order."""
169
+ snapshot = metrics.get("default_skills_snapshot")
170
+ if not isinstance(snapshot, dict):
171
+ return None
172
+ expected_skills = snapshot.get("skills", [])
173
+ if not expected_skills or not analysis.skills_injected:
174
+ return None
175
+ # Check if the injected skills appear in the expected order
176
+ # (only consider skills that are in the expected list)
177
+ expected_set = set(expected_skills)
178
+ actual_ordered = [s for s in analysis.skills_injected if s in expected_set]
179
+ expected_ordered = [s for s in expected_skills if s in set(actual_ordered)]
180
+ if actual_ordered and expected_ordered and actual_ordered != expected_ordered:
181
+ return {
182
+ "type": "skill_order",
183
+ "severity": "info",
184
+ "message": (
185
+ f"Skills injected in unexpected order: "
186
+ f"{actual_ordered} (expected: {expected_ordered})"
187
+ ),
188
+ }
189
+ return None
190
+
191
+
192
+ def _check_duplicate_tools(
193
+ analysis: TranscriptAnalysis,
194
+ ) -> Optional[Dict[str, str]]:
195
+ """Info if duplicate tool calls were detected."""
196
+ if analysis.duplicate_tool_calls:
197
+ dup_summary = ", ".join(
198
+ f"{d.tool_name}(x{len(d.indices)})"
199
+ for d in analysis.duplicate_tool_calls
200
+ )
201
+ return {
202
+ "type": "duplicate_tools",
203
+ "severity": "info",
204
+ "message": (
205
+ f"Duplicate tool calls detected: {dup_summary}"
206
+ ),
207
+ }
208
+ return None
209
+
210
+
211
+ def _check_token_explosion(
212
+ analysis: TranscriptAnalysis,
213
+ ) -> Optional[Dict[str, str]]:
214
+ """Warning if total token consumption exceeds 10 million."""
215
+ total = (
216
+ analysis.input_tokens
217
+ + analysis.cache_creation_tokens
218
+ + analysis.output_tokens
219
+ )
220
+ if total > 10_000_000:
221
+ return {
222
+ "type": "token_explosion",
223
+ "severity": "warning",
224
+ "message": (
225
+ f"Total token consumption ({total:,}) exceeded 10,000,000 "
226
+ f"— possible runaway generation"
227
+ ),
228
+ }
229
+ return None
230
+
231
+
232
+ def _check_cache_efficiency(
233
+ analysis: TranscriptAnalysis,
234
+ ) -> Optional[Dict[str, str]]:
235
+ """Info if cache read ratio is below 60% with significant token volume."""
236
+ total = (
237
+ analysis.cache_read_tokens
238
+ + analysis.cache_creation_tokens
239
+ + analysis.input_tokens
240
+ )
241
+ if total > 1000:
242
+ ratio = analysis.cache_read_tokens / total
243
+ if ratio < 0.60:
244
+ return {
245
+ "type": "cache_efficiency",
246
+ "severity": "info",
247
+ "message": (
248
+ f"Cache read ratio is {ratio:.1%} (below 60%) "
249
+ f"with {total:,} total input tokens — cache may be underutilized"
250
+ ),
251
+ }
252
+ return None
253
+
254
+
255
+ def _check_bash_permission_gate(
256
+ analysis: TranscriptAnalysis,
257
+ metrics: Dict[str, Any],
258
+ ) -> Optional[Dict[str, str]]:
259
+ """Warning if agent declares Bash but made 0 Bash calls.
260
+
261
+ When the native Claude Code permission layer denies Bash before the
262
+ hook fires, our hook never runs and no log entry is produced. The agent
263
+ reports 'Permission to use Bash has been denied' but the failure is
264
+ otherwise invisible. A declared-Bash agent with 0 actual Bash calls is
265
+ a reliable signal of this invisible gate.
266
+ """
267
+ snapshot = metrics.get("default_skills_snapshot") or {}
268
+ declared_tools = snapshot.get("tools", [])
269
+ if not isinstance(declared_tools, list):
270
+ return None
271
+
272
+ declared_lower = [t.lower() for t in declared_tools]
273
+ if "bash" not in declared_lower:
274
+ return None
275
+
276
+ if len(analysis.bash_commands) == 0:
277
+ agent_name = metrics.get("agent", "unknown")
278
+ return {
279
+ "type": "bash_permission_gate",
280
+ "severity": "warning",
281
+ "message": (
282
+ f"WARNING: Agent {agent_name} declares Bash but made 0 Bash calls. "
283
+ f"Possible native permission layer block."
284
+ ),
285
+ }
286
+ return None
287
+
288
+
289
+ def _check_duplicate_write_storm(
290
+ analysis: TranscriptAnalysis,
291
+ ) -> Optional[Dict[str, str]]:
292
+ """Warning if Write or Edit tool calls appear 3+ times with identical args."""
293
+ for dup in analysis.duplicate_tool_calls:
294
+ if dup.tool_name in ("Write", "Edit") and len(dup.indices) >= 3:
295
+ return {
296
+ "type": "duplicate_write_storm",
297
+ "severity": "warning",
298
+ "message": (
299
+ f"Duplicate {dup.tool_name} storm detected: "
300
+ f"{len(dup.indices)} identical calls at indices "
301
+ f"{dup.indices}"
302
+ ),
303
+ }
304
+ return None
305
+
306
+
307
+ def _check_duration_outlier(
308
+ analysis: TranscriptAnalysis,
309
+ ) -> Optional[Dict[str, str]]:
310
+ """Warning if agent execution exceeded 10 minutes."""
311
+ if analysis.duration_ms is not None and analysis.duration_ms > 600_000:
312
+ minutes = analysis.duration_ms / 60_000
313
+ return {
314
+ "type": "duration_outlier",
315
+ "severity": "warning",
316
+ "message": (
317
+ f"Agent execution took {minutes:.1f} minutes "
318
+ f"(threshold: 10 min) — may indicate stalled or inefficient work"
319
+ ),
320
+ }
321
+ return None
322
+
323
+
324
+ def _check_tool_call_velocity(
325
+ analysis: TranscriptAnalysis,
326
+ ) -> Optional[Dict[str, str]]:
327
+ """Warning if tool call rate exceeds 20 calls per minute."""
328
+ if (
329
+ analysis.duration_ms is not None
330
+ and analysis.duration_ms > 0
331
+ and (analysis.tool_call_count / (analysis.duration_ms / 60_000)) > 20
332
+ ):
333
+ velocity = analysis.tool_call_count / (analysis.duration_ms / 60_000)
334
+ return {
335
+ "type": "tool_call_velocity",
336
+ "severity": "warning",
337
+ "message": (
338
+ f"Tool call velocity is {velocity:.1f} calls/min "
339
+ f"(threshold: 20) — agent may be thrashing"
340
+ ),
341
+ }
342
+ return None
343
+
344
+
345
+ def audit(
346
+ metrics: Dict[str, Any],
347
+ agent_output: str = "",
348
+ task_info: Optional[Dict[str, Any]] = None,
349
+ rejected_sections: Optional[List[str]] = None,
350
+ transcript_analysis: Optional[TranscriptAnalysis] = None,
351
+ ) -> List[Dict[str, str]]:
352
+ """
353
+ Detect anomalies in workflow execution.
354
+
355
+ Checks:
356
+ - execution_failure: exit_code != 0
357
+ - consecutive_failures: 3+ failures in a row for same agent
358
+ - missing_evidence: COMPLETE but no evidence in json:contract block
359
+ - empty_evidence: json:contract evidence exists but commands_run empty or all "not run"
360
+ - skipped_verification: task has verify command in injected_context but not in commands_run
361
+ - scope_escalation: rejected_sections exist (agent tried to write outside its scope)
362
+
363
+ Transcript-analysis checks (only when transcript_analysis is provided):
364
+ - investigation_skip: first tool was Bash
365
+ - context_ignored: first tool call has no project-context paths
366
+ - context_update_missing: context-updater injected but no CONTEXT_UPDATE emitted
367
+ - excessive_tool_calls: tool_call_count > 75
368
+ - token_budget: cache_creation_tokens > 200000
369
+ - token_explosion: total tokens (input+cache_creation+output) > 10M
370
+ - cache_efficiency: cache read ratio < 60% with significant volume
371
+ - duplicate_write_storm: Write/Edit tool with 3+ identical calls
372
+ - duration_outlier: duration_ms > 600,000 (10 min)
373
+ - tool_call_velocity: > 20 tool calls per minute
374
+ - pipe_retroactive: pipe commands found in transcript
375
+ - model_mismatch: transcript model != agent definition model
376
+ - skill_order: skills injected in unexpected order
377
+ - duplicate_tools: duplicate tool calls detected
378
+ - bash_permission_gate: agent declares Bash but made 0 calls (native layer block)
379
+
380
+ Args:
381
+ metrics: Workflow metrics dict (from workflow_recorder.record()).
382
+ agent_output: Complete agent output string (for evidence checks).
383
+ task_info: Task metadata including injected_context (for verification checks).
384
+ rejected_sections: List of context sections rejected by permission validation.
385
+ transcript_analysis: Optional TranscriptAnalysis from transcript_analyzer.
386
+ When None (default), transcript-based checks are skipped for backward
387
+ compatibility.
388
+
389
+ Returns:
390
+ List of anomaly descriptions
391
+ """
392
+ anomalies: List[Dict[str, str]] = []
393
+ task_info = task_info or {}
394
+
395
+ # --- existing: execution_failure ---
396
+ if metrics.get("exit_code", 0) != 0:
397
+ anomalies.append({
398
+ "type": "execution_failure",
399
+ "severity": "error",
400
+ "message": f"Agent {metrics['agent']} failed with exit code {metrics['exit_code']}"
401
+ })
402
+
403
+ # --- existing: consecutive_failures ---
404
+ try:
405
+ workflow_memory_dir = get_workflow_memory_dir()
406
+ metrics_file = workflow_memory_dir / "metrics.jsonl"
407
+
408
+ if metrics_file.exists():
409
+ with open(metrics_file) as f:
410
+ recent = list(deque(f, maxlen=7))
411
+ # Get last 5 metrics (excluding current which is the last line)
412
+ last_5 = (
413
+ [json.loads(line) for line in recent[:-1]][-5:]
414
+ if len(recent) > 1
415
+ else []
416
+ )
417
+
418
+ # Count recent failures for same agent
419
+ agent = metrics["agent"]
420
+ recent_failures = [
421
+ m for m in last_5
422
+ if m.get("agent") == agent and m.get("exit_code", 0) != 0
423
+ ]
424
+
425
+ # If current also failed and we have 2+ previous failures
426
+ if metrics.get("exit_code", 0) != 0 and len(recent_failures) >= 2:
427
+ anomalies.append({
428
+ "type": "consecutive_failures",
429
+ "severity": "critical",
430
+ "message": (
431
+ f"Agent {agent} has failed "
432
+ f"{len(recent_failures) + 1} times consecutively"
433
+ ),
434
+ })
435
+ except Exception as e:
436
+ logger.debug(f"Could not check consecutive failures: {e}")
437
+
438
+ # --- NEW: missing_evidence ---
439
+ if agent_output:
440
+ plan_status = metrics.get("plan_status", "")
441
+ if "COMPLETE" in plan_status:
442
+ from ..agents.contract_validator import parse_contract
443
+ contract = parse_contract(agent_output)
444
+ has_evidence = (
445
+ contract is not None
446
+ and isinstance(contract.get("evidence_report"), dict)
447
+ and bool(contract["evidence_report"])
448
+ )
449
+ if not has_evidence:
450
+ anomalies.append({
451
+ "type": "missing_evidence",
452
+ "severity": "warning",
453
+ "message": (
454
+ f"Agent {metrics['agent']} completed but "
455
+ f"did not include evidence in json:contract block"
456
+ ),
457
+ })
458
+
459
+ # --- NEW: empty_evidence ---
460
+ if agent_output:
461
+ from ..agents.contract_validator import parse_contract
462
+ contract = parse_contract(agent_output)
463
+ if contract is not None:
464
+ evidence = contract.get("evidence_report")
465
+ if isinstance(evidence, dict):
466
+ commands_run = evidence.get("commands_run", [])
467
+ if isinstance(commands_run, list):
468
+ not_run_pattern = re.compile(
469
+ r"\b(not\s+run|not\s+executed|skipped|n/a|none)\b",
470
+ re.IGNORECASE,
471
+ )
472
+ if not commands_run:
473
+ # commands_run key exists but is empty list
474
+ anomalies.append({
475
+ "type": "empty_evidence",
476
+ "severity": "warning",
477
+ "message": (
478
+ f"Agent {metrics['agent']} has evidence in "
479
+ f"json:contract but commands_run is empty"
480
+ ),
481
+ })
482
+ elif all(
483
+ isinstance(c, str) and not_run_pattern.search(c)
484
+ for c in commands_run
485
+ ):
486
+ anomalies.append({
487
+ "type": "empty_evidence",
488
+ "severity": "warning",
489
+ "message": (
490
+ f"Agent {metrics['agent']} has evidence in "
491
+ f"json:contract but all commands_run entries "
492
+ f"indicate 'not run'"
493
+ ),
494
+ })
495
+
496
+ # --- NEW: skipped_verification ---
497
+ injected = task_info.get("injected_context") or {}
498
+ investigation_brief = injected.get("investigation_brief", {}) or {}
499
+ required_checks = investigation_brief.get("required_checks", [])
500
+ if required_checks and agent_output:
501
+ # Extract commands that were actually run from evidence
502
+ from ..agents.contract_validator import extract_commands_from_evidence
503
+ commands_run = extract_commands_from_evidence(agent_output)
504
+
505
+ # Check if any required check mentions a verify command
506
+ for check in required_checks:
507
+ if isinstance(check, str) and "verify" in check.lower():
508
+ # If there are required verification checks but no commands were run at all
509
+ if not commands_run:
510
+ anomalies.append({
511
+ "type": "skipped_verification",
512
+ "severity": "warning",
513
+ "message": (
514
+ f"Agent {metrics['agent']} has verification requirements "
515
+ f"in injected_context but ran no commands"
516
+ ),
517
+ })
518
+ break
519
+
520
+ # --- NEW: scope_escalation ---
521
+ if rejected_sections:
522
+ anomalies.append({
523
+ "type": "scope_escalation",
524
+ "severity": "warning",
525
+ "message": (
526
+ f"Agent {metrics['agent']} attempted to write to "
527
+ f"unauthorized sections: {', '.join(rejected_sections)}"
528
+ ),
529
+ })
530
+
531
+ # --- Transcript-analysis checks (T009) ---
532
+ if transcript_analysis is not None:
533
+ for check_fn in (
534
+ _check_investigation_skip,
535
+ _check_context_ignored,
536
+ _check_excessive_tool_calls,
537
+ _check_token_budget,
538
+ _check_duplicate_tools,
539
+ _check_token_explosion,
540
+ _check_cache_efficiency,
541
+ _check_duplicate_write_storm,
542
+ _check_duration_outlier,
543
+ _check_tool_call_velocity,
544
+ ):
545
+ result = check_fn(transcript_analysis)
546
+ if result is not None:
547
+ anomalies.append(result)
548
+
549
+ # Checks that need agent_output
550
+ result = _check_context_update_missing(transcript_analysis, agent_output)
551
+ if result is not None:
552
+ anomalies.append(result)
553
+
554
+ # Checks that need metrics
555
+ for check_fn_m in (_check_model_mismatch, _check_skill_order, _check_bash_permission_gate):
556
+ result = check_fn_m(transcript_analysis, metrics)
557
+ if result is not None:
558
+ anomalies.append(result)
559
+
560
+ # Pipe check returns a list (one per pipe command)
561
+ anomalies.extend(_check_pipe_retroactive(transcript_analysis))
562
+
563
+ return anomalies
564
+
565
+
566
+
567
+ def signal_gaia_analysis(
568
+ anomalies: List[Dict],
569
+ metrics: Dict[str, Any],
570
+ ) -> None:
571
+ """
572
+ Signal that Gaia analysis is needed.
573
+
574
+ Creates a flag file that orchestrator can detect.
575
+ """
576
+ try:
577
+ signals_dir = get_workflow_memory_dir() / "signals"
578
+ signals_dir.mkdir(parents=True, exist_ok=True)
579
+
580
+ signal_file = signals_dir / "needs_analysis.flag"
581
+
582
+ signal_data = {
583
+ "timestamp": datetime.now().isoformat(),
584
+ "created_at": datetime.now().isoformat(),
585
+ "ttl_hours": 1,
586
+ "anomalies": anomalies,
587
+ "metrics_summary": {
588
+ "agent": metrics["agent"],
589
+ "task_id": metrics["task_id"],
590
+ "duration_ms": metrics.get("duration_ms"),
591
+ "exit_code": metrics.get("exit_code"),
592
+ },
593
+ "suggested_action": "Invoke /gaia for system analysis",
594
+ }
595
+
596
+ with open(signal_file, "w") as f:
597
+ json.dump(signal_data, f, indent=2)
598
+
599
+ logger.info(f"Gaia analysis signal created: {signal_file}")
600
+
601
+ # Also log to a permanent anomaly log
602
+ anomaly_log = signals_dir.parent / "anomalies.jsonl"
603
+ with open(anomaly_log, "a") as f:
604
+ f.write(json.dumps({
605
+ "timestamp": datetime.now().isoformat(),
606
+ "anomalies": anomalies,
607
+ "metrics": metrics,
608
+ }) + "\n")
609
+
610
+ except Exception as e:
611
+ logger.warning(f"Could not create analysis signal: {e}")