@trac3er/oh-my-god 2.1.0 → 2.1.4
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/.agents/skills/omg/AGENTS.fragment.md +2 -2
- package/.agents/skills/omg/algorithms/SKILL.md +1 -1
- package/.agents/skills/omg/algorithms/openai.yaml +1 -1
- package/.agents/skills/omg/api-twin/SKILL.md +1 -1
- package/.agents/skills/omg/api-twin/openai.yaml +1 -1
- package/.agents/skills/omg/claim-judge/SKILL.md +1 -1
- package/.agents/skills/omg/claim-judge/openai.yaml +1 -1
- package/.agents/skills/omg/codex-rules.md +1 -1
- package/.agents/skills/omg/control-plane/SKILL.md +1 -1
- package/.agents/skills/omg/control-plane/openai.yaml +1 -1
- package/.agents/skills/omg/data-lineage/SKILL.md +1 -1
- package/.agents/skills/omg/data-lineage/openai.yaml +1 -1
- package/.agents/skills/omg/delta-classifier/SKILL.md +1 -1
- package/.agents/skills/omg/delta-classifier/openai.yaml +1 -1
- package/.agents/skills/omg/eval-gate/SKILL.md +1 -1
- package/.agents/skills/omg/eval-gate/openai.yaml +1 -1
- package/.agents/skills/omg/health/SKILL.md +1 -1
- package/.agents/skills/omg/health/openai.yaml +1 -1
- package/.agents/skills/omg/hook-governor/SKILL.md +1 -1
- package/.agents/skills/omg/hook-governor/openai.yaml +1 -1
- package/.agents/skills/omg/incident-replay/SKILL.md +1 -1
- package/.agents/skills/omg/incident-replay/openai.yaml +1 -1
- package/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
- package/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
- package/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
- package/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
- package/.agents/skills/omg/plan-council/SKILL.md +1 -1
- package/.agents/skills/omg/plan-council/openai.yaml +1 -1
- package/.agents/skills/omg/preflight/SKILL.md +1 -1
- package/.agents/skills/omg/preflight/openai.yaml +1 -1
- package/.agents/skills/omg/proof-gate/SKILL.md +1 -1
- package/.agents/skills/omg/proof-gate/openai.yaml +1 -1
- package/.agents/skills/omg/remote-supervisor/SKILL.md +1 -1
- package/.agents/skills/omg/remote-supervisor/openai.yaml +1 -1
- package/.agents/skills/omg/robotics/SKILL.md +1 -1
- package/.agents/skills/omg/robotics/openai.yaml +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
- package/.agents/skills/omg/security-check/SKILL.md +1 -1
- package/.agents/skills/omg/security-check/openai.yaml +1 -1
- package/.agents/skills/omg/test-intent-lock/SKILL.md +1 -1
- package/.agents/skills/omg/test-intent-lock/openai.yaml +1 -1
- package/.agents/skills/omg/tracebank/SKILL.md +1 -1
- package/.agents/skills/omg/tracebank/openai.yaml +1 -1
- package/.agents/skills/omg/vision/SKILL.md +1 -1
- package/.agents/skills/omg/vision/openai.yaml +1 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +16 -0
- package/CLI-ADAPTER-MAP.md +3 -3
- package/OMG-setup.sh +114 -1
- package/OMG_COMPAT_CONTRACT.md +1 -1
- package/README.md +2 -1
- package/artifacts/release/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/settings.json +3 -3
- package/artifacts/release/dist/enterprise/manifest.json +30 -40
- package/artifacts/release/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/public/bundle/settings.json +3 -3
- package/artifacts/release/dist/public/manifest.json +30 -40
- package/artifacts/release/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/health.yaml +1 -1
- package/artifacts/release/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/settings.json +3 -3
- package/build/lib/commands/OMG:profile-review.md +43 -0
- package/build/lib/commands/OMG:validate.md +59 -0
- package/build/lib/control_plane/service.py +40 -1
- package/build/lib/hooks/_agent_registry.py +2 -0
- package/build/lib/hooks/firewall.py +263 -4
- package/build/lib/hooks/intentgate-keyword-detector.py +15 -3
- package/build/lib/hooks/policy_engine.py +77 -0
- package/build/lib/hooks/prompt-enhancer.py +146 -0
- package/build/lib/hooks/session-end-capture.py +374 -6
- package/build/lib/hooks/session-start.py +13 -35
- package/build/lib/hooks/setup_wizard.py +333 -1
- package/build/lib/hooks/shadow_manager.py +111 -2
- package/build/lib/hooks/stop_dispatcher.py +108 -4
- package/build/lib/plugins/advanced/plugin.json +26 -9
- package/build/lib/plugins/core/plugin.json +44 -11
- package/build/lib/registry/bundles/algorithms.yaml +1 -1
- package/build/lib/registry/bundles/api-twin.yaml +1 -1
- package/build/lib/registry/bundles/claim-judge.yaml +1 -1
- package/build/lib/registry/bundles/control-plane.yaml +1 -1
- package/build/lib/registry/bundles/data-lineage.yaml +1 -1
- package/build/lib/registry/bundles/delta-classifier.yaml +1 -1
- package/build/lib/registry/bundles/eval-gate.yaml +1 -1
- package/build/lib/registry/bundles/health.yaml +1 -1
- package/build/lib/registry/bundles/hook-governor.yaml +1 -1
- package/build/lib/registry/bundles/incident-replay.yaml +1 -1
- package/build/lib/registry/bundles/lsp-pack.yaml +1 -1
- package/build/lib/registry/bundles/mcp-fabric.yaml +1 -1
- package/build/lib/registry/bundles/plan-council.yaml +1 -1
- package/build/lib/registry/bundles/preflight.yaml +1 -1
- package/build/lib/registry/bundles/proof-gate.yaml +1 -1
- package/build/lib/registry/bundles/remote-supervisor.yaml +1 -1
- package/build/lib/registry/bundles/robotics.yaml +1 -1
- package/build/lib/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/build/lib/registry/bundles/security-check.yaml +1 -1
- package/build/lib/registry/bundles/test-intent-lock.yaml +1 -1
- package/build/lib/registry/bundles/tracebank.yaml +1 -1
- package/build/lib/registry/bundles/vision.yaml +1 -1
- package/build/lib/registry/omg-capability.schema.json +1 -1
- package/build/lib/runtime/adoption.py +1 -1
- package/build/lib/runtime/background_verification.py +62 -0
- package/build/lib/runtime/claim_judge.py +165 -5
- package/build/lib/runtime/compat.py +15 -1
- package/build/lib/runtime/context_engine.py +261 -4
- package/build/lib/runtime/contract_compiler.py +370 -93
- package/build/lib/runtime/defense_state.py +79 -8
- package/build/lib/runtime/delta_classifier.py +80 -2
- package/build/lib/runtime/evidence_query.py +56 -4
- package/build/lib/runtime/evidence_registry.py +16 -0
- package/build/lib/runtime/evidence_requirements.py +47 -0
- package/build/lib/runtime/forge_agents.py +179 -9
- package/build/lib/runtime/forge_contracts.py +208 -2
- package/build/lib/runtime/interaction_journal.py +318 -42
- package/build/lib/runtime/memory_store.py +134 -1
- package/build/lib/runtime/mutation_gate.py +57 -11
- package/build/lib/runtime/omg_compat_contract_snapshot.json +2 -2
- package/build/lib/runtime/omg_mcp_server.py +14 -4
- package/build/lib/runtime/preflight.py +14 -0
- package/build/lib/runtime/profile_io.py +294 -0
- package/build/lib/runtime/proof_gate.py +122 -7
- package/build/lib/runtime/release_run_coordinator.py +339 -0
- package/build/lib/runtime/release_surfaces.py +243 -0
- package/build/lib/runtime/rollback_manifest.py +136 -0
- package/build/lib/runtime/router_critics.py +50 -0
- package/build/lib/runtime/runtime_contracts.py +137 -0
- package/build/lib/runtime/security_check.py +16 -0
- package/build/lib/runtime/session_health.py +60 -16
- package/build/lib/runtime/team_router.py +156 -13
- package/build/lib/runtime/test_intent_lock.py +102 -0
- package/build/lib/runtime/tool_plan_gate.py +249 -24
- package/build/lib/runtime/validate.py +194 -0
- package/build/lib/runtime/verification_controller.py +52 -2
- package/commands/OMG:diagnose-plugins.md +33 -0
- package/commands/OMG:profile-review.md +43 -0
- package/commands/OMG:setup.md +4 -1
- package/commands/OMG:validate.md +59 -0
- package/control_plane/service.py +40 -1
- package/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/enterprise/bundle/settings.json +3 -3
- package/dist/enterprise/manifest.json +30 -40
- package/dist/oh_my_god-2.1.4-py3-none-any.whl +0 -0
- package/dist/oh_my_god-2.1.4.tar.gz +0 -0
- package/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/public/bundle/settings.json +3 -3
- package/dist/public/manifest.json +30 -40
- package/docs/install/opencode.md +30 -0
- package/docs/proof.md +11 -0
- package/docs/release-checklist.md +14 -1
- package/hooks/_agent_registry.py +2 -0
- package/hooks/firewall.py +263 -4
- package/hooks/intentgate-keyword-detector.py +15 -3
- package/hooks/policy_engine.py +77 -0
- package/hooks/prompt-enhancer.py +146 -0
- package/hooks/session-end-capture.py +374 -6
- package/hooks/session-start.py +13 -35
- package/hooks/setup_wizard.py +342 -2
- package/hooks/shadow_manager.py +111 -2
- package/hooks/stop_dispatcher.py +108 -4
- package/hud/omg-hud.mjs +30 -6
- package/lab/pipeline.py +197 -26
- package/package.json +1 -1
- package/plugins/advanced/plugin.json +26 -9
- package/plugins/core/plugin.json +51 -12
- package/pyproject.toml +10 -2
- package/registry/bundles/algorithms.yaml +1 -1
- package/registry/bundles/api-twin.yaml +1 -1
- package/registry/bundles/claim-judge.yaml +1 -1
- package/registry/bundles/control-plane.yaml +1 -1
- package/registry/bundles/data-lineage.yaml +1 -1
- package/registry/bundles/delta-classifier.yaml +1 -1
- package/registry/bundles/eval-gate.yaml +1 -1
- package/registry/bundles/health.yaml +1 -1
- package/registry/bundles/hook-governor.yaml +1 -1
- package/registry/bundles/incident-replay.yaml +1 -1
- package/registry/bundles/lsp-pack.yaml +1 -1
- package/registry/bundles/mcp-fabric.yaml +1 -1
- package/registry/bundles/plan-council.yaml +1 -1
- package/registry/bundles/preflight.yaml +1 -1
- package/registry/bundles/proof-gate.yaml +1 -1
- package/registry/bundles/remote-supervisor.yaml +1 -1
- package/registry/bundles/robotics.yaml +1 -1
- package/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/registry/bundles/security-check.yaml +1 -1
- package/registry/bundles/test-intent-lock.yaml +1 -1
- package/registry/bundles/tracebank.yaml +1 -1
- package/registry/bundles/vision.yaml +1 -1
- package/registry/omg-capability.schema.json +1 -1
- package/runtime/adoption.py +7 -5
- package/runtime/background_verification.py +62 -0
- package/runtime/claim_judge.py +165 -5
- package/runtime/compat.py +48 -1
- package/runtime/context_engine.py +261 -4
- package/runtime/contract_compiler.py +370 -93
- package/runtime/defense_state.py +79 -8
- package/runtime/delta_classifier.py +80 -2
- package/runtime/evidence_query.py +56 -4
- package/runtime/evidence_registry.py +16 -0
- package/runtime/evidence_requirements.py +47 -0
- package/runtime/forge_agents.py +179 -9
- package/runtime/forge_contracts.py +208 -2
- package/runtime/hook_governor.py +19 -1
- package/runtime/interaction_journal.py +318 -42
- package/runtime/memory_store.py +134 -1
- package/runtime/mutation_gate.py +57 -11
- package/runtime/omg_compat_contract_snapshot.json +2 -2
- package/runtime/omg_mcp_server.py +14 -4
- package/runtime/plugin_diagnostics.py +248 -0
- package/runtime/plugin_interop.py +1007 -0
- package/runtime/preflight.py +14 -0
- package/runtime/profile_io.py +294 -0
- package/runtime/proof_gate.py +122 -7
- package/runtime/providers/opencode_provider.py +94 -0
- package/runtime/release_run_coordinator.py +339 -0
- package/runtime/release_surfaces.py +243 -0
- package/runtime/rollback_manifest.py +136 -0
- package/runtime/router_critics.py +50 -0
- package/runtime/runtime_contracts.py +137 -0
- package/runtime/security_check.py +16 -0
- package/runtime/session_health.py +60 -16
- package/runtime/team_router.py +156 -13
- package/runtime/test_intent_lock.py +102 -0
- package/runtime/tool_plan_gate.py +218 -19
- package/runtime/validate.py +289 -0
- package/runtime/verification_controller.py +52 -2
- package/scripts/omg.py +306 -3
- package/scripts/prepare-release-proof-fixtures.py +191 -7
- package/scripts/print-canonical-version.py +80 -0
- package/scripts/sync-release-identity.py +476 -0
- package/scripts/validate-release-identity.py +246 -0
- package/scripts/verify-standalone.sh +3 -0
- package/settings.json +3 -3
- package/templates/plugins-allowlist.yaml +19 -0
- package/artifacts/release/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/artifacts/release/dist/public/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/public/bundle/.kimi/mcp.json +0 -11
- package/build/lib/yaml.py +0 -321
- package/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/dist/public/bundle/.gemini/settings.json +0 -11
- package/dist/public/bundle/.kimi/mcp.json +0 -11
package/hooks/session-start.py
CHANGED
|
@@ -11,12 +11,16 @@ import time as _time
|
|
|
11
11
|
import re as _re
|
|
12
12
|
|
|
13
13
|
HOOKS_DIR = os.path.dirname(__file__)
|
|
14
|
+
PROJECT_ROOT = os.path.dirname(HOOKS_DIR)
|
|
15
|
+
if PROJECT_ROOT not in sys.path:
|
|
16
|
+
sys.path.insert(0, PROJECT_ROOT)
|
|
14
17
|
if HOOKS_DIR not in sys.path:
|
|
15
18
|
sys.path.insert(0, HOOKS_DIR)
|
|
16
19
|
|
|
17
|
-
from _common import setup_crash_handler, json_input, get_feature_flag, _resolve_project_dir
|
|
18
|
-
from state_migration import resolve_state_file, resolve_state_dir
|
|
19
|
-
from _budget import BUDGET_SESSION_TOTAL, BUDGET_SESSION_IDLE
|
|
20
|
+
from hooks._common import setup_crash_handler, json_input, get_feature_flag, _resolve_project_dir
|
|
21
|
+
from hooks.state_migration import resolve_state_file, resolve_state_dir
|
|
22
|
+
from hooks._budget import BUDGET_SESSION_TOTAL, BUDGET_SESSION_IDLE
|
|
23
|
+
from runtime.context_engine import render_profile_digest_text
|
|
20
24
|
|
|
21
25
|
setup_crash_handler("session-start", fail_closed=False)
|
|
22
26
|
|
|
@@ -43,37 +47,11 @@ project_path = resolve_state_file(project_dir, "state/project.md", "project.md")
|
|
|
43
47
|
|
|
44
48
|
profile = _read_file(profile_path, 3000)
|
|
45
49
|
if profile:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
continue
|
|
52
|
-
k, v = l.split(":", 1)
|
|
53
|
-
k = k.strip().lower()
|
|
54
|
-
v = v.strip().strip('"').strip("'")
|
|
55
|
-
if k in ("conventions", "ai_behavior"):
|
|
56
|
-
current_section = k
|
|
57
|
-
continue
|
|
58
|
-
if current_section:
|
|
59
|
-
kv[f"{current_section}.{k}"] = v
|
|
60
|
-
else:
|
|
61
|
-
kv[k] = v
|
|
62
|
-
|
|
63
|
-
name = kv.get("name", "")
|
|
64
|
-
conv_parts = []
|
|
65
|
-
for ck in ["conventions.naming", "conventions.test_cmd", "conventions.lint_cmd"]:
|
|
66
|
-
if kv.get(ck):
|
|
67
|
-
conv_parts.append(f"{ck.split('.')[-1]}={kv[ck]}")
|
|
68
|
-
comm = kv.get("ai_behavior.communication", "")
|
|
69
|
-
|
|
70
|
-
summary_parts = [name] if name else []
|
|
71
|
-
if conv_parts:
|
|
72
|
-
summary_parts.append(" ".join(conv_parts))
|
|
73
|
-
if comm:
|
|
74
|
-
summary_parts.append(f"lang:{comm}")
|
|
75
|
-
if summary_parts:
|
|
76
|
-
sections.append(f"@project: {' | '.join(summary_parts)}")
|
|
50
|
+
# Inject bounded profile digest — skips discoverable info (conventions, ai_behavior)
|
|
51
|
+
# and surfaces only non-discoverable preferences via render_profile_digest_text.
|
|
52
|
+
digest = render_profile_digest_text(project_dir, max_chars=240)
|
|
53
|
+
if digest:
|
|
54
|
+
sections.append(f"@project: profile_digest={digest}")
|
|
77
55
|
else:
|
|
78
56
|
project = _read_file(project_path, 1000)
|
|
79
57
|
if project:
|
|
@@ -243,7 +221,7 @@ if os.path.exists(tracker_path):
|
|
|
243
221
|
# 6) Recent memory (on-demand)
|
|
244
222
|
if get_feature_flag('memory'):
|
|
245
223
|
try:
|
|
246
|
-
from _memory import get_recent_memories
|
|
224
|
+
from hooks._memory import get_recent_memories
|
|
247
225
|
recent = get_recent_memories(project_dir, max_files=3, max_chars_total=150)
|
|
248
226
|
if recent:
|
|
249
227
|
sections.append(f'@recent-memory: {recent}')
|
package/hooks/setup_wizard.py
CHANGED
|
@@ -7,7 +7,9 @@ from __future__ import annotations
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
10
|
+
import re
|
|
10
11
|
import sys
|
|
12
|
+
from pathlib import Path
|
|
11
13
|
from typing import Any, cast
|
|
12
14
|
|
|
13
15
|
import yaml
|
|
@@ -35,6 +37,8 @@ from runtime.mcp_config_writers import ( # noqa: E402
|
|
|
35
37
|
import runtime.providers.codex_provider # noqa: E402, F401
|
|
36
38
|
import runtime.providers.gemini_provider # noqa: E402, F401
|
|
37
39
|
import runtime.providers.kimi_provider # noqa: E402, F401
|
|
40
|
+
from runtime.plugin_interop import discover_host_plugin_state # noqa: E402
|
|
41
|
+
from runtime.validate import run_validate as _run_post_install_validate # noqa: E402
|
|
38
42
|
from runtime.adoption import ( # noqa: E402
|
|
39
43
|
CANONICAL_MODE_NAMES,
|
|
40
44
|
CANONICAL_VERSION,
|
|
@@ -191,6 +195,28 @@ MCP_CATALOG: list[dict[str, Any]] = [
|
|
|
191
195
|
"default": False,
|
|
192
196
|
"category": "memory",
|
|
193
197
|
},
|
|
198
|
+
{
|
|
199
|
+
"id": "notebooklm",
|
|
200
|
+
"name": "NotebookLM",
|
|
201
|
+
"description": "NotebookLM MCP server for AI-powered research and note-taking",
|
|
202
|
+
"command": "npx",
|
|
203
|
+
"args": ["-y", "notebooklm-mcp@latest"],
|
|
204
|
+
"default": False,
|
|
205
|
+
"category": "productivity",
|
|
206
|
+
"warning": (
|
|
207
|
+
"NOTEBOOKLM MCP WARNING\n\n"
|
|
208
|
+
"NotebookLM requires browser automation and has several important considerations:\n\n"
|
|
209
|
+
"BROWSER AUTOMATION: NotebookLM uses Puppeteer for browser automation. "
|
|
210
|
+
"This requires a Chromium/Chrome installation and may consume significant system resources.\n\n"
|
|
211
|
+
"FIRST-RUN DOWNLOAD: On first run, NotebookLM will download a full browser instance "
|
|
212
|
+
"(typically 100-300 MB). This is a one-time operation but may take several minutes.\n\n"
|
|
213
|
+
"DEDICATED ACCOUNT: It is strongly recommended to use a dedicated Google account "
|
|
214
|
+
"for NotebookLM MCP. Do NOT use your primary account, as the MCP will store credentials locally.\n\n"
|
|
215
|
+
"AUTH EXPIRY: Google authentication tokens expire periodically. "
|
|
216
|
+
"You may need to re-authenticate if the MCP fails with auth errors.\n\n"
|
|
217
|
+
"Enable NotebookLM only if you understand these requirements and accept the risks."
|
|
218
|
+
),
|
|
219
|
+
},
|
|
194
220
|
]
|
|
195
221
|
|
|
196
222
|
_MCP_ID_ALIASES: dict[str, str] = {
|
|
@@ -451,6 +477,12 @@ def check_auth() -> dict[str, Any]:
|
|
|
451
477
|
|
|
452
478
|
_HTTP_MEMORY_MIN_LEVEL: int = _PRESET_LEVEL["interop"]
|
|
453
479
|
|
|
480
|
+
_PROFILE_ARCH_REQUEST_MAX = 8
|
|
481
|
+
_PROFILE_TAG_MAX = 12
|
|
482
|
+
_PROFILE_SUMMARY_MAX_CHARS = 240
|
|
483
|
+
_PROFILE_RECENT_UPDATES_MAX = 5
|
|
484
|
+
_PROFILE_GOVERNED_MAX = 24
|
|
485
|
+
|
|
454
486
|
|
|
455
487
|
def get_mode_choices() -> list[str]:
|
|
456
488
|
return list(CANONICAL_MODE_NAMES)
|
|
@@ -628,7 +660,7 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
|
|
|
628
660
|
"gemini": {"subscription": "free", "max_parallel_agents": 1},
|
|
629
661
|
"kimi": {"subscription": "free", "max_parallel_agents": 1},
|
|
630
662
|
}
|
|
631
|
-
preset = resolve_preset(cast(str
|
|
663
|
+
preset = resolve_preset(cast(str, preferences.get("preset") or "safe"))
|
|
632
664
|
default_config: dict[str, Any] = {
|
|
633
665
|
"version": CANONICAL_VERSION,
|
|
634
666
|
"preset": preset,
|
|
@@ -660,6 +692,8 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
|
|
|
660
692
|
state_dir = os.path.join(project_dir, ".omg", "state")
|
|
661
693
|
os.makedirs(state_dir, exist_ok=True)
|
|
662
694
|
|
|
695
|
+
_write_profile_learning_sections(state_dir, preferences)
|
|
696
|
+
|
|
663
697
|
# Write config to YAML file
|
|
664
698
|
config_path = os.path.join(state_dir, "cli-config.yaml")
|
|
665
699
|
with open(config_path, "w") as f:
|
|
@@ -674,6 +708,260 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
|
|
|
674
708
|
}
|
|
675
709
|
|
|
676
710
|
|
|
711
|
+
def _write_profile_learning_sections(state_dir: str, preferences: dict[str, Any]) -> None:
|
|
712
|
+
profile_path = os.path.join(state_dir, "profile.yaml")
|
|
713
|
+
profile_data = _load_profile_yaml(profile_path)
|
|
714
|
+
_ensure_profile_baseline(profile_data)
|
|
715
|
+
|
|
716
|
+
profile_data["preferences"] = _normalize_preferences_block(preferences.get("preferences"))
|
|
717
|
+
profile_data["user_vector"] = _normalize_user_vector_block(preferences.get("user_vector"))
|
|
718
|
+
profile_data["profile_provenance"] = _normalize_provenance_block(preferences.get("profile_provenance"))
|
|
719
|
+
profile_data["governed_preferences"] = _normalize_governed_preferences_block(preferences.get("governed_preferences"))
|
|
720
|
+
|
|
721
|
+
from runtime.profile_io import save_profile
|
|
722
|
+
save_profile(profile_path, profile_data)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def _render_explicit_empty_collections(dumped: str) -> str:
|
|
726
|
+
lines = dumped.splitlines()
|
|
727
|
+
output: list[str] = []
|
|
728
|
+
|
|
729
|
+
def _next_line(start: int) -> str:
|
|
730
|
+
if start + 1 < len(lines):
|
|
731
|
+
return lines[start + 1]
|
|
732
|
+
return ""
|
|
733
|
+
|
|
734
|
+
for idx, line in enumerate(lines):
|
|
735
|
+
next_line = _next_line(idx)
|
|
736
|
+
stripped = line.strip()
|
|
737
|
+
|
|
738
|
+
if stripped == "stack:" and (not next_line or not next_line.startswith("- ")):
|
|
739
|
+
output.append("stack: []")
|
|
740
|
+
continue
|
|
741
|
+
if stripped == "conventions:" and (not next_line or not next_line.startswith(" ")):
|
|
742
|
+
output.append("conventions: {}")
|
|
743
|
+
continue
|
|
744
|
+
if stripped == "ai_behavior:" and (not next_line or not next_line.startswith(" ")):
|
|
745
|
+
output.append("ai_behavior: {}")
|
|
746
|
+
continue
|
|
747
|
+
if stripped == "architecture_requests:" and (not next_line or not next_line.startswith(" - ")):
|
|
748
|
+
output.append(" architecture_requests: []")
|
|
749
|
+
continue
|
|
750
|
+
if stripped == "constraints:" and (not next_line or not next_line.startswith(" ")):
|
|
751
|
+
output.append(" constraints: {}")
|
|
752
|
+
continue
|
|
753
|
+
if stripped == "tags:" and (not next_line or not next_line.startswith(" - ")):
|
|
754
|
+
output.append(" tags: []")
|
|
755
|
+
continue
|
|
756
|
+
if stripped == "recent_updates:" and (not next_line or not next_line.startswith(" - ")):
|
|
757
|
+
output.append(" recent_updates: []")
|
|
758
|
+
continue
|
|
759
|
+
|
|
760
|
+
output.append(line)
|
|
761
|
+
|
|
762
|
+
return "\n".join(output) + "\n"
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def _load_profile_yaml(profile_path: str) -> dict[str, Any]:
|
|
766
|
+
from runtime.profile_io import load_profile
|
|
767
|
+
return load_profile(profile_path)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def _ensure_profile_baseline(profile_data: dict[str, Any]) -> None:
|
|
771
|
+
profile_data.setdefault("name", "omg-project")
|
|
772
|
+
profile_data.setdefault("description", "initialized by OMG standalone compat bootstrap")
|
|
773
|
+
profile_data.setdefault("language", "unknown")
|
|
774
|
+
profile_data.setdefault("framework", "unknown")
|
|
775
|
+
profile_data.setdefault("stack", [])
|
|
776
|
+
profile_data.setdefault("conventions", {})
|
|
777
|
+
profile_data.setdefault("ai_behavior", {})
|
|
778
|
+
profile_data.setdefault("governed_preferences", {"style": [], "safety": []})
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def _normalize_preferences_block(raw: Any) -> dict[str, Any]:
|
|
782
|
+
block = raw if isinstance(raw, dict) else {}
|
|
783
|
+
|
|
784
|
+
requests_obj = block.get("architecture_requests")
|
|
785
|
+
architecture_requests: list[str] = []
|
|
786
|
+
if isinstance(requests_obj, list):
|
|
787
|
+
for value in requests_obj:
|
|
788
|
+
text = str(value).strip()
|
|
789
|
+
if text:
|
|
790
|
+
architecture_requests.append(text)
|
|
791
|
+
if len(architecture_requests) >= _PROFILE_ARCH_REQUEST_MAX:
|
|
792
|
+
break
|
|
793
|
+
|
|
794
|
+
constraints_obj = block.get("constraints")
|
|
795
|
+
constraints: dict[str, Any] = {}
|
|
796
|
+
if isinstance(constraints_obj, dict):
|
|
797
|
+
for key, value in constraints_obj.items():
|
|
798
|
+
normalized_key = _normalize_constraint_key(str(key))
|
|
799
|
+
normalized_value = _normalize_constraint_value(value)
|
|
800
|
+
if normalized_key and normalized_value is not None:
|
|
801
|
+
constraints[normalized_key] = normalized_value
|
|
802
|
+
|
|
803
|
+
routing_obj = block.get("routing")
|
|
804
|
+
routing_map = routing_obj if isinstance(routing_obj, dict) else {}
|
|
805
|
+
routing = {
|
|
806
|
+
"prefer_clarification": bool(routing_map.get("prefer_clarification", False)),
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return {
|
|
810
|
+
"architecture_requests": architecture_requests,
|
|
811
|
+
"constraints": constraints,
|
|
812
|
+
"routing": routing,
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def _normalize_constraint_key(value: str) -> str:
|
|
817
|
+
normalized = value.strip().lower()
|
|
818
|
+
normalized = re.sub(r"\s+", "_", normalized)
|
|
819
|
+
normalized = re.sub(r"[^a-z0-9_]+", "", normalized)
|
|
820
|
+
return normalized
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def _normalize_constraint_value(value: Any) -> str | int | float | bool | None:
|
|
824
|
+
if isinstance(value, bool):
|
|
825
|
+
return value
|
|
826
|
+
if isinstance(value, int):
|
|
827
|
+
return value
|
|
828
|
+
if isinstance(value, float):
|
|
829
|
+
return value
|
|
830
|
+
if value is None:
|
|
831
|
+
return None
|
|
832
|
+
text = str(value).strip().lower()
|
|
833
|
+
return text if text else None
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
def _normalize_user_vector_block(raw: Any) -> dict[str, Any]:
|
|
837
|
+
block = raw if isinstance(raw, dict) else {}
|
|
838
|
+
|
|
839
|
+
tags_obj = block.get("tags")
|
|
840
|
+
tags: list[str] = []
|
|
841
|
+
if isinstance(tags_obj, list):
|
|
842
|
+
for value in tags_obj:
|
|
843
|
+
token = _normalize_tag_token(str(value))
|
|
844
|
+
if token:
|
|
845
|
+
tags.append(token)
|
|
846
|
+
if len(tags) >= _PROFILE_TAG_MAX:
|
|
847
|
+
break
|
|
848
|
+
|
|
849
|
+
summary = ""
|
|
850
|
+
summary_obj = block.get("summary")
|
|
851
|
+
if isinstance(summary_obj, str):
|
|
852
|
+
summary = " ".join(summary_obj.strip().split())[:_PROFILE_SUMMARY_MAX_CHARS]
|
|
853
|
+
|
|
854
|
+
confidence = 0.0
|
|
855
|
+
confidence_obj = block.get("confidence")
|
|
856
|
+
if isinstance(confidence_obj, (int, float, str)):
|
|
857
|
+
try:
|
|
858
|
+
confidence = float(confidence_obj)
|
|
859
|
+
except (TypeError, ValueError):
|
|
860
|
+
confidence = 0.0
|
|
861
|
+
confidence = max(0.0, min(1.0, confidence))
|
|
862
|
+
|
|
863
|
+
return {
|
|
864
|
+
"tags": tags,
|
|
865
|
+
"summary": summary,
|
|
866
|
+
"confidence": confidence,
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def _normalize_tag_token(value: str) -> str:
|
|
871
|
+
normalized = value.strip().lower()
|
|
872
|
+
normalized = re.sub(r"\s+", "_", normalized)
|
|
873
|
+
normalized = re.sub(r"[^a-z0-9_\-]+", "", normalized)
|
|
874
|
+
return normalized
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
def _normalize_provenance_block(raw: Any) -> dict[str, Any]:
|
|
878
|
+
block = raw if isinstance(raw, dict) else {}
|
|
879
|
+
updates_obj = block.get("recent_updates")
|
|
880
|
+
updates: list[dict[str, str]] = []
|
|
881
|
+
if isinstance(updates_obj, list):
|
|
882
|
+
for entry in updates_obj:
|
|
883
|
+
if not isinstance(entry, dict):
|
|
884
|
+
continue
|
|
885
|
+
run_id = str(entry.get("run_id", "")).strip()
|
|
886
|
+
source = str(entry.get("source", "")).strip()
|
|
887
|
+
field = str(entry.get("field", "")).strip()
|
|
888
|
+
updated_at = str(entry.get("updated_at", "")).strip()
|
|
889
|
+
if not (run_id and source and field and updated_at):
|
|
890
|
+
continue
|
|
891
|
+
updates.append(
|
|
892
|
+
{
|
|
893
|
+
"run_id": run_id,
|
|
894
|
+
"source": source,
|
|
895
|
+
"field": field,
|
|
896
|
+
"updated_at": updated_at,
|
|
897
|
+
}
|
|
898
|
+
)
|
|
899
|
+
if len(updates) >= _PROFILE_RECENT_UPDATES_MAX:
|
|
900
|
+
break
|
|
901
|
+
return {"recent_updates": updates}
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
def _normalize_governed_preferences_block(raw: Any) -> dict[str, list[dict[str, Any]]]:
|
|
905
|
+
block = raw if isinstance(raw, dict) else {}
|
|
906
|
+
style_raw = block.get("style")
|
|
907
|
+
safety_raw = block.get("safety")
|
|
908
|
+
return {
|
|
909
|
+
"style": _normalize_governed_section_entries(style_raw, section="style"),
|
|
910
|
+
"safety": _normalize_governed_section_entries(safety_raw, section="safety"),
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def _normalize_governed_section_entries(raw: Any, *, section: str) -> list[dict[str, Any]]:
|
|
915
|
+
if not isinstance(raw, list):
|
|
916
|
+
return []
|
|
917
|
+
entries: list[dict[str, Any]] = []
|
|
918
|
+
for item in raw:
|
|
919
|
+
if not isinstance(item, dict):
|
|
920
|
+
continue
|
|
921
|
+
field = str(item.get("field", "")).strip()
|
|
922
|
+
value = " ".join(str(item.get("value", "")).strip().split())
|
|
923
|
+
source = str(item.get("source", "")).strip()
|
|
924
|
+
learned_at = str(item.get("learned_at", "")).strip()
|
|
925
|
+
updated_at = str(item.get("updated_at", "")).strip()
|
|
926
|
+
candidate_section = str(item.get("section", section)).strip().lower()
|
|
927
|
+
confirmation_state = str(item.get("confirmation_state", "")).strip().lower()
|
|
928
|
+
|
|
929
|
+
if not (field and value and source and learned_at and updated_at):
|
|
930
|
+
continue
|
|
931
|
+
if candidate_section != section:
|
|
932
|
+
continue
|
|
933
|
+
if confirmation_state not in ("confirmed", "pending_confirmation", "inferred"):
|
|
934
|
+
continue
|
|
935
|
+
|
|
936
|
+
entry: dict[str, Any] = {
|
|
937
|
+
"field": field,
|
|
938
|
+
"value": value,
|
|
939
|
+
"source": source,
|
|
940
|
+
"learned_at": learned_at,
|
|
941
|
+
"updated_at": updated_at,
|
|
942
|
+
"section": section,
|
|
943
|
+
"confirmation_state": confirmation_state,
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if section == "style" and confirmation_state == "inferred":
|
|
947
|
+
decay_raw = item.get("decay_metadata")
|
|
948
|
+
decay = decay_raw if isinstance(decay_raw, dict) else {}
|
|
949
|
+
try:
|
|
950
|
+
score = float(decay.get("decay_score", 0.0))
|
|
951
|
+
except (TypeError, ValueError):
|
|
952
|
+
score = 0.0
|
|
953
|
+
entry["decay_metadata"] = {
|
|
954
|
+
"decay_score": max(0.0, min(1.0, score)),
|
|
955
|
+
"last_seen_at": str(decay.get("last_seen_at", updated_at)).strip() or updated_at,
|
|
956
|
+
"decay_reason": str(decay.get("decay_reason", "inferred_signal")).strip() or "inferred_signal",
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
entries.append(entry)
|
|
960
|
+
if len(entries) >= _PROFILE_GOVERNED_MAX:
|
|
961
|
+
break
|
|
962
|
+
return entries
|
|
963
|
+
|
|
964
|
+
|
|
677
965
|
def _write_project_settings_preset(project_dir: str, preset: str) -> None:
|
|
678
966
|
"""Persist preset metadata into project settings when settings.json exists."""
|
|
679
967
|
settings_path = os.path.join(project_dir, "settings.json")
|
|
@@ -745,6 +1033,11 @@ def run_setup_wizard(
|
|
|
745
1033
|
preset=selected_preset,
|
|
746
1034
|
adopt=adopt,
|
|
747
1035
|
)
|
|
1036
|
+
foreign_plugins_discovered = 0
|
|
1037
|
+
if adoption.get("selected_mode") == "coexist" or selected_preset == "interop":
|
|
1038
|
+
host_plugin_state = discover_host_plugin_state(project_dir)
|
|
1039
|
+
foreign_plugins_discovered = len(host_plugin_state.records)
|
|
1040
|
+
adoption["foreign_plugins_discovered"] = foreign_plugins_discovered
|
|
748
1041
|
|
|
749
1042
|
clis = detect_clis()
|
|
750
1043
|
auth = check_auth()
|
|
@@ -760,7 +1053,7 @@ def run_setup_wizard(
|
|
|
760
1053
|
report_path = write_adoption_report(project_dir, adoption)
|
|
761
1054
|
adoption["report_path"] = report_path
|
|
762
1055
|
|
|
763
|
-
|
|
1056
|
+
result: dict[str, Any] = {
|
|
764
1057
|
"status": "complete",
|
|
765
1058
|
"setup_mode": {
|
|
766
1059
|
"choices": get_mode_choices(),
|
|
@@ -773,3 +1066,50 @@ def run_setup_wizard(
|
|
|
773
1066
|
"preferences": prefs,
|
|
774
1067
|
"adoption": adoption,
|
|
775
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
# --- Post-install validation (runs AFTER all setup writes are complete) ---
|
|
1071
|
+
try:
|
|
1072
|
+
validation_result = _run_post_install_validate(root_dir=Path(_PROJECT_ROOT))
|
|
1073
|
+
except Exception as exc:
|
|
1074
|
+
validation_result = {
|
|
1075
|
+
"schema": "ValidateResult",
|
|
1076
|
+
"status": "fail",
|
|
1077
|
+
"checks": [
|
|
1078
|
+
{
|
|
1079
|
+
"name": "validation_error",
|
|
1080
|
+
"status": "blocker",
|
|
1081
|
+
"message": str(exc),
|
|
1082
|
+
"required": True,
|
|
1083
|
+
},
|
|
1084
|
+
],
|
|
1085
|
+
"version": "",
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
# Persist machine-readable validation artifact
|
|
1089
|
+
artifact_dir = os.path.join(project_dir, ".omg", "state")
|
|
1090
|
+
os.makedirs(artifact_dir, exist_ok=True)
|
|
1091
|
+
artifact_path = os.path.join(artifact_dir, "post-install-validation.json")
|
|
1092
|
+
try:
|
|
1093
|
+
with open(artifact_path, "w", encoding="utf-8") as f:
|
|
1094
|
+
json.dump(validation_result, f, indent=2, ensure_ascii=True)
|
|
1095
|
+
f.write("\n")
|
|
1096
|
+
except OSError:
|
|
1097
|
+
artifact_path = ""
|
|
1098
|
+
|
|
1099
|
+
checks = validation_result.get("checks", [])
|
|
1100
|
+
blockers = [
|
|
1101
|
+
c
|
|
1102
|
+
for c in checks
|
|
1103
|
+
if isinstance(c, dict) and c.get("status") == "blocker"
|
|
1104
|
+
]
|
|
1105
|
+
|
|
1106
|
+
result["post_install_validation"] = {
|
|
1107
|
+
"status": validation_result.get("status", "fail"),
|
|
1108
|
+
"artifact_path": artifact_path,
|
|
1109
|
+
"blockers": [{"name": c["name"], "message": c["message"]} for c in blockers],
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if blockers:
|
|
1113
|
+
result["status"] = "validation_failed"
|
|
1114
|
+
|
|
1115
|
+
return result
|
package/hooks/shadow_manager.py
CHANGED
|
@@ -146,7 +146,13 @@ def map_shadow_path(project_dir: str, run_id: str, file_path: str) -> str:
|
|
|
146
146
|
return os.path.join(_shadow_root(project_dir), run_id, "overlay", rel)
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def record_shadow_write(
|
|
149
|
+
def record_shadow_write(
|
|
150
|
+
project_dir: str,
|
|
151
|
+
run_id: str,
|
|
152
|
+
file_path: str,
|
|
153
|
+
source: str = "tool",
|
|
154
|
+
step_id: str | None = None,
|
|
155
|
+
) -> dict[str, Any]:
|
|
150
156
|
run_id = _validated_run_id(run_id)
|
|
151
157
|
run_dir = os.path.join(_shadow_root(project_dir), run_id)
|
|
152
158
|
os.makedirs(run_dir, exist_ok=True)
|
|
@@ -155,7 +161,8 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
155
161
|
os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
|
|
156
162
|
|
|
157
163
|
abs_file = file_path if os.path.isabs(file_path) else os.path.join(project_dir, file_path)
|
|
158
|
-
|
|
164
|
+
file_existed_before = os.path.exists(abs_file)
|
|
165
|
+
if file_existed_before:
|
|
159
166
|
shutil.copy2(abs_file, shadow_path)
|
|
160
167
|
file_hash = _hash_file(abs_file)
|
|
161
168
|
else:
|
|
@@ -169,7 +176,10 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
169
176
|
"recorded_at": _utc_now(),
|
|
170
177
|
"source": source,
|
|
171
178
|
"sha256": file_hash,
|
|
179
|
+
"file_existed_before": file_existed_before,
|
|
172
180
|
}
|
|
181
|
+
if step_id:
|
|
182
|
+
entry["step_id"] = step_id
|
|
173
183
|
|
|
174
184
|
files = manifest.get("files", [])
|
|
175
185
|
# Replace existing entry for same file.
|
|
@@ -181,6 +191,72 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
181
191
|
return entry
|
|
182
192
|
|
|
183
193
|
|
|
194
|
+
def restore_shadow_entry(project_dir: str, run_id: str, step_id: str) -> dict[str, Any]:
|
|
195
|
+
run_id = _validated_run_id(run_id)
|
|
196
|
+
run_dir = os.path.join(_shadow_root(project_dir), run_id)
|
|
197
|
+
manifest = _load_manifest(run_dir)
|
|
198
|
+
entries = manifest.get("files", [])
|
|
199
|
+
|
|
200
|
+
restored: list[str] = []
|
|
201
|
+
failed: list[dict[str, str]] = []
|
|
202
|
+
|
|
203
|
+
selected_entries: list[dict[str, Any]] = []
|
|
204
|
+
for item in entries:
|
|
205
|
+
if not isinstance(item, dict):
|
|
206
|
+
continue
|
|
207
|
+
item_step_id = str(item.get("step_id", "")).strip()
|
|
208
|
+
if item_step_id == step_id:
|
|
209
|
+
selected_entries.append(item)
|
|
210
|
+
|
|
211
|
+
if not selected_entries and isinstance(entries, list) and len(entries) == 1:
|
|
212
|
+
single = entries[0]
|
|
213
|
+
if isinstance(single, dict):
|
|
214
|
+
selected_entries = [single]
|
|
215
|
+
|
|
216
|
+
if not selected_entries:
|
|
217
|
+
return {"restored": restored, "failed": failed, "reason": "step not found"}
|
|
218
|
+
|
|
219
|
+
for item in selected_entries:
|
|
220
|
+
rel = str(item.get("file", "")).strip()
|
|
221
|
+
shadow_rel = str(item.get("shadow_file", "")).strip()
|
|
222
|
+
file_existed_before = bool(item.get("file_existed_before", True))
|
|
223
|
+
if not rel:
|
|
224
|
+
failed.append({"file": "", "reason": "missing file path"})
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
dst = os.path.join(project_dir, rel)
|
|
228
|
+
src = os.path.join(run_dir, shadow_rel) if shadow_rel else ""
|
|
229
|
+
|
|
230
|
+
if src and os.path.exists(src):
|
|
231
|
+
try:
|
|
232
|
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
|
233
|
+
shutil.copy2(src, dst)
|
|
234
|
+
restored.append(rel)
|
|
235
|
+
except Exception as exc:
|
|
236
|
+
failed.append({"file": rel, "reason": f"copy failed: {exc}"})
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
if not file_existed_before:
|
|
240
|
+
try:
|
|
241
|
+
if os.path.exists(dst):
|
|
242
|
+
os.remove(dst)
|
|
243
|
+
restored.append(rel)
|
|
244
|
+
except Exception as exc:
|
|
245
|
+
failed.append({"file": rel, "reason": f"delete failed: {exc}"})
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
failed.append({"file": rel, "reason": "shadow snapshot missing"})
|
|
249
|
+
|
|
250
|
+
manifest["status"] = "restored"
|
|
251
|
+
manifest["restored_at"] = _utc_now()
|
|
252
|
+
_save_manifest(run_dir, manifest)
|
|
253
|
+
|
|
254
|
+
reason = "shadow restore applied" if restored and not failed else "shadow restore partial"
|
|
255
|
+
if not restored and failed:
|
|
256
|
+
reason = "shadow restore failed"
|
|
257
|
+
return {"restored": restored, "failed": failed, "reason": reason}
|
|
258
|
+
|
|
259
|
+
|
|
184
260
|
def create_evidence_pack(
|
|
185
261
|
project_dir: str,
|
|
186
262
|
run_id: str,
|
|
@@ -321,6 +397,34 @@ def drop_shadow(project_dir: str, run_id: str) -> dict[str, Any]:
|
|
|
321
397
|
return {"run_id": run_id, "dropped": True}
|
|
322
398
|
|
|
323
399
|
|
|
400
|
+
def auto_journal_mutation(
|
|
401
|
+
project_dir: str,
|
|
402
|
+
tool: str,
|
|
403
|
+
file_path: str,
|
|
404
|
+
run_id: str,
|
|
405
|
+
) -> dict[str, Any] | None:
|
|
406
|
+
try:
|
|
407
|
+
from runtime.interaction_journal import InteractionJournal
|
|
408
|
+
from runtime.release_run_coordinator import resolve_current_run_id
|
|
409
|
+
except ImportError:
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
canonical_run_id = resolve_current_run_id(project_dir) or run_id
|
|
413
|
+
shadow_manifest = os.path.join(
|
|
414
|
+
_shadow_root(project_dir), run_id, "manifest.json"
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
journal = InteractionJournal(project_dir)
|
|
418
|
+
return journal.record_step(
|
|
419
|
+
tool.lower(),
|
|
420
|
+
{
|
|
421
|
+
"file_path": file_path,
|
|
422
|
+
"run_id": canonical_run_id,
|
|
423
|
+
"shadow_manifest": shadow_manifest,
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
324
428
|
def _handle_post_tool_use(payload: dict[str, Any]) -> None:
|
|
325
429
|
tool = payload.get("tool_name", "")
|
|
326
430
|
if tool not in ("Write", "Edit", "MultiEdit"):
|
|
@@ -343,6 +447,11 @@ def _handle_post_tool_use(payload: dict[str, Any]) -> None:
|
|
|
343
447
|
run_id = begin_shadow_run(project_dir, metadata={"source": "post-tool-use"})
|
|
344
448
|
record_shadow_write(project_dir, run_id, file_path)
|
|
345
449
|
|
|
450
|
+
try:
|
|
451
|
+
auto_journal_mutation(project_dir, tool, file_path, run_id)
|
|
452
|
+
except Exception:
|
|
453
|
+
pass
|
|
454
|
+
|
|
346
455
|
|
|
347
456
|
def _main() -> int:
|
|
348
457
|
# Early-exit: skip all work if shadow/evidence mode is not enabled
|