@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.
- package/.claude-plugin/marketplace.json +33 -0
- package/.claude-plugin/plugin.json +26 -0
- package/ARCHITECTURE.md +335 -0
- package/CHANGELOG.md +1212 -0
- package/CODE_OF_CONDUCT.md +11 -0
- package/CONTRIBUTING.md +146 -0
- package/INSTALL.md +436 -0
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/SECURITY.md +47 -0
- package/agents/README.md +78 -0
- package/agents/cloud-troubleshooter.md +73 -0
- package/agents/developer.md +65 -0
- package/agents/gaia-operator.md +64 -0
- package/agents/gaia-orchestrator.md +237 -0
- package/agents/gaia-planner.md +53 -0
- package/agents/gaia-system.md +70 -0
- package/agents/gitops-operator.md +61 -0
- package/agents/terraform-architect.md +63 -0
- package/bin/README.md +106 -0
- package/bin/cli/__init__.py +1 -0
- package/bin/cli/approvals.py +740 -0
- package/bin/cli/cleanup.py +562 -0
- package/bin/cli/context.py +283 -0
- package/bin/cli/doctor.py +628 -0
- package/bin/cli/history.py +305 -0
- package/bin/cli/memory.py +464 -0
- package/bin/cli/metrics.py +1068 -0
- package/bin/cli/plans.py +515 -0
- package/bin/cli/status.py +302 -0
- package/bin/cli/update.py +382 -0
- package/bin/gaia +112 -0
- package/bin/gaia-cleanup.js +531 -0
- package/bin/gaia-doctor.js +635 -0
- package/bin/gaia-evidence +126 -0
- package/bin/gaia-history.js +251 -0
- package/bin/gaia-metrics.js +1278 -0
- package/bin/gaia-review.js +269 -0
- package/bin/gaia-scan +44 -0
- package/bin/gaia-scan.py +589 -0
- package/bin/gaia-skills-diagnose.js +929 -0
- package/bin/gaia-status.js +278 -0
- package/bin/gaia-uninstall.js +111 -0
- package/bin/gaia-update.js +816 -0
- package/bin/pre-publish-validate.js +610 -0
- package/bin/python-detect.js +60 -0
- package/commands/README.md +64 -0
- package/commands/gaia.md +37 -0
- package/commands/scan-project.md +67 -0
- package/config/README.md +71 -0
- package/config/cloud/aws.json +134 -0
- package/config/cloud/gcp.json +139 -0
- package/config/context-contracts.json +158 -0
- package/config/crons-schema.md +81 -0
- package/config/git_standards.json +72 -0
- package/config/surface-routing.json +421 -0
- package/config/universal-rules.json +102 -0
- package/dist/gaia-ops/.claude-plugin/plugin.json +24 -0
- package/dist/gaia-ops/README.md +80 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/developer.md +65 -0
- package/dist/gaia-ops/agents/gaia-operator.md +64 -0
- package/dist/gaia-ops/agents/gaia-orchestrator.md +237 -0
- package/dist/gaia-ops/agents/gaia-planner.md +53 -0
- package/dist/gaia-ops/agents/gaia-system.md +70 -0
- package/dist/gaia-ops/agents/gitops-operator.md +61 -0
- package/dist/gaia-ops/agents/terraform-architect.md +63 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +71 -0
- package/dist/gaia-ops/config/cloud/aws.json +134 -0
- package/dist/gaia-ops/config/cloud/gcp.json +139 -0
- package/dist/gaia-ops/config/context-contracts.json +158 -0
- package/dist/gaia-ops/config/crons-schema.md +81 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +421 -0
- package/dist/gaia-ops/config/universal-rules.json +102 -0
- package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-ops/hooks/adapters/base.py +219 -0
- package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
- package/dist/gaia-ops/hooks/adapters/claude_code.py +1890 -0
- package/dist/gaia-ops/hooks/adapters/types.py +194 -0
- package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
- package/dist/gaia-ops/hooks/hooks.json +163 -0
- package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
- package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +120 -0
- package/dist/gaia-ops/hooks/modules/agents/state_tracker.py +267 -0
- package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +611 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-ops/hooks/modules/context/agentic_loop_detector.py +165 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +218 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +558 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +530 -0
- package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +577 -0
- package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
- package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +216 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +122 -0
- package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +120 -0
- package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +1638 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +222 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +595 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +181 -0
- package/dist/gaia-ops/hooks/modules/security/composition_rules.py +547 -0
- package/dist/gaia-ops/hooks/modules/security/flag_classifiers.py +873 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +1131 -0
- package/dist/gaia-ops/hooks/modules/security/network_hosts.py +481 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/shell_unwrapper.py +165 -0
- package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-ops/hooks/modules/session/pending_scanner.py +174 -0
- package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +160 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/session/session_registry.py +232 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +1008 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +231 -0
- package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-ops/hooks/modules/tools/stage_decomposer.py +315 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +294 -0
- package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-ops/hooks/post_compact.py +43 -0
- package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
- package/dist/gaia-ops/hooks/pre_compact.py +60 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +413 -0
- package/dist/gaia-ops/hooks/session_start.py +81 -0
- package/dist/gaia-ops/hooks/stop_hook.py +82 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +295 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +246 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +154 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +93 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +223 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +69 -0
- package/dist/gaia-ops/skills/agentic-loop/SKILL.md +80 -0
- package/dist/gaia-ops/skills/agentic-loop/reference.md +378 -0
- package/dist/gaia-ops/skills/blog-writing/SKILL.md +98 -0
- package/dist/gaia-ops/skills/blog-writing/reference.md +130 -0
- package/dist/gaia-ops/skills/brief-spec/SKILL.md +182 -0
- package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
- package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
- package/dist/gaia-ops/skills/context-updater/SKILL.md +87 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +50 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +99 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +43 -0
- package/dist/gaia-ops/skills/gaia-compact/SKILL.md +74 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +108 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +395 -0
- package/dist/gaia-ops/skills/gaia-planner/SKILL.md +37 -0
- package/dist/gaia-ops/skills/gaia-planner/reference.md +107 -0
- package/dist/gaia-ops/skills/gaia-release/SKILL.md +82 -0
- package/dist/gaia-ops/skills/gaia-release/reference.md +102 -0
- package/dist/gaia-ops/skills/gaia-self-check/SKILL.md +114 -0
- package/dist/gaia-ops/skills/gaia-self-check/reference.md +453 -0
- package/dist/gaia-ops/skills/gaia-verify/SKILL.md +77 -0
- package/dist/gaia-ops/skills/gaia-verify/reference.md +80 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +60 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/gmail-policy/SKILL.md +200 -0
- package/dist/gaia-ops/skills/gmail-policy/reference.md +150 -0
- package/dist/gaia-ops/skills/gmail-triage/SKILL.md +100 -0
- package/dist/gaia-ops/skills/gws-setup/SKILL.md +99 -0
- package/dist/gaia-ops/skills/gws-setup/reference.md +73 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +100 -0
- package/dist/gaia-ops/skills/memory-curation/SKILL.md +83 -0
- package/dist/gaia-ops/skills/memory-search/SKILL.md +88 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +160 -0
- package/dist/gaia-ops/skills/orchestrator-approval/reference.md +174 -0
- package/dist/gaia-ops/skills/pending-approvals/SKILL.md +72 -0
- package/dist/gaia-ops/skills/pending-approvals/reference.md +214 -0
- package/dist/gaia-ops/skills/readme-writing/SKILL.md +71 -0
- package/dist/gaia-ops/skills/readme-writing/reference.md +188 -0
- package/dist/gaia-ops/skills/reference.md +135 -0
- package/dist/gaia-ops/skills/request-approval/SKILL.md +140 -0
- package/dist/gaia-ops/skills/request-approval/examples.md +140 -0
- package/dist/gaia-ops/skills/request-approval/reference.md +57 -0
- package/dist/gaia-ops/skills/schedule-task/SKILL.md +64 -0
- package/dist/gaia-ops/skills/schedule-task/reference.md +233 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +141 -0
- package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +92 -0
- package/dist/gaia-ops/skills/skill-creation/reference.md +29 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +89 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/tools/__init__.py +9 -0
- package/dist/gaia-ops/tools/agentic-loop/decide-status.py +210 -0
- package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +106 -0
- package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +221 -0
- package/dist/gaia-ops/tools/context/README.md +132 -0
- package/dist/gaia-ops/tools/context/__init__.py +42 -0
- package/dist/gaia-ops/tools/context/_paths.py +20 -0
- package/dist/gaia-ops/tools/context/context_provider.py +721 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +342 -0
- package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
- package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
- package/dist/gaia-ops/tools/context/surface_router.py +278 -0
- package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
- package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
- package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
- package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
- package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
- package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
- package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
- package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
- package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +264 -0
- package/dist/gaia-ops/tools/memory/README.md +0 -0
- package/dist/gaia-ops/tools/memory/__init__.py +20 -0
- package/dist/gaia-ops/tools/memory/backfill_fts5.py +107 -0
- package/dist/gaia-ops/tools/memory/conflict_detector.py +295 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1210 -0
- package/dist/gaia-ops/tools/memory/git_invalidator.py +262 -0
- package/dist/gaia-ops/tools/memory/paths.py +102 -0
- package/dist/gaia-ops/tools/memory/scoring.py +193 -0
- package/dist/gaia-ops/tools/memory/search_store.py +360 -0
- package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
- package/dist/gaia-ops/tools/review/__init__.py +1 -0
- package/dist/gaia-ops/tools/review/review_engine.py +157 -0
- package/dist/gaia-ops/tools/scan/__init__.py +35 -0
- package/dist/gaia-ops/tools/scan/config.py +247 -0
- package/dist/gaia-ops/tools/scan/merge.py +212 -0
- package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
- package/dist/gaia-ops/tools/scan/registry.py +127 -0
- package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
- package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
- package/dist/gaia-ops/tools/scan/scanners/environment.py +349 -0
- package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
- package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
- package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
- package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
- package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
- package/dist/gaia-ops/tools/scan/setup.py +686 -0
- package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
- package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
- package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
- package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
- package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
- package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
- package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
- package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
- package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
- package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
- package/dist/gaia-ops/tools/scan/ui.py +624 -0
- package/dist/gaia-ops/tools/scan/verify.py +270 -0
- package/dist/gaia-ops/tools/scan/walk.py +118 -0
- package/dist/gaia-ops/tools/scan/workspace.py +85 -0
- package/dist/gaia-ops/tools/validation/README.md +244 -0
- package/dist/gaia-ops/tools/validation/__init__.py +17 -0
- package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
- package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
- package/dist/gaia-security/.claude-plugin/plugin.json +24 -0
- package/dist/gaia-security/README.md +90 -0
- package/dist/gaia-security/config/universal-rules.json +102 -0
- package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-security/hooks/adapters/base.py +219 -0
- package/dist/gaia-security/hooks/adapters/channel.py +17 -0
- package/dist/gaia-security/hooks/adapters/claude_code.py +1890 -0
- package/dist/gaia-security/hooks/adapters/types.py +194 -0
- package/dist/gaia-security/hooks/adapters/utils.py +25 -0
- package/dist/gaia-security/hooks/hooks.json +84 -0
- package/dist/gaia-security/hooks/modules/__init__.py +15 -0
- package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +120 -0
- package/dist/gaia-security/hooks/modules/agents/state_tracker.py +267 -0
- package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +611 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-security/hooks/modules/context/agentic_loop_detector.py +165 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +218 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +558 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +530 -0
- package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-security/hooks/modules/core/plugin_setup.py +577 -0
- package/dist/gaia-security/hooks/modules/core/state.py +179 -0
- package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +216 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +122 -0
- package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +120 -0
- package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +1638 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +222 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +595 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +181 -0
- package/dist/gaia-security/hooks/modules/security/composition_rules.py +547 -0
- package/dist/gaia-security/hooks/modules/security/flag_classifiers.py +873 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +1131 -0
- package/dist/gaia-security/hooks/modules/security/network_hosts.py +481 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/shell_unwrapper.py +165 -0
- package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-security/hooks/modules/session/pending_scanner.py +174 -0
- package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-security/hooks/modules/session/session_event_injector.py +160 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/session/session_registry.py +232 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +1008 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +231 -0
- package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-security/hooks/modules/tools/stage_decomposer.py +315 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +294 -0
- package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-security/hooks/post_tool_use.py +54 -0
- package/dist/gaia-security/hooks/pre_tool_use.py +413 -0
- package/dist/gaia-security/hooks/session_start.py +81 -0
- package/dist/gaia-security/hooks/stop_hook.py +82 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +246 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +100 -0
- package/hooks/adapters/__init__.py +52 -0
- package/hooks/adapters/base.py +219 -0
- package/hooks/adapters/channel.py +17 -0
- package/hooks/adapters/claude_code.py +1890 -0
- package/hooks/adapters/types.py +194 -0
- package/hooks/adapters/utils.py +25 -0
- package/hooks/elicitation_result.py +179 -0
- package/hooks/hooks.json +84 -0
- package/hooks/modules/README.md +189 -0
- package/hooks/modules/__init__.py +15 -0
- package/hooks/modules/agents/__init__.py +29 -0
- package/hooks/modules/agents/contract_validator.py +647 -0
- package/hooks/modules/agents/response_contract.py +496 -0
- package/hooks/modules/agents/skill_injection_verifier.py +120 -0
- package/hooks/modules/agents/state_tracker.py +267 -0
- package/hooks/modules/agents/task_info_builder.py +74 -0
- package/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/hooks/modules/agents/transcript_reader.py +152 -0
- package/hooks/modules/audit/__init__.py +28 -0
- package/hooks/modules/audit/event_detector.py +168 -0
- package/hooks/modules/audit/logger.py +131 -0
- package/hooks/modules/audit/metrics.py +134 -0
- package/hooks/modules/audit/workflow_auditor.py +611 -0
- package/hooks/modules/audit/workflow_recorder.py +296 -0
- package/hooks/modules/context/__init__.py +11 -0
- package/hooks/modules/context/agentic_loop_detector.py +165 -0
- package/hooks/modules/context/anchor_tracker.py +317 -0
- package/hooks/modules/context/compact_context_builder.py +218 -0
- package/hooks/modules/context/context_freshness.py +145 -0
- package/hooks/modules/context/context_injector.py +558 -0
- package/hooks/modules/context/context_writer.py +530 -0
- package/hooks/modules/context/contracts_loader.py +161 -0
- package/hooks/modules/core/__init__.py +40 -0
- package/hooks/modules/core/hook_entry.py +78 -0
- package/hooks/modules/core/paths.py +160 -0
- package/hooks/modules/core/plugin_mode.py +149 -0
- package/hooks/modules/core/plugin_setup.py +577 -0
- package/hooks/modules/core/state.py +179 -0
- package/hooks/modules/core/stdin.py +24 -0
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/evidence/__init__.py +34 -0
- package/hooks/modules/evidence/assertions.py +137 -0
- package/hooks/modules/evidence/index_writer.py +57 -0
- package/hooks/modules/evidence/loader.py +126 -0
- package/hooks/modules/evidence/runner.py +241 -0
- package/hooks/modules/memory/__init__.py +8 -0
- package/hooks/modules/memory/episode_writer.py +216 -0
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +122 -0
- package/hooks/modules/scanning/__init__.py +8 -0
- package/hooks/modules/scanning/scan_trigger.py +84 -0
- package/hooks/modules/security/__init__.py +120 -0
- package/hooks/modules/security/approval_cleanup.py +87 -0
- package/hooks/modules/security/approval_constants.py +23 -0
- package/hooks/modules/security/approval_grants.py +1638 -0
- package/hooks/modules/security/approval_messages.py +71 -0
- package/hooks/modules/security/approval_scopes.py +222 -0
- package/hooks/modules/security/blocked_commands.py +595 -0
- package/hooks/modules/security/blocked_message_formatter.py +87 -0
- package/hooks/modules/security/command_semantics.py +181 -0
- package/hooks/modules/security/composition_rules.py +547 -0
- package/hooks/modules/security/flag_classifiers.py +873 -0
- package/hooks/modules/security/gitops_validator.py +179 -0
- package/hooks/modules/security/mutative_verbs.py +1131 -0
- package/hooks/modules/security/network_hosts.py +481 -0
- package/hooks/modules/security/prompt_validator.py +40 -0
- package/hooks/modules/security/shell_unwrapper.py +165 -0
- package/hooks/modules/security/tiers.py +196 -0
- package/hooks/modules/session/__init__.py +10 -0
- package/hooks/modules/session/pending_scanner.py +174 -0
- package/hooks/modules/session/session_context_writer.py +100 -0
- package/hooks/modules/session/session_event_injector.py +160 -0
- package/hooks/modules/session/session_manager.py +31 -0
- package/hooks/modules/session/session_registry.py +232 -0
- package/hooks/modules/tools/__init__.py +29 -0
- package/hooks/modules/tools/bash_validator.py +1008 -0
- package/hooks/modules/tools/cloud_pipe_validator.py +231 -0
- package/hooks/modules/tools/hook_response.py +55 -0
- package/hooks/modules/tools/shell_parser.py +227 -0
- package/hooks/modules/tools/stage_decomposer.py +315 -0
- package/hooks/modules/tools/task_validator.py +294 -0
- package/hooks/modules/validation/__init__.py +23 -0
- package/hooks/modules/validation/commit_validator.py +380 -0
- package/hooks/post_compact.py +43 -0
- package/hooks/post_tool_use.py +54 -0
- package/hooks/pre_compact.py +60 -0
- package/hooks/pre_tool_use.py +413 -0
- package/hooks/session_start.py +81 -0
- package/hooks/stop_hook.py +82 -0
- package/hooks/subagent_start.py +71 -0
- package/hooks/subagent_stop.py +295 -0
- package/hooks/task_completed.py +70 -0
- package/hooks/user_prompt_submit.py +246 -0
- package/index.js +83 -0
- package/package.json +99 -0
- package/pyproject.toml +32 -0
- package/skills/README.md +154 -0
- package/skills/agent-protocol/SKILL.md +93 -0
- package/skills/agent-protocol/examples.md +223 -0
- package/skills/agent-response/SKILL.md +69 -0
- package/skills/agentic-loop/SKILL.md +80 -0
- package/skills/agentic-loop/reference.md +378 -0
- package/skills/blog-writing/SKILL.md +98 -0
- package/skills/blog-writing/reference.md +130 -0
- package/skills/brief-spec/SKILL.md +182 -0
- package/skills/command-execution/SKILL.md +64 -0
- package/skills/command-execution/reference.md +83 -0
- package/skills/context-updater/SKILL.md +87 -0
- package/skills/context-updater/examples.md +71 -0
- package/skills/developer-patterns/SKILL.md +50 -0
- package/skills/developer-patterns/reference.md +112 -0
- package/skills/execution/SKILL.md +99 -0
- package/skills/fast-queries/SKILL.md +43 -0
- package/skills/gaia-compact/SKILL.md +74 -0
- package/skills/gaia-patterns/SKILL.md +108 -0
- package/skills/gaia-patterns/reference.md +395 -0
- package/skills/gaia-planner/SKILL.md +37 -0
- package/skills/gaia-planner/reference.md +107 -0
- package/skills/gaia-release/SKILL.md +82 -0
- package/skills/gaia-release/reference.md +102 -0
- package/skills/gaia-self-check/SKILL.md +114 -0
- package/skills/gaia-self-check/reference.md +453 -0
- package/skills/gaia-verify/SKILL.md +77 -0
- package/skills/gaia-verify/reference.md +80 -0
- package/skills/git-conventions/SKILL.md +47 -0
- package/skills/gitops-patterns/SKILL.md +60 -0
- package/skills/gitops-patterns/reference.md +183 -0
- package/skills/gmail-policy/SKILL.md +200 -0
- package/skills/gmail-policy/reference.md +150 -0
- package/skills/gmail-triage/SKILL.md +100 -0
- package/skills/gws-setup/SKILL.md +99 -0
- package/skills/gws-setup/reference.md +73 -0
- package/skills/investigation/SKILL.md +100 -0
- package/skills/memory-curation/SKILL.md +83 -0
- package/skills/memory-search/SKILL.md +88 -0
- package/skills/orchestrator-approval/SKILL.md +160 -0
- package/skills/orchestrator-approval/reference.md +174 -0
- package/skills/pending-approvals/SKILL.md +72 -0
- package/skills/pending-approvals/reference.md +214 -0
- package/skills/readme-writing/SKILL.md +71 -0
- package/skills/readme-writing/reference.md +188 -0
- package/skills/reference.md +135 -0
- package/skills/request-approval/SKILL.md +140 -0
- package/skills/request-approval/examples.md +140 -0
- package/skills/request-approval/reference.md +57 -0
- package/skills/schedule-task/SKILL.md +64 -0
- package/skills/schedule-task/reference.md +233 -0
- package/skills/security-tiers/SKILL.md +141 -0
- package/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/skills/security-tiers/reference.md +39 -0
- package/skills/skill-creation/SKILL.md +92 -0
- package/skills/skill-creation/reference.md +29 -0
- package/skills/terraform-patterns/SKILL.md +89 -0
- package/skills/terraform-patterns/reference.md +93 -0
- package/templates/README.md +69 -0
- package/templates/managed-settings.template.json +43 -0
- package/tools/__init__.py +9 -0
- package/tools/agentic-loop/decide-status.py +210 -0
- package/tools/agentic-loop/parse-metric.py +106 -0
- package/tools/agentic-loop/record-iteration.py +221 -0
- package/tools/context/README.md +132 -0
- package/tools/context/__init__.py +42 -0
- package/tools/context/_paths.py +20 -0
- package/tools/context/context_provider.py +721 -0
- package/tools/context/context_section_reader.py +342 -0
- package/tools/context/deep_merge.py +159 -0
- package/tools/context/pending_updates.py +760 -0
- package/tools/context/surface_router.py +278 -0
- package/tools/fast-queries/README.md +65 -0
- package/tools/fast-queries/__init__.py +30 -0
- package/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/tools/fast-queries/run_triage.sh +59 -0
- package/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/tools/gaia_simulator/__init__.py +33 -0
- package/tools/gaia_simulator/cli.py +354 -0
- package/tools/gaia_simulator/extractor.py +457 -0
- package/tools/gaia_simulator/reporter.py +258 -0
- package/tools/gaia_simulator/routing_simulator.py +334 -0
- package/tools/gaia_simulator/runner.py +539 -0
- package/tools/gaia_simulator/skills_mapper.py +264 -0
- package/tools/memory/README.md +0 -0
- package/tools/memory/__init__.py +20 -0
- package/tools/memory/backfill_fts5.py +107 -0
- package/tools/memory/conflict_detector.py +295 -0
- package/tools/memory/episodic.py +1210 -0
- package/tools/memory/git_invalidator.py +262 -0
- package/tools/memory/paths.py +102 -0
- package/tools/memory/scoring.py +193 -0
- package/tools/memory/search_store.py +360 -0
- package/tools/persist_transcript_analysis.py +85 -0
- package/tools/review/__init__.py +1 -0
- package/tools/review/review_engine.py +157 -0
- package/tools/scan/__init__.py +35 -0
- package/tools/scan/config.py +247 -0
- package/tools/scan/merge.py +212 -0
- package/tools/scan/orchestrator.py +549 -0
- package/tools/scan/registry.py +127 -0
- package/tools/scan/scanners/__init__.py +18 -0
- package/tools/scan/scanners/base.py +137 -0
- package/tools/scan/scanners/environment.py +349 -0
- package/tools/scan/scanners/git.py +570 -0
- package/tools/scan/scanners/infrastructure.py +875 -0
- package/tools/scan/scanners/orchestration.py +600 -0
- package/tools/scan/scanners/stack.py +1085 -0
- package/tools/scan/scanners/tools.py +260 -0
- package/tools/scan/setup.py +686 -0
- package/tools/scan/tests/__init__.py +1 -0
- package/tools/scan/tests/conftest.py +796 -0
- package/tools/scan/tests/test_environment.py +323 -0
- package/tools/scan/tests/test_git.py +419 -0
- package/tools/scan/tests/test_infrastructure.py +382 -0
- package/tools/scan/tests/test_integration.py +920 -0
- package/tools/scan/tests/test_merge.py +269 -0
- package/tools/scan/tests/test_orchestration.py +304 -0
- package/tools/scan/tests/test_stack.py +604 -0
- package/tools/scan/tests/test_tools.py +349 -0
- package/tools/scan/ui.py +624 -0
- package/tools/scan/verify.py +270 -0
- package/tools/scan/walk.py +118 -0
- package/tools/scan/workspace.py +85 -0
- package/tools/validation/README.md +244 -0
- package/tools/validation/__init__.py +17 -0
- package/tools/validation/approval_gate.py +321 -0
- package/tools/validation/validate_skills.py +189 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
"""Core context injection subsystem for project agents.
|
|
2
|
+
|
|
3
|
+
Handles:
|
|
4
|
+
- build_project_context: builds context string for additionalContext injection
|
|
5
|
+
- check_pending_updates_threshold: warns when pending updates accumulate
|
|
6
|
+
- check_recent_critical_anomalies: surfaces critical anomalies from JSONL log
|
|
7
|
+
- consume_anomaly_flag: reads and deletes anomaly signal flags
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from ..core.paths import get_plugin_data_dir
|
|
20
|
+
from ..session.session_manager import get_or_create_session_id
|
|
21
|
+
from .anchor_tracker import extract_anchors, save_anchors
|
|
22
|
+
from .contracts_loader import build_context_update_reminder
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Inline explanations for every field the investigation brief can contain.
|
|
27
|
+
# Appended as YAML comments when the brief is rendered via _dict_to_yaml_annotated().
|
|
28
|
+
BRIEF_FIELD_DESCRIPTIONS: dict[str, str] = {
|
|
29
|
+
"goal": "the task as stated by the orchestrator",
|
|
30
|
+
"agent_role": "your relationship to this task: primary (owns it), cross_check (verify peer's work), adjacent (related surface), reconnaissance (explore)",
|
|
31
|
+
"primary_surface": "the surface that owns the fix or decision",
|
|
32
|
+
"active_surfaces": "all surfaces involved; drives cross_check_required",
|
|
33
|
+
"adjacent_surfaces": "surfaces that may be impacted but do not own the fix",
|
|
34
|
+
"dispatch_mode": "single_surface = one agent owns it; multi_surface = multiple agents coordinate",
|
|
35
|
+
"cross_check_required": "true when >1 active surface or this agent is not primary; fill CROSS_LAYER_IMPACTS",
|
|
36
|
+
"patterns_required": "true means load the domain skill for this surface before executing",
|
|
37
|
+
"contract_sections_to_anchor": "project-context sections to cite as evidence in your contract",
|
|
38
|
+
"required_checks": "mandatory verifications before declaring COMPLETE",
|
|
39
|
+
"evidence_required": "fields that must appear in your evidence_report json:contract block",
|
|
40
|
+
"consolidation_required": "true when cross_check_required; fill consolidation_report with ownership_assessment and conflicts",
|
|
41
|
+
"consolidation_fields": "fields required inside consolidation_report when consolidation_required is true",
|
|
42
|
+
"recommended_peer_agents": "agents to delegate to or coordinate with for non-primary surfaces",
|
|
43
|
+
"stop_conditions": "conditions under which further investigation adds no value",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _dict_to_yaml_annotated(d: dict, descriptions: dict[str, str], indent: int = 0) -> str:
|
|
48
|
+
"""Render a dict as YAML-like key-value pairs with inline description comments.
|
|
49
|
+
|
|
50
|
+
Works like _dict_to_yaml but appends ``# <description>`` after each
|
|
51
|
+
top-level scalar or list header when a matching entry exists in
|
|
52
|
+
*descriptions*. Nested structures and list items are rendered without
|
|
53
|
+
annotations to keep the output readable.
|
|
54
|
+
"""
|
|
55
|
+
lines = []
|
|
56
|
+
prefix = " " * indent
|
|
57
|
+
for key, value in d.items():
|
|
58
|
+
desc = descriptions.get(key, "") if indent == 0 else ""
|
|
59
|
+
comment = f" # {desc}" if desc else ""
|
|
60
|
+
if value is None or value == "" or value == [] or value == {}:
|
|
61
|
+
continue
|
|
62
|
+
if isinstance(value, dict):
|
|
63
|
+
lines.append(f"{prefix}{key}:{comment}")
|
|
64
|
+
lines.append(_dict_to_yaml(value, indent + 1))
|
|
65
|
+
elif isinstance(value, list):
|
|
66
|
+
lines.append(f"{prefix}{key}:{comment}")
|
|
67
|
+
for item in value:
|
|
68
|
+
if isinstance(item, dict):
|
|
69
|
+
item_lines = _dict_to_yaml(item, indent + 1).splitlines()
|
|
70
|
+
if item_lines:
|
|
71
|
+
first = item_lines[0].lstrip()
|
|
72
|
+
lines.append(f"{prefix} - {first}")
|
|
73
|
+
for il in item_lines[1:]:
|
|
74
|
+
lines.append(f"{prefix} {il}")
|
|
75
|
+
else:
|
|
76
|
+
lines.append(f"{prefix} - {item}")
|
|
77
|
+
else:
|
|
78
|
+
lines.append(f"{prefix}{key}: {value}{comment}")
|
|
79
|
+
return "\n".join(lines)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _find_python() -> str:
|
|
83
|
+
"""Return the Python 3 command name for this platform.
|
|
84
|
+
|
|
85
|
+
Tries ``python3`` first (Linux/macOS), then ``python`` (Windows).
|
|
86
|
+
Falls back to ``sys.executable`` (the current interpreter) as a
|
|
87
|
+
last resort -- this always works since hooks are already running
|
|
88
|
+
under Python.
|
|
89
|
+
"""
|
|
90
|
+
for cmd in ("python3", "python"):
|
|
91
|
+
if shutil.which(cmd):
|
|
92
|
+
return cmd
|
|
93
|
+
return sys.executable
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _dict_to_yaml(d, indent: int = 0) -> str:
|
|
97
|
+
"""Convert a dict to indented YAML-like key-value pairs (no external dependency).
|
|
98
|
+
|
|
99
|
+
- Nested dicts are indented by 2 spaces per level.
|
|
100
|
+
- Lists are rendered as markdown bullet lists (- item).
|
|
101
|
+
- Scalar values are rendered inline.
|
|
102
|
+
- None/empty values are skipped.
|
|
103
|
+
"""
|
|
104
|
+
lines = []
|
|
105
|
+
prefix = " " * indent
|
|
106
|
+
for key, value in d.items():
|
|
107
|
+
if value is None or value == "" or value == [] or value == {}:
|
|
108
|
+
continue
|
|
109
|
+
if isinstance(value, dict):
|
|
110
|
+
lines.append(f"{prefix}{key}:")
|
|
111
|
+
lines.append(_dict_to_yaml(value, indent + 1))
|
|
112
|
+
elif isinstance(value, list):
|
|
113
|
+
lines.append(f"{prefix}{key}:")
|
|
114
|
+
for item in value:
|
|
115
|
+
if isinstance(item, dict):
|
|
116
|
+
# Render first key inline, rest indented
|
|
117
|
+
item_lines = _dict_to_yaml(item, indent + 1).splitlines()
|
|
118
|
+
if item_lines:
|
|
119
|
+
first = item_lines[0].lstrip()
|
|
120
|
+
lines.append(f"{prefix} - {first}")
|
|
121
|
+
for il in item_lines[1:]:
|
|
122
|
+
lines.append(f"{prefix} {il}")
|
|
123
|
+
else:
|
|
124
|
+
lines.append(f"{prefix} - {item}")
|
|
125
|
+
else:
|
|
126
|
+
lines.append(f"{prefix}{key}: {value}")
|
|
127
|
+
return "\n".join(lines)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _prune_empty_values(data: dict) -> dict:
|
|
131
|
+
"""Drop keys with empty telemetry values while preserving False/0."""
|
|
132
|
+
pruned = {}
|
|
133
|
+
for key, value in data.items():
|
|
134
|
+
if value in (None, "", [], {}):
|
|
135
|
+
continue
|
|
136
|
+
pruned[key] = value
|
|
137
|
+
return pruned
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def build_context_telemetry_snapshot(context_payload: dict) -> dict:
|
|
141
|
+
"""Build a compact telemetry snapshot from injected context payload data."""
|
|
142
|
+
if not isinstance(context_payload, dict) or not context_payload:
|
|
143
|
+
return {}
|
|
144
|
+
|
|
145
|
+
project_knowledge = context_payload.get("project_knowledge") or {}
|
|
146
|
+
metadata = context_payload.get("metadata") or {}
|
|
147
|
+
surface_routing = context_payload.get("surface_routing") or {}
|
|
148
|
+
investigation_brief = context_payload.get("investigation_brief") or {}
|
|
149
|
+
write_permissions = context_payload.get("write_permissions") or {}
|
|
150
|
+
|
|
151
|
+
contract_sections = sorted(project_knowledge.keys())
|
|
152
|
+
readable_sections = sorted(write_permissions.get("readable_sections") or [])
|
|
153
|
+
writable_sections = sorted(write_permissions.get("writable_sections") or [])
|
|
154
|
+
|
|
155
|
+
return _prune_empty_values({
|
|
156
|
+
"contract_sections": contract_sections,
|
|
157
|
+
"contract_sections_count": len(contract_sections),
|
|
158
|
+
"metadata": _prune_empty_values({
|
|
159
|
+
"cloud_provider": metadata.get("cloud_provider"),
|
|
160
|
+
"contract_version": metadata.get("contract_version"),
|
|
161
|
+
"rules_count": metadata.get("rules_count"),
|
|
162
|
+
"historical_episodes_count": metadata.get("historical_episodes_count"),
|
|
163
|
+
"surface_routing_version": metadata.get("surface_routing_version"),
|
|
164
|
+
"active_surfaces_count": metadata.get("active_surfaces_count"),
|
|
165
|
+
"surface_routing_confidence": metadata.get("surface_routing_confidence"),
|
|
166
|
+
}),
|
|
167
|
+
"surface_routing": _prune_empty_values({
|
|
168
|
+
"primary_surface": surface_routing.get("primary_surface"),
|
|
169
|
+
"active_surfaces": sorted(surface_routing.get("active_surfaces") or []),
|
|
170
|
+
"dispatch_mode": surface_routing.get("dispatch_mode"),
|
|
171
|
+
"multi_surface": surface_routing.get("multi_surface"),
|
|
172
|
+
"recommended_agents": sorted(surface_routing.get("recommended_agents") or []),
|
|
173
|
+
}),
|
|
174
|
+
"investigation_brief": _prune_empty_values({
|
|
175
|
+
"agent_role": investigation_brief.get("agent_role"),
|
|
176
|
+
"primary_surface": investigation_brief.get("primary_surface"),
|
|
177
|
+
"adjacent_surfaces": sorted(investigation_brief.get("adjacent_surfaces") or []),
|
|
178
|
+
"cross_check_required": investigation_brief.get("cross_check_required"),
|
|
179
|
+
"consolidation_required": investigation_brief.get("consolidation_required"),
|
|
180
|
+
"required_checks_count": len(investigation_brief.get("required_checks") or []),
|
|
181
|
+
"evidence_required": sorted(investigation_brief.get("evidence_required") or []),
|
|
182
|
+
}),
|
|
183
|
+
"context_update_scope": _prune_empty_values({
|
|
184
|
+
"readable_sections": readable_sections,
|
|
185
|
+
"readable_sections_count": len(readable_sections),
|
|
186
|
+
"writable_sections": writable_sections,
|
|
187
|
+
"writable_sections_count": len(writable_sections),
|
|
188
|
+
}),
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def check_pending_updates_threshold() -> str:
|
|
193
|
+
"""
|
|
194
|
+
Check if pending updates count exceeds threshold and return warning text.
|
|
195
|
+
|
|
196
|
+
Returns warning string to inject into prompt, or empty string if below threshold.
|
|
197
|
+
Must NEVER block or slow down context injection (target: <50ms).
|
|
198
|
+
"""
|
|
199
|
+
try:
|
|
200
|
+
threshold = int(os.environ.get("PENDING_UPDATE_THRESHOLD", "10"))
|
|
201
|
+
|
|
202
|
+
# Fast path: try to read index directly (no module import)
|
|
203
|
+
index_path = Path(".claude/project-context/pending-updates/pending-index.json")
|
|
204
|
+
if not index_path.exists():
|
|
205
|
+
return ""
|
|
206
|
+
|
|
207
|
+
with open(index_path, 'r') as f:
|
|
208
|
+
index_data = json.load(f)
|
|
209
|
+
|
|
210
|
+
pending_count = index_data.get("pending_count", 0)
|
|
211
|
+
if pending_count < threshold:
|
|
212
|
+
return ""
|
|
213
|
+
|
|
214
|
+
logger.info(f"Pending updates threshold reached: {pending_count} >= {threshold}")
|
|
215
|
+
return (
|
|
216
|
+
f"\n# Pending Context Updates Warning\n"
|
|
217
|
+
f"There are {pending_count} pending context update suggestions awaiting review. "
|
|
218
|
+
f"Run `gaia-review` or `python3 tools/review/review_engine.py list` to review them.\n\n"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.debug(f"Pending updates check failed (non-fatal): {e}")
|
|
223
|
+
return ""
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def check_recent_critical_anomalies() -> str:
|
|
227
|
+
"""Check anomalies.jsonl for recent critical anomalies and return a summary.
|
|
228
|
+
|
|
229
|
+
Scans the last 20 lines of the anomaly log for critical-severity entries
|
|
230
|
+
from the past hour. Returns a short warning string suitable for context
|
|
231
|
+
injection, or empty string if nothing noteworthy is found.
|
|
232
|
+
|
|
233
|
+
This is intentionally lightweight: reads only the tail of the file and
|
|
234
|
+
returns at most a one-line count + type summary.
|
|
235
|
+
"""
|
|
236
|
+
anomaly_log = (
|
|
237
|
+
get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "anomalies.jsonl"
|
|
238
|
+
)
|
|
239
|
+
if not anomaly_log.exists():
|
|
240
|
+
return ""
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
# Read only the tail (last 20 lines) for speed
|
|
244
|
+
lines = anomaly_log.read_text().splitlines()[-20:]
|
|
245
|
+
one_hour_ago = datetime.now().timestamp() - 3600
|
|
246
|
+
critical_types: list[str] = []
|
|
247
|
+
|
|
248
|
+
for line in lines:
|
|
249
|
+
if not line.strip():
|
|
250
|
+
continue
|
|
251
|
+
try:
|
|
252
|
+
entry = json.loads(line)
|
|
253
|
+
except json.JSONDecodeError:
|
|
254
|
+
continue
|
|
255
|
+
ts = entry.get("timestamp", "")
|
|
256
|
+
if ts:
|
|
257
|
+
try:
|
|
258
|
+
entry_time = datetime.fromisoformat(ts).timestamp()
|
|
259
|
+
except (ValueError, TypeError):
|
|
260
|
+
continue
|
|
261
|
+
if entry_time < one_hour_ago:
|
|
262
|
+
continue
|
|
263
|
+
for anomaly in entry.get("anomalies", []):
|
|
264
|
+
if anomaly.get("severity") == "critical":
|
|
265
|
+
critical_types.append(anomaly.get("type", "unknown"))
|
|
266
|
+
|
|
267
|
+
if not critical_types:
|
|
268
|
+
return ""
|
|
269
|
+
|
|
270
|
+
# Deduplicate and summarize
|
|
271
|
+
unique_types = sorted(set(critical_types))
|
|
272
|
+
return (
|
|
273
|
+
f"\n# Recent Critical Anomalies\n"
|
|
274
|
+
f"{len(critical_types)} critical anomaly(ies) in the last hour "
|
|
275
|
+
f"(types: {', '.join(unique_types)}). "
|
|
276
|
+
f"Consider investigating with /gaia.\n"
|
|
277
|
+
)
|
|
278
|
+
except Exception as e:
|
|
279
|
+
logger.debug(f"Critical anomaly check failed (non-fatal): {e}")
|
|
280
|
+
return ""
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def consume_anomaly_flag(enriched_prompt: str) -> str:
|
|
284
|
+
"""Read and delete the needs_analysis.flag if it exists, appending a warning.
|
|
285
|
+
|
|
286
|
+
The flag is created by subagent_stop.py when workflow anomalies are
|
|
287
|
+
detected. Reading it once and deleting ensures the warning is shown
|
|
288
|
+
exactly once. Must not slow down context injection -- returns
|
|
289
|
+
immediately if the file does not exist.
|
|
290
|
+
|
|
291
|
+
TTL enforcement: flags older than 1 hour (by created_at or file mtime)
|
|
292
|
+
are auto-expired and deleted without injecting a warning.
|
|
293
|
+
"""
|
|
294
|
+
flag_path = get_plugin_data_dir() / "project-context" / "workflow-episodic-memory" / "signals" / "needs_analysis.flag"
|
|
295
|
+
if not flag_path.exists():
|
|
296
|
+
return enriched_prompt
|
|
297
|
+
try:
|
|
298
|
+
signal_data = json.loads(flag_path.read_text())
|
|
299
|
+
|
|
300
|
+
# TTL check: auto-expire flags older than ttl_hours (default 1 hour)
|
|
301
|
+
ttl_hours = signal_data.get("ttl_hours", 1)
|
|
302
|
+
ttl_seconds = ttl_hours * 3600
|
|
303
|
+
created_at = signal_data.get("created_at") or signal_data.get("timestamp")
|
|
304
|
+
if created_at:
|
|
305
|
+
created_dt = datetime.fromisoformat(created_at)
|
|
306
|
+
age_seconds = (datetime.now() - created_dt).total_seconds()
|
|
307
|
+
if age_seconds > ttl_seconds:
|
|
308
|
+
flag_path.unlink()
|
|
309
|
+
logger.info(
|
|
310
|
+
"Auto-expired anomaly flag (age: %.0fs, ttl: %ds)",
|
|
311
|
+
age_seconds, ttl_seconds,
|
|
312
|
+
)
|
|
313
|
+
return enriched_prompt
|
|
314
|
+
else:
|
|
315
|
+
# Fallback: check file modification time
|
|
316
|
+
mtime = flag_path.stat().st_mtime
|
|
317
|
+
age_seconds = datetime.now().timestamp() - mtime
|
|
318
|
+
if age_seconds > ttl_seconds:
|
|
319
|
+
flag_path.unlink()
|
|
320
|
+
logger.info(
|
|
321
|
+
"Auto-expired anomaly flag by mtime (age: %.0fs, ttl: %ds)",
|
|
322
|
+
age_seconds, ttl_seconds,
|
|
323
|
+
)
|
|
324
|
+
return enriched_prompt
|
|
325
|
+
|
|
326
|
+
anomalies = signal_data.get("anomalies", [])
|
|
327
|
+
summary = "; ".join(a.get("message", "") for a in anomalies if a.get("message"))
|
|
328
|
+
if summary:
|
|
329
|
+
enriched_prompt += (
|
|
330
|
+
f"\n# Anomaly Alert\n"
|
|
331
|
+
f"Recent anomalies detected: {summary}. "
|
|
332
|
+
f"Consider investigating with /gaia.\n"
|
|
333
|
+
)
|
|
334
|
+
flag_path.unlink()
|
|
335
|
+
logger.info("Consumed anomaly flag and injected warning")
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.debug(f"Failed to consume anomaly flag (non-fatal): {e}")
|
|
338
|
+
return enriched_prompt
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def build_project_context(
|
|
342
|
+
parameters: dict,
|
|
343
|
+
project_agents: list,
|
|
344
|
+
hooks_dir: Path = None,
|
|
345
|
+
) -> tuple:
|
|
346
|
+
"""
|
|
347
|
+
Build project context string for a project agent without mutating parameters.
|
|
348
|
+
|
|
349
|
+
Returns the context string suitable for additionalContext injection, plus a
|
|
350
|
+
telemetry snapshot. Does NOT modify parameters in any way.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
parameters: Task tool parameters (read-only).
|
|
354
|
+
project_agents: List of valid project agent names.
|
|
355
|
+
hooks_dir: Path to the hooks directory (for fallback paths).
|
|
356
|
+
Defaults to Path(__file__).parent.parent.parent if None.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
(context_string, telemetry_snapshot) or (None, {}) if no context to inject.
|
|
360
|
+
"""
|
|
361
|
+
if hooks_dir is None:
|
|
362
|
+
hooks_dir = Path(__file__).parent.parent.parent
|
|
363
|
+
|
|
364
|
+
subagent_type = parameters.get("subagent_type", "")
|
|
365
|
+
|
|
366
|
+
# Only inject for project agents (not for generic agents like Explore, general-purpose, etc.)
|
|
367
|
+
if subagent_type not in project_agents:
|
|
368
|
+
logger.debug(f"Skipping context injection for non-project agent: {subagent_type}")
|
|
369
|
+
return None, {}
|
|
370
|
+
|
|
371
|
+
prompt = parameters.get("prompt", "")
|
|
372
|
+
if not prompt:
|
|
373
|
+
logger.warning(f"No prompt provided for {subagent_type}, skipping context injection")
|
|
374
|
+
return None, {}
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
# Find context_provider.py
|
|
378
|
+
context_provider_paths = [
|
|
379
|
+
hooks_dir.parent / "tools" / "context" / "context_provider.py", # plugin root (works in both modes)
|
|
380
|
+
Path(".claude/tools/context/context_provider.py"), # npm symlink fallback
|
|
381
|
+
]
|
|
382
|
+
|
|
383
|
+
context_provider = None
|
|
384
|
+
for path in context_provider_paths:
|
|
385
|
+
if path.exists():
|
|
386
|
+
context_provider = path
|
|
387
|
+
break
|
|
388
|
+
|
|
389
|
+
if not context_provider:
|
|
390
|
+
logger.warning("context_provider.py not found, skipping context injection")
|
|
391
|
+
return None, {}
|
|
392
|
+
|
|
393
|
+
# Execute context_provider.py to get filtered context
|
|
394
|
+
logger.info(f"Building context for {subagent_type}...")
|
|
395
|
+
result = subprocess.run(
|
|
396
|
+
[_find_python(), str(context_provider), subagent_type, prompt],
|
|
397
|
+
capture_output=True,
|
|
398
|
+
text=True,
|
|
399
|
+
timeout=15,
|
|
400
|
+
cwd=os.getcwd()
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
if result.returncode != 0:
|
|
404
|
+
logger.error(f"context_provider.py failed: {result.stderr}")
|
|
405
|
+
return None, {}
|
|
406
|
+
|
|
407
|
+
# Parse context JSON
|
|
408
|
+
try:
|
|
409
|
+
context_payload = json.loads(result.stdout)
|
|
410
|
+
except json.JSONDecodeError as e:
|
|
411
|
+
logger.error(f"Failed to parse context JSON: {e}")
|
|
412
|
+
return None, {}
|
|
413
|
+
|
|
414
|
+
# Extract and save context anchors for hit tracking
|
|
415
|
+
try:
|
|
416
|
+
anchors = extract_anchors(context_payload)
|
|
417
|
+
if anchors:
|
|
418
|
+
session_id = get_or_create_session_id()
|
|
419
|
+
save_anchors(session_id, subagent_type, anchors)
|
|
420
|
+
logger.debug(
|
|
421
|
+
"Saved %d context anchors for %s", len(anchors), subagent_type,
|
|
422
|
+
)
|
|
423
|
+
except Exception as exc:
|
|
424
|
+
logger.debug("Anchor extraction failed (non-fatal): %s", exc)
|
|
425
|
+
|
|
426
|
+
# Check pending update count (non-blocking, fast path)
|
|
427
|
+
pending_warning = check_pending_updates_threshold()
|
|
428
|
+
|
|
429
|
+
# Build context update reminder for empty writable sections
|
|
430
|
+
update_reminder = build_context_update_reminder(
|
|
431
|
+
subagent_type, project_agents, hooks_dir
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Build context sections from payload
|
|
435
|
+
project_knowledge = context_payload.get("project_knowledge", {})
|
|
436
|
+
write_perms = context_payload.get("write_permissions", {})
|
|
437
|
+
investigation_brief = context_payload.get("investigation_brief", {})
|
|
438
|
+
rules = context_payload.get("rules", {})
|
|
439
|
+
surface_routing_data = context_payload.get("surface_routing", {})
|
|
440
|
+
metadata = context_payload.get("metadata", {})
|
|
441
|
+
historical = context_payload.get("historical_context", {})
|
|
442
|
+
|
|
443
|
+
# Extract memory_index from historical before JSON rendering to avoid duplication
|
|
444
|
+
memory_index_text = historical.pop("memory_index", "") if historical else ""
|
|
445
|
+
memory_index_section = f"\n### Memory Index\n\n{memory_index_text}\n" if memory_index_text else ""
|
|
446
|
+
|
|
447
|
+
# Optional sections
|
|
448
|
+
rules_section = f"\n## Rules\n\n{json.dumps(rules, indent=2)}\n" if rules.get("universal") or rules.get("agent_specific") else ""
|
|
449
|
+
routing_section = f"\n## Surface Routing\n\n{json.dumps(surface_routing_data, indent=2)}\n" if surface_routing_data else ""
|
|
450
|
+
metadata_section = f"\n## Metadata\n\n{json.dumps(metadata, indent=2)}\n" if metadata else ""
|
|
451
|
+
historical_section = f"\n## Historical Context\n\n{json.dumps(historical, indent=2)}\n" if historical else ""
|
|
452
|
+
|
|
453
|
+
# Build Context Orientation header listing which sections are present
|
|
454
|
+
orientation_lines = ["# Context Orientation\n"]
|
|
455
|
+
orientation_lines.append("Sections present in this payload:\n")
|
|
456
|
+
if project_knowledge:
|
|
457
|
+
orientation_lines.append("- **Project Context** -- structured knowledge about the current project; guides scope and conventions")
|
|
458
|
+
if rules_section:
|
|
459
|
+
orientation_lines.append("- **Rules** -- universal and agent-specific constraints that override default behavior")
|
|
460
|
+
if routing_section:
|
|
461
|
+
orientation_lines.append("- **Surface Routing** -- intent-to-agent mapping; use when delegating or checking ownership")
|
|
462
|
+
if investigation_brief:
|
|
463
|
+
orientation_lines.append("- **Brief** -- goal, acceptance criteria, and scope for the current task")
|
|
464
|
+
if write_perms:
|
|
465
|
+
orientation_lines.append("- **Permissions** -- which context sections are writable vs readable; required before emitting CONTEXT_UPDATE")
|
|
466
|
+
if memory_index_section:
|
|
467
|
+
orientation_lines.append("- **Memory Index** -- ranked memory documents relevant to this session; read high-score entries first")
|
|
468
|
+
if historical_section:
|
|
469
|
+
orientation_lines.append("- **Historical Context** -- past episodes and learned patterns; consult before repeating prior work")
|
|
470
|
+
if metadata_section:
|
|
471
|
+
orientation_lines.append("- **Metadata** -- session and environment metadata; use for debugging and traceability")
|
|
472
|
+
orientation_section = "\n".join(orientation_lines) + "\n"
|
|
473
|
+
|
|
474
|
+
# Save context_payload to disk for downstream hooks (SubagentStop)
|
|
475
|
+
try:
|
|
476
|
+
payload_dir = Path(os.environ.get("TMPDIR", "/tmp")) / "gaia-context-payloads"
|
|
477
|
+
payload_dir.mkdir(parents=True, exist_ok=True)
|
|
478
|
+
agent_id = parameters.get("_agent_id", "") or subagent_type
|
|
479
|
+
payload_path = payload_dir / f"{agent_id}.json"
|
|
480
|
+
payload_path.write_text(json.dumps(context_payload, separators=(',', ':')))
|
|
481
|
+
logger.debug(f"Context payload saved to {payload_path}")
|
|
482
|
+
except Exception as exc:
|
|
483
|
+
logger.debug(f"Failed to save context payload to disk (non-fatal): {exc}")
|
|
484
|
+
|
|
485
|
+
# Build brief as YAML/Markdown-KV with inline field descriptions
|
|
486
|
+
brief_mkv = _dict_to_yaml_annotated(investigation_brief, BRIEF_FIELD_DESCRIPTIONS) if investigation_brief else ""
|
|
487
|
+
|
|
488
|
+
# Build write permissions as YAML/Markdown-KV
|
|
489
|
+
write_perms_dict = {
|
|
490
|
+
"writable": write_perms.get("writable_sections", []),
|
|
491
|
+
"readable": write_perms.get("readable_sections", []),
|
|
492
|
+
"context_update_required": [s for s in write_perms.get("writable_sections", [])
|
|
493
|
+
if not project_knowledge.get(s)],
|
|
494
|
+
}
|
|
495
|
+
write_perms_mkv = _dict_to_yaml(write_perms_dict)
|
|
496
|
+
|
|
497
|
+
context_string = f"""{orientation_section}{rules_section}
|
|
498
|
+
# Project Context
|
|
499
|
+
|
|
500
|
+
{_dict_to_yaml(project_knowledge)}
|
|
501
|
+
|
|
502
|
+
# Routing
|
|
503
|
+
{routing_section}
|
|
504
|
+
# Brief
|
|
505
|
+
|
|
506
|
+
{brief_mkv}
|
|
507
|
+
|
|
508
|
+
# Permissions
|
|
509
|
+
|
|
510
|
+
{write_perms_mkv}
|
|
511
|
+
{memory_index_section}{pending_warning}{update_reminder}{metadata_section}{historical_section}"""
|
|
512
|
+
|
|
513
|
+
# Append anomaly signal flag (consume once)
|
|
514
|
+
context_string = consume_anomaly_flag(context_string)
|
|
515
|
+
|
|
516
|
+
# Surface recent critical anomalies from the JSONL log
|
|
517
|
+
critical_summary = check_recent_critical_anomalies()
|
|
518
|
+
if critical_summary:
|
|
519
|
+
context_string += critical_summary
|
|
520
|
+
|
|
521
|
+
# Inject recent operational events (non-blocking)
|
|
522
|
+
try:
|
|
523
|
+
from ..events.event_writer import read_events
|
|
524
|
+
recent = read_events(hours=24, limit=20)
|
|
525
|
+
if recent:
|
|
526
|
+
lines = ["\n# Recent Events (last 24h)"]
|
|
527
|
+
for evt in recent:
|
|
528
|
+
ts_short = evt.get("ts", "")[:16]
|
|
529
|
+
etype = evt.get("type", "")
|
|
530
|
+
agent_name = evt.get("agent", "")
|
|
531
|
+
result_str = evt.get("result", "")
|
|
532
|
+
label = f"{agent_name}: " if agent_name else ""
|
|
533
|
+
lines.append(f"- [{ts_short}] {etype}: {label}{result_str}")
|
|
534
|
+
context_string += "\n".join(lines) + "\n"
|
|
535
|
+
except Exception as exc:
|
|
536
|
+
logger.debug("Event context injection failed (non-fatal): %s", exc)
|
|
537
|
+
|
|
538
|
+
# Build telemetry snapshot
|
|
539
|
+
telemetry = build_context_telemetry_snapshot(context_payload)
|
|
540
|
+
|
|
541
|
+
sections_count = len(context_payload.get("project_knowledge", {}))
|
|
542
|
+
rules_count = context_payload.get("metadata", {}).get("rules_count", 0)
|
|
543
|
+
|
|
544
|
+
logger.info(
|
|
545
|
+
f"Context built for {subagent_type} "
|
|
546
|
+
f"(sections={sections_count}, rules={rules_count})"
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
return context_string, telemetry
|
|
550
|
+
|
|
551
|
+
except subprocess.TimeoutExpired:
|
|
552
|
+
logger.error("context_provider.py timed out (15s)")
|
|
553
|
+
return None, {}
|
|
554
|
+
except Exception as e:
|
|
555
|
+
logger.error(f"Error building context: {e}", exc_info=True)
|
|
556
|
+
return None, {}
|
|
557
|
+
|
|
558
|
+
|