multi-forge 1.0.2__tar.gz → 1.1.0__tar.gz
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.
- {multi_forge-1.0.2 → multi_forge-1.1.0}/PKG-INFO +2 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/pyproject.toml +4 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/activity.py +11 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/__init__.py +1 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/codex_patch.py +2 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/codex_policy.py +17 -7
- multi_forge-1.1.0/src/forge/cli/hooks/codex_policy_feedback.py +317 -0
- multi_forge-1.1.0/src/forge/cli/hooks/codex_stop.py +223 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/commands.py +80 -15
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/direct_commands.py +62 -14
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/policy.py +43 -16
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/policy.py +195 -14
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/proxy_costs.py +1 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_codex.py +20 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_fork.py +26 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_lane.py +35 -2
- multi_forge-1.1.0/src/forge/cli/session_launch_args.py +105 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_lifecycle.py +58 -10
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_manage.py +2 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_resume_modes.py +5 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_route_recovery.py +18 -2
- multi_forge-1.1.0/src/forge/cli/supervisor_auth.py +59 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/backends/litellm.yaml +52 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +6 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +6 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-gemini.yaml +7 -3
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-gemini.yaml +10 -3
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/model_catalog.yaml +28 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/model_routes.yaml +60 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/effort.py +16 -5
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/_lifecycle.py +32 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/codex.py +38 -11
- multi_forge-1.1.0/src/forge/core/invoker/codex_fork.py +96 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/codex_stream.py +6 -6
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/types.py +11 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/__init__.py +7 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/clients/litellm.py +35 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/clients/openai_compat.py +1 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/clients/openrouter.py +4 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/types.py +3 -0
- multi_forge-1.1.0/src/forge/core/native_review_types.py +20 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/claude_session.py +58 -8
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_bridge.py +3 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_enrollment.py +11 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_interactive.py +14 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_session.py +23 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/policy.py +328 -50
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session.py +40 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_authority_launch.py +35 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_context.py +10 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/usage_summary.py +23 -0
- multi_forge-1.1.0/src/forge/core/reactive/codex_reviewer_home.py +452 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/env.py +22 -6
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/llm_call.py +5 -1
- multi_forge-1.1.0/src/forge/core/reactive/reviewer_runtime.py +156 -0
- multi_forge-1.1.0/src/forge/core/reactive/reviewer_settings.py +93 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/session_runner.py +97 -22
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/structured_output.py +7 -0
- multi_forge-1.1.0/src/forge/core/reactive/supervisor_auth.py +117 -0
- multi_forge-1.1.0/src/forge/core/reactive/watchdog.py +236 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/run_id.py +6 -0
- multi_forge-1.1.0/src/forge/core/runtime/codex_feedback.py +134 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime/codex_preflight.py +6 -6
- multi_forge-1.1.0/src/forge/core/runtime/codex_review_source.py +441 -0
- multi_forge-1.1.0/src/forge/core/runtime/codex_turn_enrollment.py +181 -0
- multi_forge-1.1.0/src/forge/core/runtime/launch_args.py +224 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/emit.py +3 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/codex_hooks.py +3 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/hook_dispatcher.py +49 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/hook_migration.py +9 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/installer.py +7 -7
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/ownership.py +13 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/tracking.py +5 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/deterministic/base.py +7 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/deterministic/tdd.py +1 -1
- multi_forge-1.1.0/src/forge/policy/semantic/attempts.py +264 -0
- multi_forge-1.1.0/src/forge/policy/semantic/deadline.py +99 -0
- multi_forge-1.1.0/src/forge/policy/semantic/identity.py +221 -0
- multi_forge-1.1.0/src/forge/policy/semantic/native_codex.py +255 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/plan_check.py +49 -14
- multi_forge-1.1.0/src/forge/policy/semantic/plan_source.py +75 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/shadow.py +112 -29
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/shadow_runner.py +71 -28
- multi_forge-1.1.0/src/forge/policy/semantic/stop_review.py +866 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/supervisor.py +313 -100
- multi_forge-1.1.0/src/forge/policy/semantic/turn_evidence.py +453 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/verdict.py +27 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/store.py +8 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/supervisor_lane_degrade.py +6 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/types.py +24 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/client_adapter.py +2 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/data_models.py +2 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/reasoning.py +37 -11
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/server.py +16 -4
- multi_forge-1.1.0/src/forge/review/backend_readiness.py +102 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/engine.py +6 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/models.py +10 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/runtime_config.py +19 -8
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/claude/invoke.py +11 -9
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/claude/paths.py +1 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/codex_invoke.py +7 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/events.py +142 -5
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/launch.py +10 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/manager.py +18 -15
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/models.py +64 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/store.py +151 -14
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/worktree/config_copy.py +43 -5
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/sidecar/docker.py +31 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/SKILL.md +6 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/4-proxy.md +4 -4
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/6-hook.md +4 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist.md +1 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/coverage-map.md +1 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/runtime-matrix.json +1 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/scripts/qa-run-metrics.py +11 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/scripts/qa-selection.py +31 -2
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/scripts/start-container.sh +97 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/.gitignore +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/LICENSE +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/NOTICE +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/README.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/agents/.gitkeep +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/commands/.gitkeep +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/adapters/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/adapters/litellm.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/creation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/remote/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/remote/base.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/remote/openrouter.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/backend/sources.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/auth.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/backend.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/claude.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/codex.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/config_cmd.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/consumer_lane_freeze.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/editor.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/extensions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/gc.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/guards.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/_group.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/_helpers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/authority.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/codex_transfer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/protocols.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/read_hygiene.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/hooks/verification.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/info.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/logs.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/main.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/memory.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/memory_report.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/memory_writer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/model.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/output.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/proxy.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/proxy_audit.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/runtime.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/search.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_adopt.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_authority.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_authority_options.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_memory.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_model.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_repair.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_rewind.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/session_supervisor_options.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/status_line.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/context.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/formatting.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/names.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/palette.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/rendering.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/sources.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/throttle.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/statusline/types.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/telemetry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/trace.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/transfer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/workflow.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/cli/workspace.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/dataclass_utils.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/backends/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/codex-responses-local.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-anthropic.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/loader.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/config/schema.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/capabilities.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/credentials_file.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/protocols.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/secrets.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/auth/template_secrets.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/backend_dependency.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/credential_registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/model_practices.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/identifiers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/invoker/claude.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/lanes.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/clients/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/clients/base.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/credentials.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/detection.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/errors.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/openrouter_policy.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/llm/protocols.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/logging.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/metric_formatting.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/catalog.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/direct_model.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/model_practices.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/model_reference.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/model_routes.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/models/types.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/naming.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/backend_reconcile.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_adopt.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/codex_thread_index.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/context.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/gc.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/provider_trace.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/proxy.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/resolution.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_active.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_adopt.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_authority.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_fork_execution.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_fork_preflight.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_model.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_model_routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_repair.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/session_routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/transfer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/ops/workspace.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/paths.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/process.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/provider_types.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/cost_tracking.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/headless_json.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/proxy.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/tagger.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/reactive/throttle.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime/codex_preflight_cache.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime/codex_rollouts.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime/registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/runtime_vocab.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/exceptions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/io.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/lock.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/retention.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/timestamps.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/state/versioned_store.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/caps.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/downstream.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/downstream_retention.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/jsonl_io.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/upstream.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/telemetry/vocabulary.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/tiers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/transcript.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/typing_helpers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/billing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/correlation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/ledger.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/measurement.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/usage/vocabulary.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/wire_shapes.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/workqueue/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/workqueue/queue.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/core/workqueue/types.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/doctor.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/exceptions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/hooks.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/models.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/module_planning.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/path_policy.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/preset.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/project_compat.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/project_registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/runtime_removal.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/settings_merge.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/settings_rollback.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/skill_cache.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/skill_compiler.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/skill_planning.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/skill_validation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/unmanaged.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/install/version.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/action_identity.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/deterministic/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/deterministic/coding_standards.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/deterministic/registry.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/diff.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/engine.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/protocols.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/queries.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/semantic/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/team/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/team/config.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/team/handlers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/policy/team/prompts.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/accounting_persistence.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/audit_logger.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/base_client.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/client_factory.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/converters.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/cost_logger.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/cost_tracker.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/error_hints.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/intercept.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/metrics.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/model_routes.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/passthrough.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/passthrough_ingress.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/ports.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/provider_trace_logger.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/proxies.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/proxy_identity.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/proxy_orchestrator.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/proxy_startup.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/request_id.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/response_headers.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/responses_ingress.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/responses_passthrough.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/runtime_truth.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/sse_framing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/stream_relay.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/proxy/utils.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/adversarial.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/consensus.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/code_debate_evaluation.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/codereview-performance.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/codereview-quick.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/codereview-security.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/codereview.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/consensus_evaluation.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/debate_evaluation.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/docreview-quick.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/docreview.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/resources/thinkdeep.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/synthesis.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/review/worker_preparation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/bm25_store.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/content_store.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/engine.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/exceptions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/extractor.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/index_state.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/store.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/search/tokenizer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/active.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/addendum.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/artifacts.py +1 -1
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/authority.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/claude/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/claude/cleanup.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/claude/relocate.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/cleanup.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/codex_handoff.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/config.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/consumer_lanes.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/context_limit.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/context_rendering.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/effective.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/exceptions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/git.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/hooks/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/hooks/models.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/hooks/session_start.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/identity.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/index.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/launch_confirmation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/launchability.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/memory_inheritance.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/memory_writer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/model_pin.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/overrides.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/passport.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/plan_resolution.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/prev_sessions.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/project_memory.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/rewind.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/routing.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/shadow_curation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/transfer.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/validation.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/verification_config.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/workspace.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/worktree/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/worktree/cleanup.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/session/worktree/create.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/sidecar/__init__.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/forge/sidecar/container.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/analyze/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/analyze/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/challenge/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/challenge/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/consensus/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/consensus/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/consensus/resources/synthesis.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/debate/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/debate/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/panel/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/panel/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/panel/resources/synthesis.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/0-enable.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/10-resume.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/11-config.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/12-search.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/13-policy.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/15-skills.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/16-memory.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/17-info.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/18-disable.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/2-extension.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/5-session.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/7-costs.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/execution-budget.json +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/resources/report-template.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/scripts/qa-artifact.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/qa/scripts/walkthrough-state.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/references/claude-4.6.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/references/claude-4.8.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/references/gemini-3.1.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/references/gpt-5.5.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/references/skills-writing-guide.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/resources/code-anthropic.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/resources/code-gemini.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/resources/code-openai.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review/resources/code.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/resources/docs-gemini.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/resources/docs-openai.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/review-docs/resources/docs.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/smoke-test/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/smoke-test/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/content.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/forge-skill.yaml +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/code-anthropic.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/code-gemini.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/code-openai.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/code.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/docs-anthropic.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/docs-gemini.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/docs-openai.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/understand/resources/docs.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/SKILL.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/resources/checklist.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/resources/journey-map.md +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/claude-wrapper.sh +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/cleanup-owned.sh +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/package-identity.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/protected-paths.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/walkthrough-report.py +0 -0
- {multi_forge-1.0.2 → multi_forge-1.1.0}/src/skills/walkthrough/scripts/walkthrough-state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: multi-forge
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration
|
|
5
5
|
Project-URL: Homepage, https://github.com/hapa1i/multi-forge
|
|
6
6
|
Project-URL: Repository, https://github.com/hapa1i/multi-forge
|
|
@@ -47,7 +47,7 @@ Requires-Dist: pyyaml<7,>=6.0.3
|
|
|
47
47
|
Requires-Dist: redis<8,>=5.2.0
|
|
48
48
|
Requires-Dist: rich<14,>=13.9.4
|
|
49
49
|
Requires-Dist: ruamel-yaml>=0.18.0
|
|
50
|
-
Requires-Dist: starlette<1.
|
|
50
|
+
Requires-Dist: starlette<1.8,>=1.7.0
|
|
51
51
|
Requires-Dist: tenacity>=8.0.0
|
|
52
52
|
Requires-Dist: uvicorn<1,>=0.33.0
|
|
53
53
|
Requires-Dist: uvloop<1,>=0.21.0; sys_platform != 'win32'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "multi-forge"
|
|
3
|
-
version = "1.0
|
|
3
|
+
version = "1.1.0"
|
|
4
4
|
description = "Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -53,8 +53,7 @@ dependencies = [
|
|
|
53
53
|
"cryptography>=50.0.0",
|
|
54
54
|
# LiteLLM 1.95 imports outbound-credential tagged unions at startup.
|
|
55
55
|
"expression>=5.6.0,<6",
|
|
56
|
-
# Keep
|
|
57
|
-
# behavior is verified separately from this LiteLLM/model-support update.
|
|
56
|
+
# Keep FastAPI's lock-proven route-tree behavior; upgrade it separately.
|
|
58
57
|
"fastapi>=0.136.3,<0.137",
|
|
59
58
|
"fastapi-sso>=0.19.0,<1",
|
|
60
59
|
"orjson>=3.11.6,<4",
|
|
@@ -63,7 +62,8 @@ dependencies = [
|
|
|
63
62
|
"python-multipart>=0.0.31,<1",
|
|
64
63
|
# LiteLLM 1.95 imports its Redis support from the default startup graph.
|
|
65
64
|
"redis>=5.2.0,<8",
|
|
66
|
-
|
|
65
|
+
# Starlette 1.7 fixes TestClient's deprecated AnyIO BlockingPortal import.
|
|
66
|
+
"starlette>=1.7.0,<1.8",
|
|
67
67
|
"uvicorn>=0.33.0,<1",
|
|
68
68
|
"uvloop>=0.21.0,<1; sys_platform != 'win32'",
|
|
69
69
|
"watchfiles>=0.20",
|
|
@@ -109,6 +109,17 @@ def _render(summary: SessionActivitySummary, *, period: str) -> None:
|
|
|
109
109
|
"not your full interactive session.[/dim]"
|
|
110
110
|
)
|
|
111
111
|
|
|
112
|
+
if summary.turn_review:
|
|
113
|
+
turn = summary.turn_review
|
|
114
|
+
console.print(
|
|
115
|
+
f"[bold]Stop review[/bold]: {turn['state']} · spent={turn.get('spent', 'unknown')} · {turn.get('reason') or 'bounded snapshot'}"
|
|
116
|
+
)
|
|
117
|
+
if summary.supervisor_reviews:
|
|
118
|
+
latest = summary.supervisor_reviews[0]
|
|
119
|
+
console.print(
|
|
120
|
+
f"Supervisor review: {latest['state']} at {latest['started_at']} ({latest.get('reason') or latest.get('verdict') or 'running'})"
|
|
121
|
+
)
|
|
122
|
+
|
|
112
123
|
pol = summary.policy
|
|
113
124
|
# Fail-open breakdown is ledger-derived (the supervisor CommandUsage), so the
|
|
114
125
|
# Supervisor line must render even when there is no decision log (pol is None) --
|
|
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
# The Click group — imported by cli/main.py
|
|
17
17
|
from ._group import hooks
|
|
18
|
+
from . import codex_stop as _codex_stop # noqa: F401 -- registers dedicated Codex turn hooks
|
|
18
19
|
from . import commands as _commands # noqa: F401 — registers @hooks.command() decorators
|
|
19
20
|
from .verification import (
|
|
20
21
|
_get_last_assistant_text_for_verification,
|
|
@@ -48,6 +48,7 @@ class PatchFileOp:
|
|
|
48
48
|
move_to: str | None
|
|
49
49
|
added_content: str # introduced lines ("" for delete)
|
|
50
50
|
raw_section: str # verbatim header + body (raw_diff source)
|
|
51
|
+
source_path: str | None = None
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
@dataclass
|
|
@@ -65,6 +66,7 @@ class _Section:
|
|
|
65
66
|
move_to=self.move_to,
|
|
66
67
|
added_content="" if self.kind == "delete" else _extract_codex_added_lines(self.body),
|
|
67
68
|
raw_section="\n".join(self.header_lines + self.body),
|
|
69
|
+
source_path=self.path if self.move_to else None,
|
|
68
70
|
)
|
|
69
71
|
|
|
70
72
|
|
|
@@ -18,6 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
import json
|
|
19
19
|
import os
|
|
20
20
|
from collections.abc import Callable
|
|
21
|
+
from dataclasses import replace
|
|
21
22
|
from pathlib import Path
|
|
22
23
|
from typing import Any
|
|
23
24
|
|
|
@@ -42,6 +43,11 @@ class CodexHookAdapter:
|
|
|
42
43
|
|
|
43
44
|
ORIGIN = "codex"
|
|
44
45
|
|
|
46
|
+
def __init__(self, *, native_review: bool = False) -> None:
|
|
47
|
+
# Only native review consumes the executor boundary; deriving it spawns a
|
|
48
|
+
# process probe, which ordinary policy checks must not pay per edit.
|
|
49
|
+
self._native_review = native_review
|
|
50
|
+
|
|
45
51
|
def build_contexts(self, payload: dict[str, Any], tool_name: str, manifest: Any) -> list[ActionContext]:
|
|
46
52
|
"""Build per-file ``ActionContext``s from a Codex PreToolUse payload ([] if unbuildable)."""
|
|
47
53
|
if tool_name != "apply_patch":
|
|
@@ -63,7 +69,16 @@ class CodexHookAdapter:
|
|
|
63
69
|
cwd = Path(payload_cwd) if isinstance(payload_cwd, str) and payload_cwd else Path(os.getcwd())
|
|
64
70
|
cwd = cwd.resolve()
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
boundary = None
|
|
73
|
+
if self._native_review:
|
|
74
|
+
from forge.core.runtime.codex_review_source import boundary_from_hook
|
|
75
|
+
|
|
76
|
+
boundary = boundary_from_hook(payload)
|
|
77
|
+
return [
|
|
78
|
+
replace(self._context_for_op(op, cwd, manifest), native_boundary=boundary)
|
|
79
|
+
for op in ops
|
|
80
|
+
if op.kind != "delete"
|
|
81
|
+
]
|
|
67
82
|
|
|
68
83
|
def _context_for_op(self, op: PatchFileOp, cwd: Path, manifest: Any) -> ActionContext:
|
|
69
84
|
# Normalize to the tool names every policy's applies_to expects.
|
|
@@ -150,15 +165,10 @@ class CodexHookResponder:
|
|
|
150
165
|
return self._deny_wire(reason)
|
|
151
166
|
|
|
152
167
|
def allow_feedback(self, additional_context: str) -> dict[str, Any]:
|
|
153
|
-
"""Build
|
|
154
|
-
|
|
155
|
-
Not emitted in Phase 3: PreToolUse ``additionalContext`` delivery on allow is
|
|
156
|
-
unprobed (only SessionStart's is confirmed), so the command allows silently.
|
|
157
|
-
"""
|
|
168
|
+
"""Build bare context: explicit allow suppresses delivery on measured Codex."""
|
|
158
169
|
return {
|
|
159
170
|
"hookSpecificOutput": {
|
|
160
171
|
"hookEventName": "PreToolUse",
|
|
161
|
-
"permissionDecision": "allow",
|
|
162
172
|
"additionalContext": additional_context,
|
|
163
173
|
}
|
|
164
174
|
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Pure, bounded audience projection of already evaluated Codex policy results."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import asdict, dataclass
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from forge.cli.hooks.policy import DENY_NOTE, NEEDS_REVIEW_GUIDANCE
|
|
11
|
+
from forge.policy.semantic.stop_review import StopOutcome
|
|
12
|
+
from forge.policy.types import CompositeDecision, PolicyDecision, Violation
|
|
13
|
+
|
|
14
|
+
FIELD_CHARS = 600
|
|
15
|
+
FINDING_BYTES = 2400
|
|
16
|
+
FINDING_LIMIT = 12
|
|
17
|
+
MODEL_BYTES = 12000
|
|
18
|
+
OPERATOR_BYTES = 5000
|
|
19
|
+
WIRE_BYTES = 20000
|
|
20
|
+
_INSPECT = "Inspect: forge policy supervisor status --json; forge session show <session> --json."
|
|
21
|
+
_EVIDENCE_NOTICE = "Policy evidence could not be saved; the verdict is unchanged."
|
|
22
|
+
_NO_QUOTE = "No verified plan quotation is available."
|
|
23
|
+
_UNREVIEWED = "Policy review unavailable; this allowed action is unreviewed."
|
|
24
|
+
_RECOVERY = {
|
|
25
|
+
"timeout": "Reviewer timed out. Check supervisor status and its timeout setting.",
|
|
26
|
+
"subscription_exhausted": "Reviewer subscription exhausted. Restore quota before relying on review.",
|
|
27
|
+
"auth_unavailable": "Reviewer authentication unavailable. Check the configured reviewer login.",
|
|
28
|
+
"config": "Reviewer configuration is invalid. Check supervisor status.",
|
|
29
|
+
"plan_missing": "Approved plan is unavailable. Restore the configured plan file.",
|
|
30
|
+
"parse_failure": "Reviewer response was invalid. Check supervisor status and review evidence.",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class _Finding:
|
|
36
|
+
path: str | None
|
|
37
|
+
policy: str
|
|
38
|
+
violation: Violation | None = None
|
|
39
|
+
intent: str | None = None
|
|
40
|
+
warning: str | None = None
|
|
41
|
+
diagnostic: str | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_policy_feedback(
|
|
45
|
+
file_results: list[tuple[str | None, CompositeDecision]],
|
|
46
|
+
*,
|
|
47
|
+
model_feedback: bool,
|
|
48
|
+
source_only: bool,
|
|
49
|
+
supported: bool,
|
|
50
|
+
persistence_failed: bool = False,
|
|
51
|
+
) -> str | None:
|
|
52
|
+
"""Select audiences without altering verdicts, records, or policy state.
|
|
53
|
+
|
|
54
|
+
New allow channels require measured executor identity. Blocking output keeps
|
|
55
|
+
the existing wire on every version. All arbitrary text is quoted JSON data;
|
|
56
|
+
quoting is attribution, not a claim of prompt-injection immunity.
|
|
57
|
+
"""
|
|
58
|
+
denied = [(p, r) for p, r in file_results if r.final_decision == "deny"]
|
|
59
|
+
review = [(p, r) for p, r in file_results if r.final_decision == "needs_review"]
|
|
60
|
+
selected = denied or review or file_results
|
|
61
|
+
blocked = bool(denied or review)
|
|
62
|
+
findings, notices = _collect(selected, blocked=blocked)
|
|
63
|
+
if persistence_failed:
|
|
64
|
+
notices.append(_Finding(None, "forge.policy", diagnostic="evidence_unavailable"))
|
|
65
|
+
title = (
|
|
66
|
+
"Policy blocked this patch."
|
|
67
|
+
if denied
|
|
68
|
+
else (
|
|
69
|
+
"Policy review required; this patch remains blocked."
|
|
70
|
+
if review
|
|
71
|
+
else "Policy warnings for an allowed action."
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
model_title = title + " The following JSON entries are quoted policy evidence."
|
|
75
|
+
if denied:
|
|
76
|
+
model_title += " " + DENY_NOTE
|
|
77
|
+
elif review:
|
|
78
|
+
model_title += " " + NEEDS_REVIEW_GUIDANCE
|
|
79
|
+
wire: dict[str, Any] = {}
|
|
80
|
+
if blocked or (supported and model_feedback and findings):
|
|
81
|
+
context = _channel(findings, model_title, MODEL_BYTES, source_only=source_only, blocked=blocked)
|
|
82
|
+
hook: dict[str, str] = {"hookEventName": "PreToolUse"}
|
|
83
|
+
if blocked:
|
|
84
|
+
hook.update(permissionDecision="deny", permissionDecisionReason=context)
|
|
85
|
+
else:
|
|
86
|
+
hook["additionalContext"] = context
|
|
87
|
+
wire["hookSpecificOutput"] = hook
|
|
88
|
+
if supported and (findings or notices):
|
|
89
|
+
wire["systemMessage"] = _channel(findings + notices, title, OPERATOR_BYTES, source_only=False, operator=True)
|
|
90
|
+
if not wire:
|
|
91
|
+
return None
|
|
92
|
+
serialized = _json(wire)
|
|
93
|
+
# Field budgets count JSON-encoded strings, including escape expansion.
|
|
94
|
+
assert len(serialized.encode("utf-8")) <= WIRE_BYTES
|
|
95
|
+
return serialized
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def render_stop_feedback(outcome: StopOutcome, *, source_only: bool, model_feedback: bool) -> str | None:
|
|
99
|
+
"""Stop blocks reach the model as user authority; notices are operator-only.
|
|
100
|
+
|
|
101
|
+
There is no measured non-blocking model channel for Stop. Summary-off thus
|
|
102
|
+
affects no operator notice and never removes required correction guidance.
|
|
103
|
+
"""
|
|
104
|
+
wire: dict[str, Any] = {}
|
|
105
|
+
decision = outcome.decision
|
|
106
|
+
findings = _violations(None, decision, decision.violations or decision.warning_findings) if decision else []
|
|
107
|
+
if outcome.correction and decision is not None:
|
|
108
|
+
title = (
|
|
109
|
+
"Forge's supervisor found divergence in changes already made. "
|
|
110
|
+
"Address the verified plan findings, then finish. This turn receives no further semantic review. "
|
|
111
|
+
+ DENY_NOTE
|
|
112
|
+
+ " The following JSON entries are quoted policy evidence."
|
|
113
|
+
)
|
|
114
|
+
if not findings:
|
|
115
|
+
title += " No verified finding is available. Stop and ask the operator."
|
|
116
|
+
wire.update(
|
|
117
|
+
decision="block",
|
|
118
|
+
reason=_channel(findings, title, MODEL_BYTES, source_only=source_only, blocked=True),
|
|
119
|
+
)
|
|
120
|
+
title = {
|
|
121
|
+
"reviewed": "Forge reviewed the bounded repository snapshot; later changes are unreviewed.",
|
|
122
|
+
"no_changes": "Forge Stop review skipped: no changes or outstanding work in the captured scope.",
|
|
123
|
+
"unreviewed": "Forge Stop review is stale or has unreviewed continuation changes.",
|
|
124
|
+
"incomplete": "Forge Stop review incomplete; this turn's review allowance is spent.",
|
|
125
|
+
"partial": "Forge reviewed only the available evidence. Omitted work remains unreviewed; no correction was requested.",
|
|
126
|
+
}.get(outcome.status, "Forge Stop review unavailable; work is unreviewed.")
|
|
127
|
+
if outcome.correction:
|
|
128
|
+
title = "Forge requested one correction; the result and subsequent changes remain unreviewed."
|
|
129
|
+
if outcome.reason == "carry_limit":
|
|
130
|
+
title += (
|
|
131
|
+
" Pending work reached the carry limit. Preview forge policy supervisor reset-review before excluding it."
|
|
132
|
+
)
|
|
133
|
+
elif outcome.reason in {"incomplete_evidence", "missing_turn_start"}:
|
|
134
|
+
title += " Inspect supervisor status for excluded scope. A new explicit turn reviews new work, not the excluded history."
|
|
135
|
+
elif outcome.reason == "new_explicit_turn_required":
|
|
136
|
+
title += " Submit a new explicit turn to start after the requested reset."
|
|
137
|
+
elif outcome.status not in {"reviewed", "no_changes"}:
|
|
138
|
+
title += " Submit a new explicit turn to assess outstanding work; inspect supervisor status for unavailable evidence."
|
|
139
|
+
wire["systemMessage"] = _channel(findings, title, OPERATOR_BYTES, source_only=False, operator=True)
|
|
140
|
+
serialized = _json(wire)
|
|
141
|
+
assert len(serialized.encode()) <= WIRE_BYTES
|
|
142
|
+
return serialized
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def decision_is_unreviewed(decision: PolicyDecision) -> bool:
|
|
146
|
+
"""An escalation failure alone does not make the final review unavailable."""
|
|
147
|
+
return decision.decision != "needs_review" and bool(decision.fail_open or decision.failure_type)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _collect(
|
|
151
|
+
results: list[tuple[str | None, CompositeDecision]], *, blocked: bool
|
|
152
|
+
) -> tuple[list[_Finding], list[_Finding]]:
|
|
153
|
+
findings: list[_Finding] = []
|
|
154
|
+
notices: list[_Finding] = []
|
|
155
|
+
for path, result in results:
|
|
156
|
+
for decision in result.decisions:
|
|
157
|
+
codes = decision.diagnostic_codes
|
|
158
|
+
if "evidence_unavailable" in codes or decision.failure_type == "evidence_unavailable":
|
|
159
|
+
notices.append(_Finding(path, decision.policy_id, diagnostic="evidence_unavailable"))
|
|
160
|
+
if blocked:
|
|
161
|
+
if decision.decision != result.final_decision:
|
|
162
|
+
continue
|
|
163
|
+
findings.extend(_violations(path, decision, decision.violations))
|
|
164
|
+
if not decision.violations:
|
|
165
|
+
findings.append(_Finding(path, decision.policy_id, diagnostic="unresolved"))
|
|
166
|
+
elif (
|
|
167
|
+
decision.decision == "needs_review"
|
|
168
|
+
or decision.failure_type in ("skipped", "evidence_unavailable")
|
|
169
|
+
or "unconfigured" in codes
|
|
170
|
+
):
|
|
171
|
+
continue
|
|
172
|
+
elif decision_is_unreviewed(decision):
|
|
173
|
+
findings.append(_Finding(path, decision.policy_id, diagnostic=decision.failure_type or "unavailable"))
|
|
174
|
+
elif decision.warning_findings:
|
|
175
|
+
findings.extend(_violations(path, decision, decision.warning_findings))
|
|
176
|
+
elif not codes:
|
|
177
|
+
# Legacy/free-form warnings retain their owner; source-only never
|
|
178
|
+
# treats a plain string as a verified quotation or authored policy.
|
|
179
|
+
findings.extend(_Finding(path, decision.policy_id, warning=w) for w in decision.warnings)
|
|
180
|
+
if blocked and not any(f.path == path for f in findings):
|
|
181
|
+
findings.append(_Finding(path, "forge.policy", diagnostic="unresolved"))
|
|
182
|
+
return findings, notices
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _violations(path: str | None, decision: PolicyDecision, violations: list[Violation]) -> list[_Finding]:
|
|
186
|
+
return [_Finding(path, decision.policy_id, v, decision.intent) for v in violations]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _json(value: Any) -> str:
|
|
190
|
+
return json.dumps(value, ensure_ascii=True, separators=(",", ":"))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _projection(
|
|
194
|
+
finding: _Finding, *, source_only: bool, limit: int, operator: bool, blocked: bool
|
|
195
|
+
) -> tuple[dict[str, Any], bool]:
|
|
196
|
+
truncated = False
|
|
197
|
+
|
|
198
|
+
def clip(value: object) -> str:
|
|
199
|
+
nonlocal truncated
|
|
200
|
+
text = value if isinstance(value, str) else ""
|
|
201
|
+
if len(text) > limit:
|
|
202
|
+
truncated = True
|
|
203
|
+
return text[:limit]
|
|
204
|
+
|
|
205
|
+
v = finding.violation
|
|
206
|
+
rule = v.rule_id if v else finding.policy
|
|
207
|
+
data: dict[str, Any] = {"path": clip(finding.path), "policy": clip(finding.policy), "rule": clip(rule)}
|
|
208
|
+
# A shortened path remains distinguishable from another path with the same prefix.
|
|
209
|
+
if finding.path and len(finding.path) > limit:
|
|
210
|
+
data["path_sha256"] = hashlib.sha256(finding.path.encode()).hexdigest()
|
|
211
|
+
for key, value in (("rule", rule), ("policy", finding.policy)):
|
|
212
|
+
if len(value) > limit:
|
|
213
|
+
data[key + "_sha256"] = hashlib.sha256(value.encode()).hexdigest()
|
|
214
|
+
data["label"] = clip(f"{finding.path or '?'}: [{rule}]")
|
|
215
|
+
if finding.diagnostic:
|
|
216
|
+
code = finding.diagnostic
|
|
217
|
+
if code == "evidence_unavailable":
|
|
218
|
+
data["status"] = _EVIDENCE_NOTICE
|
|
219
|
+
elif code == "unresolved":
|
|
220
|
+
data["status"] = "Review unresolved; this patch remains blocked. " + _NO_QUOTE
|
|
221
|
+
else:
|
|
222
|
+
data["status"] = _UNREVIEWED
|
|
223
|
+
if operator:
|
|
224
|
+
data["recovery"] = _RECOVERY.get(
|
|
225
|
+
code, "Reviewer unavailable. Check supervisor status and review evidence."
|
|
226
|
+
)
|
|
227
|
+
elif source_only and (v is None or v.provenance != "policy"):
|
|
228
|
+
quotes = []
|
|
229
|
+
if v is not None:
|
|
230
|
+
for quote in v.verified_citations[:3]:
|
|
231
|
+
text = clip(quote.text)
|
|
232
|
+
quotes.append(
|
|
233
|
+
{
|
|
234
|
+
"source": clip(quote.source),
|
|
235
|
+
"sha256": quote.digest,
|
|
236
|
+
"start": quote.start,
|
|
237
|
+
"end": quote.start + len(text),
|
|
238
|
+
"text": text,
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
truncated |= len(v.verified_citations) > 3
|
|
242
|
+
data["status"] = (
|
|
243
|
+
"Policy finding; verified approved-plan quotations follow."
|
|
244
|
+
if quotes
|
|
245
|
+
else "Policy reported a finding. " + _NO_QUOTE
|
|
246
|
+
)
|
|
247
|
+
data["quotes"] = quotes
|
|
248
|
+
if blocked and not quotes:
|
|
249
|
+
data["status"] += " Stop and ask the operator before retrying this finding."
|
|
250
|
+
elif v is not None:
|
|
251
|
+
data.update(
|
|
252
|
+
severity=clip(v.severity),
|
|
253
|
+
message=clip(v.message),
|
|
254
|
+
intent=clip(finding.intent),
|
|
255
|
+
evidence=clip(v.evidence),
|
|
256
|
+
suggested_fix=clip(v.suggested_fix),
|
|
257
|
+
)
|
|
258
|
+
data["citations"] = [clip(c) for c in v.citations[:3]]
|
|
259
|
+
truncated |= len(v.citations) > 3
|
|
260
|
+
else:
|
|
261
|
+
data["message"] = clip(finding.warning)
|
|
262
|
+
if truncated:
|
|
263
|
+
data["truncated"] = True
|
|
264
|
+
return data, truncated
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _channel(
|
|
268
|
+
findings: list[_Finding],
|
|
269
|
+
title: str,
|
|
270
|
+
budget: int,
|
|
271
|
+
*,
|
|
272
|
+
source_only: bool,
|
|
273
|
+
operator: bool = False,
|
|
274
|
+
blocked: bool = False,
|
|
275
|
+
) -> str:
|
|
276
|
+
# Deduplicate the full attributed finding before truncation, within this response only.
|
|
277
|
+
unique = {_json(asdict(f)): f for f in findings}
|
|
278
|
+
ordered = sorted(
|
|
279
|
+
unique.values(), key=lambda f: (f.path or "", f.policy, f.violation.rule_id if f.violation else "")
|
|
280
|
+
)
|
|
281
|
+
lines: list[str] = []
|
|
282
|
+
omitted = 0
|
|
283
|
+
truncated = 0
|
|
284
|
+
footer = "\n" + _INSPECT if operator else ""
|
|
285
|
+
|
|
286
|
+
def assemble(skipped: int, shortened: int) -> str:
|
|
287
|
+
return (
|
|
288
|
+
title
|
|
289
|
+
+ "\n"
|
|
290
|
+
+ "\n".join(lines)
|
|
291
|
+
+ f"\nOmitted findings: {skipped}. Truncated findings: {shortened}."
|
|
292
|
+
+ footer
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
for finding in ordered:
|
|
296
|
+
if len(lines) >= FINDING_LIMIT:
|
|
297
|
+
omitted += 1
|
|
298
|
+
continue
|
|
299
|
+
limit = FIELD_CHARS
|
|
300
|
+
data, shortened = _projection(finding, source_only=source_only, limit=limit, operator=operator, blocked=blocked)
|
|
301
|
+
while len(_json(data).encode()) > FINDING_BYTES and limit > 8:
|
|
302
|
+
limit //= 2
|
|
303
|
+
data, shortened = _projection(
|
|
304
|
+
finding, source_only=source_only, limit=limit, operator=operator, blocked=blocked
|
|
305
|
+
)
|
|
306
|
+
line = _json(data)
|
|
307
|
+
if len(line.encode()) > FINDING_BYTES:
|
|
308
|
+
omitted += 1
|
|
309
|
+
continue
|
|
310
|
+
lines.append(line)
|
|
311
|
+
# Reserve final counters at their maximum length before accepting a finding.
|
|
312
|
+
if len(_json(assemble(len(ordered), len(ordered))).encode()) > budget:
|
|
313
|
+
lines.pop()
|
|
314
|
+
omitted += 1
|
|
315
|
+
else:
|
|
316
|
+
truncated += int(shortened)
|
|
317
|
+
return assemble(omitted, truncated)
|