multi-forge 0.8.0__tar.gz → 0.8.1__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-0.8.0 → multi_forge-0.8.1}/PKG-INFO +1 -1
- {multi_forge-0.8.0 → multi_forge-0.8.1}/pyproject.toml +1 -1
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/commands.py +1 -1
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/direct_commands.py +2 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/policy.py +2 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory.py +118 -37
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/policy.py +2 -3
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_lane.py +4 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/__init__.py +3 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/__init__.py +1 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/io.py +28 -3
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/codex_hooks.py +2 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/supervisor.py +11 -12
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/consumer_lanes.py +11 -8
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/overrides.py +3 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/passport.py +232 -16
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/project_memory.py +15 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/store.py +2 -1
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/16-memory.md +81 -2
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist.md +8 -6
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/resources/checklist.md +100 -5
- {multi_forge-0.8.0 → multi_forge-0.8.1}/.gitignore +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/LICENSE +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/NOTICE +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/README.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/agents/.gitkeep +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/commands/.gitkeep +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/adapters/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/adapters/litellm.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/creation.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/base.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/openrouter.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/sources.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/activity.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/auth.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/backend.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/claude.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/codex.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/config_cmd.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/consumer_lane_freeze.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/editor.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/extensions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/gc.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/guards.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/_group.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/_helpers.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_patch.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_policy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/protocols.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/verification.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/logs.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/main.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory_report.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory_writer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/model.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/output.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy_audit.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy_costs.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/runtime.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/search.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_codex.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_fork.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_lifecycle.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_manage.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_memory.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_model_pin.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_resume_modes.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_rewind.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_routing.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_supervisor_options.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/status_line.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/context.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/names.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/palette.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/throttle.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/telemetry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/trace.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/transfer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/workflow.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/dataclass_utils.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/backends/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/backends/litellm.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/loader.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/schema.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/capabilities.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/credentials_file.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/protocols.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/secrets.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/template_secrets.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/backend_dependency.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/credential_registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/model_catalog.yaml +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/effort.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/_lifecycle.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/claude.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/codex.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/codex_stream.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/lanes.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/base.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/litellm.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/openai_compat.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/openrouter.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/credentials.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/detection.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/errors.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/protocols.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/logging.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/catalog.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/direct_model.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/naming.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/backend_reconcile.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/claude_session.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_bridge.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_enrollment.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_interactive.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_session.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/context.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/gc.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/policy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/provider_trace.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/proxy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/resolution.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/session.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/session_context.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/transfer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/usage_summary.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/paths.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/process.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/provider_types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/cost_tracking.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/env.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/headless_json.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/proxy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/routing.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/session_runner.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/structured_output.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/tagger.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/throttle.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/run_id.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_preflight.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_preflight_cache.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime_vocab.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/exceptions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/lock.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/retention.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/timestamps.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/versioned_store.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/caps.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/downstream.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/jsonl_io.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/upstream.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/vocabulary.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/tiers.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/transcript.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/typing_helpers.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/billing.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/correlation.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/emit.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/ledger.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/measurement.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/vocabulary.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/queue.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/cli.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/doctor.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/exceptions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hook_dispatcher.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hook_migration.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hooks.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/installer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/models.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/preset.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/project_compat.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/project_registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/settings_merge.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/tracking.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/version.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/base.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/registry.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/tdd.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/engine.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/protocols.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/queries.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/plan_check.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/shadow.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/shadow_runner.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/verdict.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/store.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/supervisor_lane_degrade.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/config.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/handlers.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/prompts.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/types.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/branches.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/config.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/divergence.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/policy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/stages.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/audit_logger.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/base_client.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/client_adapter.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/client_factory.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/converters.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/cost_logger.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/cost_tracker.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/data_models.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/error_hints.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/intercept.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/metrics.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/model_spec.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/passthrough.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/ports.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/provider_trace_logger.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxies.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_identity.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_orchestrator.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_startup.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/responses_ingress.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/responses_passthrough.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/server.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/stream_relay.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/utils.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/adversarial.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/consensus.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/engine.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/models.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-performance.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-quick.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-security.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/debate_evaluation.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/docreview-quick.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/docreview.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/thinkdeep.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/routing.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/synthesis.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/runtime_config.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/bm25_store.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/content_store.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/engine.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/exceptions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/extractor.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/index_state.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/store.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/tokenizer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/active.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/addendum.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/artifacts.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/cleanup.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/invoke.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/paths.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/relocate.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/cleanup.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/codex_handoff.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/codex_invoke.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/config.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/context_limit.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/effective.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/exceptions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/models.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/session_start.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/identity.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/index.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/launch.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/launch_confirmation.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/manager.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/memory_inheritance.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/memory_writer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/model_pin.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/models.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/plan_resolution.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/prev_sessions.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/rewind.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/shadow_curation.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/transfer.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/validation.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/cleanup.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/config_copy.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/create.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/__init__.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/container.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/docker.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/analyze/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/challenge/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/consensus/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/consensus/resources/synthesis.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/debate/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/panel/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/panel/resources/synthesis.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/0-enable.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/10-resume.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/11-config.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/12-search.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/13-policy.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/15-skills.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/17-info.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/18-disable.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/2-extension.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/4-proxy.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/5-session.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/6-hook.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/7-costs.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/report-template.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/scripts/start-container.sh +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/scripts/walkthrough-state.py +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/claude-4.6.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/claude-4.8.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/gemini-3.1.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/gpt-5.5.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/skills-writing-guide.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-anthropic.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-gemini.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-openai.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/smoke-test/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-anthropic.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-gemini.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-openai.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-gemini.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-openai.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/SKILL.md +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
- {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/walkthrough-state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-forge
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -1016,7 +1016,7 @@ def worktree_create() -> None:
|
|
|
1016
1016
|
# Fallback: try without -b (use detached HEAD)
|
|
1017
1017
|
logger.debug("worktree-create: branched creation failed: %s", result.stderr.strip())
|
|
1018
1018
|
result = subprocess.run(
|
|
1019
|
-
[git, "worktree", "add", str(worktree_path)],
|
|
1019
|
+
[git, "worktree", "add", "--detach", str(worktree_path)],
|
|
1020
1020
|
cwd=str(repo_root),
|
|
1021
1021
|
capture_output=True,
|
|
1022
1022
|
text=True,
|
|
@@ -770,8 +770,8 @@ def _handle_policy_supervisor(argv: list[str]) -> None:
|
|
|
770
770
|
- ``%policy supervisor``: show current config (including the bound consumer lane)
|
|
771
771
|
|
|
772
772
|
The consumer-lane *runtime* is intentionally not settable here -- it is a resolving-command
|
|
773
|
-
flag (``forge policy supervisor set --runtime``) so the binding stays write-once.
|
|
774
|
-
only shows the bound lane.
|
|
773
|
+
flag (``forge policy supervisor set <target> --runtime``) so the binding stays write-once.
|
|
774
|
+
This path only shows the bound lane.
|
|
775
775
|
"""
|
|
776
776
|
cwd = Path.cwd().resolve()
|
|
777
777
|
store = resolve_session_store(cwd)
|
|
@@ -172,8 +172,8 @@ def register_supervisor_and_restore(engine: Any, effective: Any, manifest: Any)
|
|
|
172
172
|
|
|
173
173
|
Returns the supervisor's injected consumer-lane binding (None when no supervisor is
|
|
174
174
|
configured, or when it resolves to the default lane). The caller threads this same
|
|
175
|
-
value into the post-eval freeze so the binding records the lane
|
|
176
|
-
|
|
175
|
+
value into the post-eval freeze so the binding records the lane committed for this
|
|
176
|
+
registered check -- not a fresh manifest read that could race an intent change.
|
|
177
177
|
"""
|
|
178
178
|
sup = effective.policy.supervisor if effective.policy else None
|
|
179
179
|
has_supervisor = bool(sup and sup.resume_id and not sup.suspended)
|
|
@@ -30,12 +30,17 @@ from forge.session.exceptions import (
|
|
|
30
30
|
from forge.session.passport import (
|
|
31
31
|
VALID_STRATEGY_NAMES,
|
|
32
32
|
Passport,
|
|
33
|
+
PreparedPassportWrite,
|
|
34
|
+
apply_prepared_passport_write,
|
|
33
35
|
derive_shadow_path,
|
|
36
|
+
prepare_passport_write,
|
|
34
37
|
read_passport,
|
|
35
38
|
remove_passport,
|
|
36
39
|
resolve_passport_source,
|
|
37
40
|
resolve_with_overrides,
|
|
38
41
|
synthesize_passport,
|
|
42
|
+
upgrade_passport_envelope,
|
|
43
|
+
validate_okf_reserved_basenames,
|
|
39
44
|
write_passport,
|
|
40
45
|
)
|
|
41
46
|
from forge.session.project_memory import (
|
|
@@ -87,6 +92,7 @@ def memory() -> None:
|
|
|
87
92
|
forge memory track docs/changelog.md --strategy changelog # author a passport (sessionless)
|
|
88
93
|
forge memory list # show passported docs
|
|
89
94
|
forge memory passport show docs/changelog.md # inspect a passport
|
|
95
|
+
forge memory passport upgrade docs/changelog.md # add the OKF envelope
|
|
90
96
|
forge memory shadows review --for docs/impl_notes.md # curate shadow proposals
|
|
91
97
|
"""
|
|
92
98
|
|
|
@@ -187,6 +193,9 @@ def track_cmd(
|
|
|
187
193
|
except PassportError as e:
|
|
188
194
|
raise click.ClickException(f"Malformed passport in {path}: {e}") from e
|
|
189
195
|
|
|
196
|
+
if passport is None and intent is not None and not intent.strip():
|
|
197
|
+
raise click.ClickException("--intent must be a non-empty string when creating a passport.")
|
|
198
|
+
|
|
190
199
|
# Scan roots power the out-of-root warning and the collision check. A
|
|
191
200
|
# corrupt config must not block authoring (system-boundary warn+degrade).
|
|
192
201
|
roots = _resolve_scan_roots()
|
|
@@ -233,9 +242,9 @@ def track_cmd(
|
|
|
233
242
|
new_pp = synthesize_passport(
|
|
234
243
|
strategy=strategy, # type: ignore[arg-type] # checked above
|
|
235
244
|
intent=intent,
|
|
236
|
-
writers=writers
|
|
245
|
+
writers=writers if writers is not None else "all-sessions",
|
|
237
246
|
)
|
|
238
|
-
write_passport(abs_path, new_pp)
|
|
247
|
+
write_passport(abs_path, new_pp, okf_path=path)
|
|
239
248
|
except PassportError as e:
|
|
240
249
|
raise click.ClickException(str(e)) from e
|
|
241
250
|
console.print(f"Passport created for [cyan]{path}[/cyan] (strategy: {strategy}).")
|
|
@@ -300,17 +309,16 @@ def _track_existing_shadow_only(
|
|
|
300
309
|
)
|
|
301
310
|
|
|
302
311
|
has_flags = strategy is not None or writers is not None
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
resolved_pp = passport
|
|
313
|
+
pp_warnings: list[str] = []
|
|
314
|
+
prepared: PreparedPassportWrite | None = None
|
|
315
|
+
try:
|
|
316
|
+
if has_flags:
|
|
306
317
|
resolved_pp, pp_warnings = resolve_with_overrides(passport, strategy=strategy, writers=writers)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
updated = True
|
|
312
|
-
for w in pp_warnings:
|
|
313
|
-
console.print(f"[yellow]Warning:[/yellow] {w}")
|
|
318
|
+
if pp_warnings:
|
|
319
|
+
prepared = prepare_passport_write(abs_path, resolved_pp)
|
|
320
|
+
except PassportError as exc:
|
|
321
|
+
raise click.ClickException(str(exc)) from exc
|
|
314
322
|
|
|
315
323
|
created = _auto_create_shadow(shadow_path, forge_root)
|
|
316
324
|
shadow_abs = (forge_root / shadow_path).resolve()
|
|
@@ -318,7 +326,11 @@ def _track_existing_shadow_only(
|
|
|
318
326
|
raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
|
|
319
327
|
if created:
|
|
320
328
|
console.print(f"Shadow file created: {shadow_path}.")
|
|
321
|
-
if
|
|
329
|
+
if pp_warnings:
|
|
330
|
+
assert prepared is not None
|
|
331
|
+
apply_prepared_passport_write(abs_path, prepared)
|
|
332
|
+
for w in pp_warnings:
|
|
333
|
+
console.print(f"[yellow]Warning:[/yellow] {w}")
|
|
322
334
|
console.print(f"Passport updated in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
|
|
323
335
|
else:
|
|
324
336
|
console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
|
|
@@ -353,6 +365,10 @@ def _track_propose(
|
|
|
353
365
|
# Self-shadow: compare resolved paths, not raw strings
|
|
354
366
|
resolved_shadow = (forge_root / shadow_path).resolve()
|
|
355
367
|
resolved_official = (forge_root / path).resolve()
|
|
368
|
+
try:
|
|
369
|
+
validate_okf_reserved_basenames(shadow_path, resolved_shadow)
|
|
370
|
+
except PassportError as exc:
|
|
371
|
+
raise click.ClickException(f"Invalid shadow path: {exc}") from exc
|
|
356
372
|
if resolved_shadow == resolved_official:
|
|
357
373
|
raise click.ClickException("Shadow path cannot be the same as the official doc.")
|
|
358
374
|
|
|
@@ -360,17 +376,15 @@ def _track_propose(
|
|
|
360
376
|
if collision:
|
|
361
377
|
raise click.ClickException(collision)
|
|
362
378
|
|
|
363
|
-
#
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if not shadow_abs.is_file():
|
|
367
|
-
raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
|
|
368
|
-
|
|
369
|
-
# Passport handling: synthesize or update.
|
|
379
|
+
# Prepare the complete effective passport before materializing the shadow.
|
|
380
|
+
# For existing passports this deliberately omits ``okf_path`` so re-track
|
|
381
|
+
# never validates, repairs, or adds an outer envelope.
|
|
370
382
|
# For existing passports, pass strategy only when the user explicitly provided --strategy
|
|
371
383
|
# so the passport's own strategy is preserved by default.
|
|
384
|
+
pp_warnings: list[str]
|
|
385
|
+
prepared: PreparedPassportWrite | None = None
|
|
386
|
+
result_kind: str
|
|
372
387
|
if isinstance(passport, Passport) and passport.update.mode == "shadow-only":
|
|
373
|
-
# Already shadow-only -- apply overrides if any
|
|
374
388
|
try:
|
|
375
389
|
resolved_pp, pp_warnings = resolve_with_overrides(
|
|
376
390
|
passport,
|
|
@@ -378,17 +392,12 @@ def _track_propose(
|
|
|
378
392
|
shadow_path=(shadow_path if shadow_path != passport.update.shadow_path else None),
|
|
379
393
|
writers=writers,
|
|
380
394
|
)
|
|
395
|
+
if pp_warnings:
|
|
396
|
+
prepared = prepare_passport_write(abs_path, resolved_pp)
|
|
381
397
|
except PassportError as e:
|
|
382
398
|
raise click.ClickException(str(e)) from e
|
|
383
|
-
if pp_warnings
|
|
384
|
-
write_passport(abs_path, resolved_pp)
|
|
385
|
-
for w in pp_warnings:
|
|
386
|
-
console.print(f"[yellow]Warning:[/yellow] {w}")
|
|
387
|
-
console.print(f"Passport updated in [cyan]{path}[/cyan]. Future sessions will use the new values.")
|
|
388
|
-
else:
|
|
389
|
-
console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
|
|
399
|
+
result_kind = "updated" if pp_warnings else "unchanged"
|
|
390
400
|
elif isinstance(passport, Passport):
|
|
391
|
-
# Convert existing direct passport to shadow-only
|
|
392
401
|
try:
|
|
393
402
|
resolved_pp, pp_warnings = resolve_with_overrides(
|
|
394
403
|
passport,
|
|
@@ -397,25 +406,45 @@ def _track_propose(
|
|
|
397
406
|
shadow_path=shadow_path,
|
|
398
407
|
writers=writers,
|
|
399
408
|
)
|
|
409
|
+
prepared = prepare_passport_write(abs_path, resolved_pp)
|
|
400
410
|
except PassportError as e:
|
|
401
411
|
raise click.ClickException(str(e)) from e
|
|
402
|
-
|
|
403
|
-
for w in pp_warnings:
|
|
404
|
-
console.print(f"[yellow]Warning:[/yellow] {w}")
|
|
405
|
-
console.print(f"Passport in [cyan]{path}[/cyan] converted to shadow-only proposals at {shadow_path}.")
|
|
412
|
+
result_kind = "converted"
|
|
406
413
|
else:
|
|
407
|
-
# No passport -- synthesize with default strategy
|
|
408
414
|
try:
|
|
409
415
|
new_pp = synthesize_passport(
|
|
410
416
|
strategy=new_passport_strategy,
|
|
411
417
|
intent=intent,
|
|
412
418
|
update_mode="shadow-only",
|
|
413
419
|
shadow_path=shadow_path,
|
|
414
|
-
writers=writers
|
|
420
|
+
writers=writers if writers is not None else "all-sessions",
|
|
415
421
|
)
|
|
416
|
-
|
|
422
|
+
prepared = prepare_passport_write(abs_path, new_pp, okf_path=path)
|
|
417
423
|
except PassportError as e:
|
|
418
424
|
raise click.ClickException(str(e)) from e
|
|
425
|
+
pp_warnings = []
|
|
426
|
+
result_kind = "created"
|
|
427
|
+
|
|
428
|
+
# Materialization begins only after path, collision, effective-passport,
|
|
429
|
+
# frontmatter, and (for new tracking) OKF envelope validation all succeed.
|
|
430
|
+
created = _auto_create_shadow(shadow_path, forge_root)
|
|
431
|
+
shadow_abs = (forge_root / shadow_path).resolve()
|
|
432
|
+
if not shadow_abs.is_file():
|
|
433
|
+
raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
|
|
434
|
+
|
|
435
|
+
if result_kind != "unchanged":
|
|
436
|
+
assert prepared is not None
|
|
437
|
+
apply_prepared_passport_write(abs_path, prepared)
|
|
438
|
+
|
|
439
|
+
for w in pp_warnings:
|
|
440
|
+
console.print(f"[yellow]Warning:[/yellow] {w}")
|
|
441
|
+
if result_kind == "updated":
|
|
442
|
+
console.print(f"Passport updated in [cyan]{path}[/cyan]. Future sessions will use the new values.")
|
|
443
|
+
elif result_kind == "unchanged":
|
|
444
|
+
console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
|
|
445
|
+
elif result_kind == "converted":
|
|
446
|
+
console.print(f"Passport in [cyan]{path}[/cyan] converted to shadow-only proposals at {shadow_path}.")
|
|
447
|
+
else:
|
|
419
448
|
console.print(
|
|
420
449
|
f"Shadow-only passport written for [cyan]{path}[/cyan] "
|
|
421
450
|
f"(strategy: {new_passport_strategy}, proposals at {shadow_path})."
|
|
@@ -1029,7 +1058,7 @@ def _review_curate(
|
|
|
1029
1058
|
|
|
1030
1059
|
@memory.group("passport")
|
|
1031
1060
|
def passport_group() -> None:
|
|
1032
|
-
"""Inspect and remove memory-doc passports."""
|
|
1061
|
+
"""Inspect, upgrade, and remove memory-doc passports."""
|
|
1033
1062
|
|
|
1034
1063
|
|
|
1035
1064
|
@passport_group.command("show")
|
|
@@ -1115,6 +1144,58 @@ def passport_show_cmd(path: str, as_json: bool) -> None:
|
|
|
1115
1144
|
console.print(table)
|
|
1116
1145
|
|
|
1117
1146
|
|
|
1147
|
+
@passport_group.command("upgrade")
|
|
1148
|
+
@click.argument("path")
|
|
1149
|
+
def passport_upgrade_cmd(path: str) -> None:
|
|
1150
|
+
"""Add missing OKF envelope fields to an existing passport."""
|
|
1151
|
+
try:
|
|
1152
|
+
ctx = ExecutionContext.from_cwd()
|
|
1153
|
+
except ForgeOpError as e:
|
|
1154
|
+
print_error(str(e), console=err_console)
|
|
1155
|
+
print_tip("Run this command from an enabled Forge project.", console=err_console)
|
|
1156
|
+
sys.exit(1)
|
|
1157
|
+
|
|
1158
|
+
if ctx.forge_root is None:
|
|
1159
|
+
print_error("Not inside a Forge project.", console=err_console)
|
|
1160
|
+
print_tip("Run 'forge extension enable' first.", console=err_console)
|
|
1161
|
+
sys.exit(1)
|
|
1162
|
+
|
|
1163
|
+
from forge.cli.guards import enforce_target_project_compatibility
|
|
1164
|
+
|
|
1165
|
+
enforce_target_project_compatibility(ctx.forge_root)
|
|
1166
|
+
|
|
1167
|
+
resolved_base = ctx.forge_root.resolve()
|
|
1168
|
+
reason = is_safe_designated_doc_path(path, ctx.forge_root, resolved_base)
|
|
1169
|
+
if reason:
|
|
1170
|
+
print_error(f"Invalid path: {reason}", console=err_console)
|
|
1171
|
+
print_tip(
|
|
1172
|
+
"Use a project-relative Markdown path inside this Forge project.",
|
|
1173
|
+
console=err_console,
|
|
1174
|
+
)
|
|
1175
|
+
sys.exit(1)
|
|
1176
|
+
|
|
1177
|
+
abs_path = (ctx.forge_root / path).resolve()
|
|
1178
|
+
if not abs_path.is_file():
|
|
1179
|
+
print_error(f"File not found: {path}", console=err_console)
|
|
1180
|
+
print_tip("Check the project-relative path and try again.", console=err_console)
|
|
1181
|
+
sys.exit(1)
|
|
1182
|
+
|
|
1183
|
+
try:
|
|
1184
|
+
added = upgrade_passport_envelope(abs_path, logical_path=path)
|
|
1185
|
+
except PassportError as e:
|
|
1186
|
+
print_error(f"Could not upgrade passport in {path}: {e}", console=err_console)
|
|
1187
|
+
print_tip(
|
|
1188
|
+
"Fix the reported frontmatter or path issue, then rerun this command.",
|
|
1189
|
+
console=err_console,
|
|
1190
|
+
)
|
|
1191
|
+
sys.exit(1)
|
|
1192
|
+
|
|
1193
|
+
if added:
|
|
1194
|
+
console.print(f"OKF envelope upgraded in [cyan]{path}[/cyan] (added: {', '.join(added)}).")
|
|
1195
|
+
else:
|
|
1196
|
+
console.print(f"OKF envelope already complete in [cyan]{path}[/cyan]; no changes made.")
|
|
1197
|
+
|
|
1198
|
+
|
|
1118
1199
|
@passport_group.command("remove")
|
|
1119
1200
|
@click.argument("path")
|
|
1120
1201
|
@click.option("--json", "as_json", is_flag=True, default=False, help="Output as JSON")
|
|
@@ -288,9 +288,8 @@ def enable(bundles: tuple[str, ...], fail_mode: str, permissive: bool, session_n
|
|
|
288
288
|
"""
|
|
289
289
|
if not bundles:
|
|
290
290
|
# Fail loud, not a silent no-op: the CLI is the explicit surface and requires
|
|
291
|
-
# the bundles to enable.
|
|
292
|
-
# bundles
|
|
293
|
-
# see design_workflows.md "Re-enable enforcement".
|
|
291
|
+
# the bundles to enable. The interactive `%policy enable` surface also requires
|
|
292
|
+
# explicit bundles; its distinction is that it writes overrides rather than intent.
|
|
294
293
|
print_error_with_tip(
|
|
295
294
|
"No policy bundles specified.",
|
|
296
295
|
"Use --bundle tdd and/or --bundle coding_standards.",
|
|
@@ -139,8 +139,10 @@ _SESSION_OPTION = click.option(
|
|
|
139
139
|
def session_lane() -> None:
|
|
140
140
|
"""Bind a consumer's LLM-work to a (runtime, backend, model) lane for the session.
|
|
141
141
|
|
|
142
|
-
Lanes are session-owned intent
|
|
143
|
-
|
|
142
|
+
Lanes are session-owned intent. An explicit supervisor lane freezes at its first
|
|
143
|
+
registered policy check; auxiliary-consumer lanes freeze at first real dispatch.
|
|
144
|
+
The supervisor also accepts
|
|
145
|
+
'forge policy supervisor set <target> --runtime/--backend' (same storage).
|
|
144
146
|
|
|
145
147
|
\b
|
|
146
148
|
Examples:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Shared LLM client abstraction for Forge components.
|
|
2
2
|
|
|
3
|
-
This module provides a unified, async-first interface for calling LLMs
|
|
4
|
-
|
|
3
|
+
This module provides a unified, async-first interface for calling LLMs through
|
|
4
|
+
remote/local LiteLLM endpoints and OpenRouter directly. A native Anthropic
|
|
5
|
+
client is not implemented yet; ``anthropic/`` model ids route through LiteLLM.
|
|
5
6
|
|
|
6
7
|
Usage:
|
|
7
8
|
# Async usage (Proxy)
|
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import os
|
|
14
|
+
import stat
|
|
14
15
|
import tempfile
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any
|
|
@@ -60,6 +61,7 @@ def atomic_write_text(
|
|
|
60
61
|
content: str,
|
|
61
62
|
*,
|
|
62
63
|
mode: int | None = None,
|
|
64
|
+
preserve_existing_mode: bool = False,
|
|
63
65
|
create_parents: bool = True,
|
|
64
66
|
) -> None:
|
|
65
67
|
"""Write text to a file atomically.
|
|
@@ -72,12 +74,21 @@ def atomic_write_text(
|
|
|
72
74
|
path: Target file path.
|
|
73
75
|
content: Text content to write.
|
|
74
76
|
mode: Optional final file mode (for example, 0o600).
|
|
77
|
+
preserve_existing_mode: Preserve the target's permission bits when it exists.
|
|
78
|
+
A missing target retains the secure tempfile default. Mutually exclusive with ``mode``.
|
|
75
79
|
create_parents: Create parent directories if they don't exist.
|
|
76
80
|
|
|
77
81
|
Raises:
|
|
82
|
+
ValueError: If ``mode`` and ``preserve_existing_mode`` are both requested.
|
|
78
83
|
OSError: If the write or rename fails.
|
|
79
84
|
"""
|
|
80
|
-
atomic_write_bytes(
|
|
85
|
+
atomic_write_bytes(
|
|
86
|
+
path,
|
|
87
|
+
content.encode("utf-8"),
|
|
88
|
+
mode=mode,
|
|
89
|
+
preserve_existing_mode=preserve_existing_mode,
|
|
90
|
+
create_parents=create_parents,
|
|
91
|
+
)
|
|
81
92
|
|
|
82
93
|
|
|
83
94
|
def atomic_write_bytes(
|
|
@@ -85,6 +96,7 @@ def atomic_write_bytes(
|
|
|
85
96
|
content: bytes,
|
|
86
97
|
*,
|
|
87
98
|
mode: int | None = None,
|
|
99
|
+
preserve_existing_mode: bool = False,
|
|
88
100
|
create_parents: bool = True,
|
|
89
101
|
) -> None:
|
|
90
102
|
"""Write bytes to a file atomically.
|
|
@@ -97,11 +109,24 @@ def atomic_write_bytes(
|
|
|
97
109
|
path: Target file path.
|
|
98
110
|
content: Bytes to write.
|
|
99
111
|
mode: Optional final file mode (for example, 0o600).
|
|
112
|
+
preserve_existing_mode: Preserve the target's permission bits when it exists.
|
|
113
|
+
A missing target retains the secure tempfile default. Mutually exclusive with ``mode``.
|
|
100
114
|
create_parents: Create parent directories if they don't exist.
|
|
101
115
|
|
|
102
116
|
Raises:
|
|
117
|
+
ValueError: If ``mode`` and ``preserve_existing_mode`` are both requested.
|
|
103
118
|
OSError: If the write or rename fails.
|
|
104
119
|
"""
|
|
120
|
+
if mode is not None and preserve_existing_mode:
|
|
121
|
+
raise ValueError("mode and preserve_existing_mode are mutually exclusive")
|
|
122
|
+
|
|
123
|
+
resolved_mode = mode
|
|
124
|
+
if preserve_existing_mode:
|
|
125
|
+
try:
|
|
126
|
+
resolved_mode = stat.S_IMODE(path.stat().st_mode)
|
|
127
|
+
except FileNotFoundError:
|
|
128
|
+
resolved_mode = None
|
|
129
|
+
|
|
105
130
|
if create_parents:
|
|
106
131
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
107
132
|
|
|
@@ -115,8 +140,8 @@ def atomic_write_bytes(
|
|
|
115
140
|
with os.fdopen(fd, "wb") as f:
|
|
116
141
|
f.write(content)
|
|
117
142
|
f.flush()
|
|
118
|
-
if
|
|
119
|
-
os.fchmod(f.fileno(),
|
|
143
|
+
if resolved_mode is not None:
|
|
144
|
+
os.fchmod(f.fileno(), resolved_mode)
|
|
120
145
|
os.fsync(f.fileno())
|
|
121
146
|
os.replace(temp_path, str(path))
|
|
122
147
|
try:
|
|
@@ -440,7 +440,7 @@ def apply_codex_merge(config_path: Path, entries: tuple[CodexHookEntry, ...]) ->
|
|
|
440
440
|
|
|
441
441
|
backup_path = backup_codex_config(config_path)
|
|
442
442
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
443
|
-
atomic_write_text(config_path, new_text)
|
|
443
|
+
atomic_write_text(config_path, new_text, preserve_existing_mode=True)
|
|
444
444
|
return backup_path
|
|
445
445
|
|
|
446
446
|
|
|
@@ -548,7 +548,7 @@ def remove_codex_block(config_path: Path, entries: tuple[CodexHookEntry, ...]) -
|
|
|
548
548
|
config_path.unlink()
|
|
549
549
|
return CodexRemoveResult(removed=True, deleted_file=True, leftover_commands=leftover)
|
|
550
550
|
|
|
551
|
-
atomic_write_text(config_path, new_text)
|
|
551
|
+
atomic_write_text(config_path, new_text, preserve_existing_mode=True)
|
|
552
552
|
return CodexRemoveResult(removed=True, leftover_commands=leftover)
|
|
553
553
|
|
|
554
554
|
|
|
@@ -109,15 +109,14 @@ _CLAUDE_MODEL_PIN_ENV_VARS = (
|
|
|
109
109
|
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
110
110
|
)
|
|
111
111
|
|
|
112
|
-
# The supervisor as a consumer-lane binding (epic consumer_lanes
|
|
113
|
-
#
|
|
114
|
-
# `
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
# `
|
|
118
|
-
# `anthropic-direct` is a real catalog source
|
|
119
|
-
# proxied supervisor, which routes through
|
|
120
|
-
# load-bearing; T4 adds the `codex` arm + a narrow `SupervisorConfig` override.
|
|
112
|
+
# The supervisor as a consumer-lane binding (epic consumer_lanes). It reads repo
|
|
113
|
+
# files (a `tool_agent` capability floor), and `runtime_id` selects the dispatch arm
|
|
114
|
+
# in `_dispatch_supervisor`: `claude_code` or `codex`. `backend_id`/`model` remain
|
|
115
|
+
# nominal placement metadata for dispatch: the Claude arm derives transport
|
|
116
|
+
# (`base_url`) and its `opus` pin dynamically, while Codex chooses its own model.
|
|
117
|
+
# `backend_id` is nevertheless load-bearing for billing (for example `claude-max`).
|
|
118
|
+
# `anthropic-direct` is a real catalog source; no single backend is "correct" for a
|
|
119
|
+
# proxied Claude supervisor, which routes through whichever proxy was resolved.
|
|
121
120
|
SUPERVISOR_CONSUMER = Consumer(
|
|
122
121
|
id="supervisor",
|
|
123
122
|
capability_floor="tool_agent",
|
|
@@ -494,9 +493,9 @@ def _dispatch_supervisor(
|
|
|
494
493
|
) -> SessionResult:
|
|
495
494
|
"""Dispatch one supervisor run on the resolved lane's runtime.
|
|
496
495
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
496
|
+
The shipped arms are ``claude_code`` (native resume, optionally proxied) and
|
|
497
|
+
``codex`` (fresh, plan-fed ``codex exec``). Any other runtime has no supervisor
|
|
498
|
+
adapter and fails open through ``_SupervisorRoutingError``.
|
|
500
499
|
"""
|
|
501
500
|
runtime = lane.runtime_id
|
|
502
501
|
if runtime == "claude_code":
|
|
@@ -81,7 +81,7 @@ def confirmed_lane(state: SessionState, consumer: Consumer) -> LaneRecord | None
|
|
|
81
81
|
"""Return ``consumer``'s frozen lane record, or None if not yet bound.
|
|
82
82
|
|
|
83
83
|
The resolving commands' already-bound reject reads this: a non-None result
|
|
84
|
-
means the lane is frozen, so ``policy supervisor set --runtime`` must refuse to
|
|
84
|
+
means the lane is frozen, so ``policy supervisor set <target> --runtime`` must refuse to
|
|
85
85
|
change it (the binding is immutable once written, card D2).
|
|
86
86
|
"""
|
|
87
87
|
binding = _confirmed_binding(state, consumer)
|
|
@@ -148,9 +148,10 @@ def set_intent_lane(state: SessionState, consumer: Consumer, lane_record: LaneRe
|
|
|
148
148
|
"""Record ``consumer``'s requested lane in ``intent`` (the resolving-command setter).
|
|
149
149
|
|
|
150
150
|
The write companion to ``read_bound_lane``'s intent branch: resolving commands
|
|
151
|
-
(start/fork ``--supervisor-runtime``, ``policy supervisor set --runtime``) call this
|
|
152
|
-
to record the requested placement.
|
|
153
|
-
|
|
151
|
+
(start/fork ``--supervisor-runtime``, ``policy supervisor set <target> --runtime``) call this
|
|
152
|
+
to record the requested placement. ``ensure_consumer_lane_binding`` later freezes the
|
|
153
|
+
lane at the consumer's lifecycle commitment point: first registered policy check for
|
|
154
|
+
the supervisor, first real dispatch for auxiliary consumers. Callers enforce the
|
|
154
155
|
already-bound reject (``confirmed_lane``) before invoking this -- it does not itself
|
|
155
156
|
guard against overwriting a request on an already-frozen session.
|
|
156
157
|
"""
|
|
@@ -192,10 +193,12 @@ def clear_intent_lane(state: SessionState, consumer: Consumer) -> None:
|
|
|
192
193
|
def ensure_consumer_lane_binding(state: SessionState, consumer: Consumer, lane_record: LaneRecord | None) -> None:
|
|
193
194
|
"""Freeze ``consumer``'s explicitly-chosen lane into ``confirmed``, write-if-absent.
|
|
194
195
|
|
|
195
|
-
The single immutability seam (card D2). ``lane_record`` is the lane
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
The single immutability seam (card D2). ``lane_record`` is the lane selected for the
|
|
197
|
+
consumer's lifecycle commitment (``read_bound_lane`` -- confirmed-first, else the intent
|
|
198
|
+
override), NOT a fresh manifest read. For the supervisor that commitment is the registered
|
|
199
|
+
policy check and may precede a runtime dispatch; auxiliary callers invoke this only after
|
|
200
|
+
committing to a real dispatch. The threaded value prevents a concurrent intent change from
|
|
201
|
+
freezing a lane the lifecycle event did not select.
|
|
199
202
|
|
|
200
203
|
**The default lane is never frozen.** ``lane_record is None`` means no explicit choice was
|
|
201
204
|
recorded (neither ``confirmed`` nor ``intent``) and the consumer ran on its default; we leave
|
|
@@ -209,7 +209,7 @@ def validate_key(key: str) -> list[str]:
|
|
|
209
209
|
|
|
210
210
|
if first_part == "consumer_lanes":
|
|
211
211
|
# Consumer-lane bindings (epic consumer_lanes, T1b) are set only through resolving commands
|
|
212
|
-
# (--supervisor-runtime at start/fork, 'forge policy supervisor set --runtime'), which expand a
|
|
212
|
+
# (--supervisor-runtime at start/fork, 'forge policy supervisor set <target> --runtime'), which expand a
|
|
213
213
|
# runtime to a full validated LaneRecord and enforce the already-bound reject. A raw override --
|
|
214
214
|
# partial (can't rehydrate a 3-field LaneRecord) or full-object (bypasses that validation, and
|
|
215
215
|
# after first dispatch is recorded-but-ignored since dispatch reads the frozen confirmed binding)
|
|
@@ -217,7 +217,8 @@ def validate_key(key: str) -> list[str]:
|
|
|
217
217
|
raise InvalidOverrideKeyError(
|
|
218
218
|
key,
|
|
219
219
|
"consumer lanes are set via resolving commands, not overrides",
|
|
220
|
-
hint="use --supervisor-runtime at session start/fork or
|
|
220
|
+
hint="use --supervisor-runtime at session start/fork or "
|
|
221
|
+
"'forge policy supervisor set <target> --runtime'",
|
|
221
222
|
)
|
|
222
223
|
|
|
223
224
|
if "*" in key:
|