multi-forge 0.9.4__tar.gz → 1.0.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.9.4 → multi_forge-1.0.1}/.gitignore +3 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/PKG-INFO +30 -12
- {multi_forge-0.9.4 → multi_forge-1.0.1}/README.md +7 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/pyproject.toml +49 -35
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/__init__.py +3 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/adapters/litellm.py +19 -8
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/creation.py +2 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/sources.py +11 -13
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/activity.py +18 -22
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/auth.py +0 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/backend.py +6 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/claude.py +33 -21
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/config_cmd.py +305 -34
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/consumer_lane_freeze.py +7 -7
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/editor.py +22 -11
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/extensions.py +151 -64
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/gc.py +26 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/_helpers.py +0 -10
- multi_forge-1.0.1/src/forge/cli/hooks/authority.py +127 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_patch.py +11 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_policy.py +19 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/commands.py +115 -40
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/direct_commands.py +208 -104
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/policy.py +21 -12
- multi_forge-1.0.1/src/forge/cli/hooks/verification.py +616 -0
- multi_forge-0.9.4/src/forge/install/cli.py → multi_forge-1.0.1/src/forge/cli/info.py +23 -33
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/logs.py +16 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/main.py +52 -19
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory.py +17 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory_writer.py +2 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/model.py +0 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/output.py +6 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/policy.py +236 -138
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy.py +215 -142
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy_audit.py +9 -26
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy_costs.py +40 -54
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/runtime.py +2 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/search.py +44 -46
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session.py +32 -49
- multi_forge-1.0.1/src/forge/cli/session_authority.py +99 -0
- multi_forge-1.0.1/src/forge/cli/session_authority_options.py +44 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_codex.py +29 -1
- multi_forge-1.0.1/src/forge/cli/session_fork.py +742 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_lane.py +4 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_lifecycle.py +567 -151
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_manage.py +232 -72
- multi_forge-1.0.1/src/forge/cli/session_model.py +135 -0
- multi_forge-1.0.1/src/forge/cli/session_repair.py +216 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_resume_modes.py +48 -23
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_rewind.py +34 -0
- multi_forge-1.0.1/src/forge/cli/session_route_recovery.py +262 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_supervisor_options.py +3 -24
- multi_forge-1.0.1/src/forge/cli/status_line.py +130 -0
- multi_forge-1.0.1/src/forge/cli/statusline/__init__.py +9 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/context.py +12 -12
- multi_forge-0.9.4/src/forge/cli/status_line.py → multi_forge-1.0.1/src/forge/cli/statusline/formatting.py +60 -713
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/names.py +1 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/palette.py +24 -25
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/registry.py +164 -72
- multi_forge-1.0.1/src/forge/cli/statusline/rendering.py +40 -0
- multi_forge-1.0.1/src/forge/cli/statusline/sources.py +398 -0
- multi_forge-1.0.1/src/forge/cli/statusline/types.py +20 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/trace.py +7 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/workflow.py +95 -113
- multi_forge-1.0.1/src/forge/cli/workspace.py +114 -0
- multi_forge-1.0.1/src/forge/config/defaults/backends/litellm.yaml +459 -0
- multi_forge-1.0.1/src/forge/config/defaults/templates/anthropic-passthrough.yaml +31 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +17 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +8 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +8 -5
- multi_forge-1.0.1/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +38 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +10 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +10 -1
- multi_forge-1.0.1/src/forge/config/defaults/templates/litellm-gemini.yaml +41 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +13 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +15 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai.yaml +15 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +11 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +10 -0
- multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +44 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +3 -2
- multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-glm.yaml +34 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +6 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +1 -0
- multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +40 -0
- multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-openai.yaml +47 -0
- multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-qwen.yaml +48 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/loader.py +57 -30
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/schema.py +278 -48
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/model_catalog.yaml +628 -119
- multi_forge-1.0.1/src/forge/core/data/model_practices.yaml +2 -0
- multi_forge-1.0.1/src/forge/core/data/model_routes.yaml +1441 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/effort.py +6 -4
- multi_forge-1.0.1/src/forge/core/identifiers.py +15 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/_lifecycle.py +148 -32
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/codex_stream.py +3 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/types.py +6 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/lanes.py +14 -16
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/__init__.py +3 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/base.py +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/litellm.py +113 -39
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/openai_compat.py +27 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/openrouter.py +29 -9
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/detection.py +0 -2
- multi_forge-1.0.1/src/forge/core/llm/openrouter_policy.py +23 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/protocols.py +5 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/types.py +9 -1
- multi_forge-1.0.1/src/forge/core/metric_formatting.py +95 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/__init__.py +26 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/catalog.py +3 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/direct_model.py +16 -14
- multi_forge-1.0.1/src/forge/core/models/model_practices.py +264 -0
- multi_forge-1.0.1/src/forge/core/models/model_reference.py +34 -0
- multi_forge-1.0.1/src/forge/core/models/model_routes.py +430 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/types.py +8 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/__init__.py +9 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/claude_session.py +428 -156
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_bridge.py +53 -23
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_enrollment.py +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_interactive.py +127 -100
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_session.py +195 -48
- multi_forge-1.0.1/src/forge/core/ops/codex_thread_index.py +15 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/context.py +9 -42
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/gc.py +60 -20
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/policy.py +66 -7
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/provider_trace.py +2 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/proxy.py +0 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/resolution.py +15 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session.py +33 -2
- multi_forge-1.0.1/src/forge/core/ops/session_active.py +23 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session_adopt.py +23 -15
- multi_forge-1.0.1/src/forge/core/ops/session_authority.py +407 -0
- multi_forge-1.0.1/src/forge/core/ops/session_authority_launch.py +587 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session_context.py +61 -33
- multi_forge-1.0.1/src/forge/core/ops/session_fork_execution.py +887 -0
- multi_forge-1.0.1/src/forge/core/ops/session_fork_preflight.py +1028 -0
- multi_forge-1.0.1/src/forge/core/ops/session_model.py +438 -0
- multi_forge-1.0.1/src/forge/core/ops/session_model_routing.py +883 -0
- multi_forge-1.0.1/src/forge/core/ops/session_repair.py +457 -0
- multi_forge-1.0.1/src/forge/core/ops/session_routing.py +327 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/transfer.py +13 -10
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/usage_summary.py +37 -29
- multi_forge-1.0.1/src/forge/core/ops/workspace.py +86 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/paths.py +1 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/process.py +2 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/__init__.py +2 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/env.py +53 -23
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/headless_json.py +5 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/routing.py +7 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/tagger.py +2 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/throttle.py +17 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/run_id.py +15 -13
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_preflight.py +18 -8
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_preflight_cache.py +8 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/__init__.py +20 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/io.py +4 -3
- multi_forge-1.0.1/src/forge/core/state/retention.py +108 -0
- multi_forge-1.0.1/src/forge/core/state/timestamps.py +212 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/caps.py +0 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/downstream.py +55 -78
- multi_forge-1.0.1/src/forge/core/telemetry/downstream_retention.py +592 -0
- multi_forge-1.0.1/src/forge/core/telemetry/jsonl_io.py +90 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/upstream.py +30 -43
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/transcript.py +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/typing_helpers.py +6 -7
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/correlation.py +7 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/emit.py +10 -17
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/ledger.py +36 -52
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/measurement.py +18 -14
- multi_forge-1.0.1/src/forge/core/wire_shapes.py +30 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/queue.py +113 -11
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/types.py +1 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/codex_hooks.py +182 -8
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hook_dispatcher.py +12 -7
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hook_migration.py +8 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hooks.py +17 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/installer.py +392 -357
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/models.py +4 -3
- multi_forge-1.0.1/src/forge/install/path_policy.py +236 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/preset.py +30 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/runtime_removal.py +36 -31
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/settings_merge.py +47 -61
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_compiler.py +73 -19
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_planning.py +8 -52
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_validation.py +3 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/unmanaged.py +1 -9
- multi_forge-1.0.1/src/forge/policy/action_identity.py +76 -0
- multi_forge-1.0.1/src/forge/policy/deterministic/registry.py +100 -0
- multi_forge-1.0.1/src/forge/policy/diff.py +303 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/engine.py +12 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/plan_check.py +18 -16
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/shadow.py +8 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/shadow_runner.py +4 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/supervisor.py +139 -53
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/verdict.py +65 -22
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/supervisor_lane_degrade.py +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/config.py +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/handlers.py +34 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/types.py +6 -2
- multi_forge-1.0.1/src/forge/proxy/accounting_persistence.py +68 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/audit_logger.py +0 -11
- multi_forge-1.0.1/src/forge/proxy/base_client.py +45 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/client_adapter.py +40 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/client_factory.py +46 -129
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/converters.py +116 -58
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/cost_logger.py +32 -49
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/cost_tracker.py +63 -15
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/intercept.py +196 -23
- multi_forge-1.0.1/src/forge/proxy/model_routes.py +75 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/passthrough.py +47 -32
- multi_forge-1.0.1/src/forge/proxy/passthrough_ingress.py +366 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/provider_trace_logger.py +8 -13
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxies.py +6 -9
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_orchestrator.py +116 -56
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/reasoning.py +18 -0
- multi_forge-1.0.1/src/forge/proxy/request_id.py +21 -0
- multi_forge-1.0.1/src/forge/proxy/response_headers.py +78 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/responses_ingress.py +12 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/responses_passthrough.py +75 -68
- multi_forge-1.0.1/src/forge/proxy/runtime_truth.py +112 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/server.py +549 -480
- multi_forge-1.0.1/src/forge/proxy/sse_framing.py +52 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/utils.py +202 -23
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/adversarial.py +19 -35
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/consensus.py +15 -34
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/engine.py +7 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/models.py +30 -42
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/routing.py +48 -90
- multi_forge-1.0.1/src/forge/review/worker_preparation.py +132 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/runtime_config.py +254 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/bm25_store.py +69 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/content_store.py +21 -7
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/engine.py +2 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/index_state.py +62 -19
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/store.py +14 -10
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/__init__.py +11 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/active.py +66 -13
- multi_forge-1.0.1/src/forge/session/artifacts.py +424 -0
- multi_forge-1.0.1/src/forge/session/authority.py +549 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/__init__.py +4 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/invoke.py +51 -5
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/paths.py +42 -20
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/cleanup.py +2 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/codex_invoke.py +12 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/consumer_lanes.py +6 -6
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/context_limit.py +39 -19
- multi_forge-1.0.1/src/forge/session/context_rendering.py +48 -0
- multi_forge-1.0.1/src/forge/session/events.py +562 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/exceptions.py +41 -8
- multi_forge-1.0.1/src/forge/session/git.py +67 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/session_start.py +12 -37
- multi_forge-1.0.1/src/forge/session/index.py +849 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/launch.py +21 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/launch_confirmation.py +23 -10
- multi_forge-1.0.1/src/forge/session/launchability.py +29 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/manager.py +655 -237
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/memory_writer.py +21 -30
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/model_pin.py +76 -16
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/models.py +107 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/overrides.py +61 -11
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/passport.py +16 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/rewind.py +32 -47
- multi_forge-1.0.1/src/forge/session/routing.py +633 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/shadow_curation.py +42 -54
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/store.py +161 -27
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/transfer.py +110 -99
- multi_forge-1.0.1/src/forge/session/verification_config.py +34 -0
- multi_forge-1.0.1/src/forge/session/workspace.py +205 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/__init__.py +5 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/cleanup.py +43 -11
- multi_forge-1.0.1/src/forge/session/worktree/config_copy.py +332 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/create.py +78 -129
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/container.py +17 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/analyze/forge-skill.yaml +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/challenge/forge-skill.yaml +2 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/resources/synthesis.md +4 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/SKILL.md +361 -118
- multi_forge-1.0.1/src/skills/qa/resources/checklist/0-enable.md +119 -0
- multi_forge-1.0.1/src/skills/qa/resources/checklist/10-resume.md +312 -0
- multi_forge-1.0.1/src/skills/qa/resources/checklist/11-config.md +267 -0
- multi_forge-1.0.1/src/skills/qa/resources/checklist/12-search.md +146 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/13-policy.md +56 -2
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/14-workflow.md +30 -8
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/15-skills.md +41 -17
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/16-memory.md +9 -3
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/17-info.md +62 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/18-disable.md +55 -19
- multi_forge-1.0.1/src/skills/qa/resources/checklist/19-uninstall.md +141 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/2-extension.md +18 -11
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/3-authentication.md +21 -18
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/4-proxy.md +383 -45
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/5-session.md +456 -44
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/6-hook.md +74 -35
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/7-costs.md +207 -72
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/8-status-line.md +63 -15
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/9-direct-commands.md +45 -1
- multi_forge-1.0.1/src/skills/qa/resources/checklist.md +105 -0
- multi_forge-1.0.1/src/skills/qa/resources/coverage-map.md +58 -0
- multi_forge-1.0.1/src/skills/qa/resources/execution-budget.json +29 -0
- multi_forge-1.0.1/src/skills/qa/resources/report-template.md +96 -0
- multi_forge-1.0.1/src/skills/qa/resources/runtime-matrix.json +58 -0
- multi_forge-1.0.1/src/skills/qa/scripts/qa-artifact.py +247 -0
- multi_forge-1.0.1/src/skills/qa/scripts/qa-run-metrics.py +335 -0
- multi_forge-1.0.1/src/skills/qa/scripts/qa-selection.py +232 -0
- multi_forge-1.0.1/src/skills/qa/scripts/start-container.sh +1206 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/scripts/walkthrough-state.py +349 -85
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/forge-skill.yaml +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/skills-writing-guide.md +6 -4
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/forge-skill.yaml +1 -1
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/scripts/smoke-test.sh +7 -8
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/forge-skill.yaml +1 -1
- multi_forge-1.0.1/src/skills/walkthrough/SKILL.md +408 -0
- multi_forge-1.0.1/src/skills/walkthrough/resources/checklist.md +818 -0
- multi_forge-1.0.1/src/skills/walkthrough/resources/journey-map.md +55 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/claude-wrapper.sh +37 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/cleanup-owned.sh +592 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/package-identity.py +353 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/protected-paths.py +207 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/run-in-repo.sh +192 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/setup-test-repo.sh +470 -0
- multi_forge-1.0.1/src/skills/walkthrough/scripts/walkthrough-report.py +343 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/walkthrough/scripts/walkthrough-state.py +349 -85
- multi_forge-0.9.4/src/forge/cli/hooks/verification.py +0 -370
- multi_forge-0.9.4/src/forge/cli/session_fork.py +0 -1142
- multi_forge-0.9.4/src/forge/cli/session_model_pin.py +0 -108
- multi_forge-0.9.4/src/forge/cli/statusline/__init__.py +0 -9
- multi_forge-0.9.4/src/forge/config/defaults/backends/litellm.yaml +0 -242
- multi_forge-0.9.4/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -31
- multi_forge-0.9.4/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -31
- multi_forge-0.9.4/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -22
- multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -26
- multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -23
- multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -23
- multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -28
- multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -28
- multi_forge-0.9.4/src/forge/core/state/retention.py +0 -79
- multi_forge-0.9.4/src/forge/core/state/timestamps.py +0 -65
- multi_forge-0.9.4/src/forge/core/telemetry/jsonl_io.py +0 -37
- multi_forge-0.9.4/src/forge/policy/deterministic/registry.py +0 -147
- multi_forge-0.9.4/src/forge/policy/workflow/__init__.py +0 -17
- multi_forge-0.9.4/src/forge/policy/workflow/branches.py +0 -67
- multi_forge-0.9.4/src/forge/policy/workflow/config.py +0 -63
- multi_forge-0.9.4/src/forge/policy/workflow/divergence.py +0 -113
- multi_forge-0.9.4/src/forge/policy/workflow/policy.py +0 -87
- multi_forge-0.9.4/src/forge/policy/workflow/stages.py +0 -288
- multi_forge-0.9.4/src/forge/proxy/base_client.py +0 -181
- multi_forge-0.9.4/src/forge/proxy/model_spec.py +0 -158
- multi_forge-0.9.4/src/forge/session/artifacts.py +0 -216
- multi_forge-0.9.4/src/forge/session/index.py +0 -602
- multi_forge-0.9.4/src/forge/session/worktree/config_copy.py +0 -203
- multi_forge-0.9.4/src/skills/qa/resources/checklist/0-enable.md +0 -84
- multi_forge-0.9.4/src/skills/qa/resources/checklist/10-resume.md +0 -204
- multi_forge-0.9.4/src/skills/qa/resources/checklist/11-config.md +0 -150
- multi_forge-0.9.4/src/skills/qa/resources/checklist/12-search.md +0 -64
- multi_forge-0.9.4/src/skills/qa/resources/checklist/19-uninstall.md +0 -151
- multi_forge-0.9.4/src/skills/qa/resources/checklist.md +0 -132
- multi_forge-0.9.4/src/skills/qa/resources/report-template.md +0 -62
- multi_forge-0.9.4/src/skills/qa/scripts/start-container.sh +0 -548
- multi_forge-0.9.4/src/skills/walkthrough/SKILL.md +0 -602
- multi_forge-0.9.4/src/skills/walkthrough/resources/checklist.md +0 -1036
- multi_forge-0.9.4/src/skills/walkthrough/scripts/run-in-repo.sh +0 -138
- multi_forge-0.9.4/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -205
- {multi_forge-0.9.4 → multi_forge-1.0.1}/LICENSE +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/NOTICE +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/agents/.gitkeep +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/commands/.gitkeep +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/adapters/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/registry.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/base.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/openrouter.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/codex.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/guards.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/_group.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/protocols.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory_report.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_adopt.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_memory.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_routing.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/throttle.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/telemetry.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/transfer.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/dataclass_utils.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/backends/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/capabilities.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/credentials_file.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/protocols.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/secrets.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/template_secrets.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/backend_dependency.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/credential_registry.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/claude.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/codex.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/credentials.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/errors.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/logging.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/naming.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/backend_reconcile.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_adopt.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/provider_types.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/cost_tracking.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/llm_call.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/proxy.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/session_runner.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/structured_output.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/registry.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime_vocab.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/exceptions.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/lock.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/versioned_store.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/vocabulary.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/tiers.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/billing.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/vocabulary.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/doctor.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/exceptions.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/module_planning.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/ownership.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/project_compat.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/project_registry.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/settings_rollback.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_cache.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/tracking.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/version.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/base.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/tdd.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/protocols.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/queries.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/store.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/prompts.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/data_models.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/error_hints.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/metrics.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/ports.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_identity.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_startup.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/stream_relay.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-performance.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-quick.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-security.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/debate_evaluation.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/docreview-quick.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/docreview.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/thinkdeep.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/synthesis.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/exceptions.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/extractor.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/tokenizer.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/addendum.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/cleanup.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/relocate.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/codex_handoff.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/config.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/effective.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/models.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/identity.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/memory_inheritance.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/plan_resolution.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/prev_sessions.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/project_memory.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/validation.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/__init__.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/docker.py +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/analyze/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/challenge/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/forge-skill.yaml +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/resources/synthesis.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/debate/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/debate/forge-skill.yaml +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/forge-skill.yaml +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/claude-4.6.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/claude-4.8.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/gemini-3.1.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/gpt-5.5.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-anthropic.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-gemini.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-openai.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/forge-skill.yaml +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/content.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-anthropic.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-gemini.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-openai.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-gemini.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-openai.md +0 -0
- {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs.md +0 -0
|
@@ -62,6 +62,7 @@ __pypackages__/
|
|
|
62
62
|
.claude/*
|
|
63
63
|
!.claude/commands/
|
|
64
64
|
.claude/commands/*
|
|
65
|
+
!.claude/commands/comment-cleanup.md
|
|
65
66
|
!.claude/commands/gather-context.md
|
|
66
67
|
!.claude/commands/refactor_audit.md
|
|
67
68
|
!.claude/commands/simplicity_audit.md
|
|
@@ -70,6 +71,8 @@ __pypackages__/
|
|
|
70
71
|
.agents/*
|
|
71
72
|
!.agents/skills/
|
|
72
73
|
.agents/skills/*
|
|
74
|
+
!.agents/skills/comment-cleanup/
|
|
75
|
+
!.agents/skills/comment-cleanup/SKILL.md
|
|
73
76
|
!.agents/skills/gather-context/
|
|
74
77
|
!.agents/skills/gather-context/SKILL.md
|
|
75
78
|
!.agents/skills/refactor-audit/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: multi-forge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.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
|
|
@@ -21,22 +21,38 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Software Development
|
|
23
23
|
Requires-Python: <3.14,>=3.11
|
|
24
|
+
Requires-Dist: aiohttp<4,>=3.14.3
|
|
25
|
+
Requires-Dist: apscheduler<4,>=3.11.2
|
|
26
|
+
Requires-Dist: backoff<3,>=2.2.1
|
|
27
|
+
Requires-Dist: boto3<2,>=1.43.1
|
|
24
28
|
Requires-Dist: click>=8.3.1
|
|
25
29
|
Requires-Dist: coolname<6,>=5.0.0
|
|
30
|
+
Requires-Dist: cryptography>=50.0.0
|
|
26
31
|
Requires-Dist: dacite>=1.8.0
|
|
27
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: expression<6,>=5.6.0
|
|
33
|
+
Requires-Dist: fastapi-sso<1,>=0.19.0
|
|
34
|
+
Requires-Dist: fastapi<0.137,>=0.136.3
|
|
28
35
|
Requires-Dist: httpx>=0.25.0
|
|
29
|
-
Requires-Dist: litellm
|
|
36
|
+
Requires-Dist: litellm==1.102.0
|
|
30
37
|
Requires-Dist: openai>=1.0.0
|
|
38
|
+
Requires-Dist: orjson<4,>=3.11.6
|
|
31
39
|
Requires-Dist: packaging>=21.0
|
|
40
|
+
Requires-Dist: pydantic-settings<3,>=2.14.1
|
|
32
41
|
Requires-Dist: pydantic>=2.0.0
|
|
42
|
+
Requires-Dist: pyjwt<3,>=2.13.0
|
|
43
|
+
Requires-Dist: python-dateutil<3,>=2.9.0
|
|
33
44
|
Requires-Dist: python-dotenv>=1.2.2
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
45
|
+
Requires-Dist: python-multipart<1,>=0.0.31
|
|
46
|
+
Requires-Dist: pyyaml<7,>=6.0.3
|
|
47
|
+
Requires-Dist: redis<8,>=5.2.0
|
|
48
|
+
Requires-Dist: rich<14,>=13.9.4
|
|
36
49
|
Requires-Dist: ruamel-yaml>=0.18.0
|
|
50
|
+
Requires-Dist: starlette<1.4,>=1.3.1
|
|
37
51
|
Requires-Dist: tenacity>=8.0.0
|
|
38
|
-
Requires-Dist: uvicorn
|
|
52
|
+
Requires-Dist: uvicorn<1,>=0.33.0
|
|
53
|
+
Requires-Dist: uvloop<1,>=0.21.0; sys_platform != 'win32'
|
|
39
54
|
Requires-Dist: watchfiles>=0.20
|
|
55
|
+
Requires-Dist: websockets<16,>=15.0.1
|
|
40
56
|
Description-Content-Type: text/markdown
|
|
41
57
|
|
|
42
58
|
# Multi-Forge
|
|
@@ -180,8 +196,10 @@ forge session start --proxy openrouter-anthropic
|
|
|
180
196
|
|
|
181
197
|
Developing on Forge itself? See [CONTRIBUTING.md](CONTRIBUTING.md) for the editable install.
|
|
182
198
|
|
|
183
|
-
Once running, try `/
|
|
184
|
-
|
|
199
|
+
Once running, try `/smoke-test` (Claude) or `$smoke-test` (Codex) for a read-only health check. `/walkthrough` teaches
|
|
200
|
+
the direct managed-session, model-route, policy, telemetry, and structured-resume loop in a sandboxed repository; add
|
|
201
|
+
`--codex` or `--sidecar` only for those optional chapters. See the
|
|
202
|
+
[manual-testing guide](docs/end-user/manual_testing.md).
|
|
185
203
|
|
|
186
204
|
## Plan, Execute, Review
|
|
187
205
|
|
|
@@ -263,9 +281,9 @@ hashes, lengths, and counts, never prompt or completion text.
|
|
|
263
281
|
|
|
264
282
|
## Skills
|
|
265
283
|
|
|
266
|
-
Nine skills compile for **both** runtimes -- Claude invokes
|
|
267
|
-
`
|
|
268
|
-
|
|
284
|
+
Nine skills compile for **both** runtimes -- Claude invokes `/<name>`, Codex invokes `$<name>`: `analyze`, `challenge`,
|
|
285
|
+
`consensus`, `debate`, `panel`, `review`, `review-docs`, `smoke-test`, `understand`. Only `/walkthrough` and `/qa` are
|
|
286
|
+
Claude-hosted; walkthrough can still exercise Codex as an optional managed runtime.
|
|
269
287
|
|
|
270
288
|
The same runners are available from the terminal, and `--check` turns one into an exit-code gate you can script:
|
|
271
289
|
|
|
@@ -139,8 +139,10 @@ forge session start --proxy openrouter-anthropic
|
|
|
139
139
|
|
|
140
140
|
Developing on Forge itself? See [CONTRIBUTING.md](CONTRIBUTING.md) for the editable install.
|
|
141
141
|
|
|
142
|
-
Once running, try `/
|
|
143
|
-
|
|
142
|
+
Once running, try `/smoke-test` (Claude) or `$smoke-test` (Codex) for a read-only health check. `/walkthrough` teaches
|
|
143
|
+
the direct managed-session, model-route, policy, telemetry, and structured-resume loop in a sandboxed repository; add
|
|
144
|
+
`--codex` or `--sidecar` only for those optional chapters. See the
|
|
145
|
+
[manual-testing guide](docs/end-user/manual_testing.md).
|
|
144
146
|
|
|
145
147
|
## Plan, Execute, Review
|
|
146
148
|
|
|
@@ -222,9 +224,9 @@ hashes, lengths, and counts, never prompt or completion text.
|
|
|
222
224
|
|
|
223
225
|
## Skills
|
|
224
226
|
|
|
225
|
-
Nine skills compile for **both** runtimes -- Claude invokes
|
|
226
|
-
`
|
|
227
|
-
|
|
227
|
+
Nine skills compile for **both** runtimes -- Claude invokes `/<name>`, Codex invokes `$<name>`: `analyze`, `challenge`,
|
|
228
|
+
`consensus`, `debate`, `panel`, `review`, `review-docs`, `smoke-test`, `understand`. Only `/walkthrough` and `/qa` are
|
|
229
|
+
Claude-hosted; walkthrough can still exercise Codex as an optional managed runtime.
|
|
228
230
|
|
|
229
231
|
The same runners are available from the terminal, and `--check` turns one into an exit-code gate you can script:
|
|
230
232
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "multi-forge"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.1"
|
|
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"
|
|
7
7
|
authors = [
|
|
8
8
|
{ name = "Habib Farr", email = "hapali@pm.me" },
|
|
9
9
|
]
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# LiteLLM's CLI selects uvloop on non-Windows systems. Keep Python 3.14 outside
|
|
11
|
+
# the supported range until that clean-install/runtime matrix is verified.
|
|
12
|
+
# See https://github.com/hapa1i/multi-forge/issues/1
|
|
13
13
|
requires-python = ">=3.11,<3.14"
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
@@ -28,26 +28,48 @@ dependencies = [
|
|
|
28
28
|
"dacite>=1.8.0",
|
|
29
29
|
"click>=8.3.1",
|
|
30
30
|
"coolname>=5.0.0,<6",
|
|
31
|
-
"rich>=13.9.
|
|
31
|
+
"rich>=13.9.4,<14",
|
|
32
32
|
"pydantic>=2.0.0",
|
|
33
33
|
"openai>=1.0.0",
|
|
34
34
|
"httpx>=0.25.0",
|
|
35
35
|
"tenacity>=8.0.0",
|
|
36
|
+
# Preserve process-local TZ semantics for IANA keys, TZif paths, and POSIX rules.
|
|
37
|
+
"python-dateutil>=2.9.0,<3",
|
|
36
38
|
"python-dotenv>=1.2.2",
|
|
37
|
-
"pyyaml>=6.0.
|
|
39
|
+
"pyyaml>=6.0.3,<7",
|
|
38
40
|
"ruamel.yaml>=0.18.0",
|
|
39
41
|
"packaging>=21.0",
|
|
40
|
-
# Proxy dependencies (previously optional, now core).
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
# Proxy dependencies (previously optional, now core). Forge owns the
|
|
43
|
+
# default-Uvicorn LiteLLM startup set because the upstream `proxy` extra
|
|
44
|
+
# caps cryptography below Forge's patched floor. This set is start/health
|
|
45
|
+
# validated through LiteLLM 1.102.0; run scripts/test-wheel-runtime.sh before
|
|
46
|
+
# raising the ceiling. Alternate servers, databases, and cloud integrations
|
|
47
|
+
# remain outside Forge's runtime path.
|
|
48
|
+
"aiohttp>=3.14.3,<4",
|
|
49
|
+
"apscheduler>=3.11.2,<4",
|
|
50
|
+
"backoff>=2.2.1,<3",
|
|
51
|
+
# LiteLLM 1.88+ imports the Focus S3 destination during proxy startup.
|
|
52
|
+
"boto3>=1.43.1,<2",
|
|
53
|
+
"cryptography>=50.0.0",
|
|
54
|
+
# LiteLLM 1.95 imports outbound-credential tagged unions at startup.
|
|
55
|
+
"expression>=5.6.0,<6",
|
|
56
|
+
# Keep the lock-proven FastAPI/Starlette pair while their newer route-tree
|
|
57
|
+
# behavior is verified separately from this LiteLLM/model-support update.
|
|
58
|
+
"fastapi>=0.136.3,<0.137",
|
|
59
|
+
"fastapi-sso>=0.19.0,<1",
|
|
60
|
+
"orjson>=3.11.6,<4",
|
|
61
|
+
"pydantic-settings>=2.14.1,<3",
|
|
62
|
+
"pyjwt>=2.13.0,<3",
|
|
63
|
+
"python-multipart>=0.0.31,<1",
|
|
64
|
+
# LiteLLM 1.95 imports its Redis support from the default startup graph.
|
|
65
|
+
"redis>=5.2.0,<8",
|
|
66
|
+
"starlette>=1.3.1,<1.4",
|
|
67
|
+
"uvicorn>=0.33.0,<1",
|
|
68
|
+
"uvloop>=0.21.0,<1; sys_platform != 'win32'",
|
|
46
69
|
"watchfiles>=0.20",
|
|
47
|
-
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
"litellm[proxy]>=1.88.0",
|
|
70
|
+
"websockets>=15.0.1,<16",
|
|
71
|
+
# Packaged Gemini 3.8 Flash metadata and the verified proxy runtime.
|
|
72
|
+
"litellm==1.102.0",
|
|
51
73
|
]
|
|
52
74
|
|
|
53
75
|
[project.scripts]
|
|
@@ -66,7 +88,6 @@ build-backend = "hatchling.build"
|
|
|
66
88
|
[tool.hatch.build.targets.wheel]
|
|
67
89
|
packages = ["src/forge"]
|
|
68
90
|
|
|
69
|
-
# Include non-Python data files (YAML catalogs, etc.) in the wheel
|
|
70
91
|
[tool.hatch.build.targets.wheel.sources]
|
|
71
92
|
"src" = ""
|
|
72
93
|
|
|
@@ -106,10 +127,15 @@ markers = [
|
|
|
106
127
|
[tool.pytest-asyncio]
|
|
107
128
|
asyncio_mode = "auto"
|
|
108
129
|
|
|
130
|
+
[tool.ruff.lint]
|
|
131
|
+
# Ruff 0.16 expanded its implicit defaults. Keep the repository's established
|
|
132
|
+
# lint contract explicit so upgrading the executable does not change policy.
|
|
133
|
+
select = ["E4", "E7", "E9", "F"]
|
|
134
|
+
|
|
109
135
|
[tool.mypy]
|
|
110
136
|
# Same-named standalone scripts in separate skill directories collide in mypy's
|
|
111
|
-
# module map during broad scans.
|
|
112
|
-
#
|
|
137
|
+
# module map during broad scans. The walkthrough copy is checked directly, while
|
|
138
|
+
# source parity and the dual-copy behavioral matrix cover the excluded QA copy.
|
|
113
139
|
exclude = [
|
|
114
140
|
'src/skills/qa/scripts/walkthrough-state\.py$',
|
|
115
141
|
]
|
|
@@ -125,23 +151,10 @@ disallow_untyped_calls = false
|
|
|
125
151
|
constraint-dependencies = [
|
|
126
152
|
"filelock>=3.20.3",
|
|
127
153
|
"virtualenv>=20.36.1",
|
|
128
|
-
"aiohttp>=3.14.1",
|
|
129
|
-
"cryptography>=48.0.1",
|
|
130
154
|
"mcp>=1.28.1",
|
|
131
|
-
"python-multipart>=0.0.31",
|
|
132
155
|
"Pygments>=2.20.0",
|
|
133
|
-
"PyJWT>=2.13.0",
|
|
134
|
-
"starlette>=1.3.1",
|
|
135
156
|
"pyasn1>=0.6.4",
|
|
136
157
|
]
|
|
137
|
-
# litellm proxy extras can pin vulnerable transitive dependencies before
|
|
138
|
-
# upstream metadata catches up. Forge doesn't use these directly; override to
|
|
139
|
-
# force patched versions required by Dependabot alerts.
|
|
140
|
-
override-dependencies = [
|
|
141
|
-
"cryptography>=48.0.1",
|
|
142
|
-
"fastapi-sso>=0.19.0",
|
|
143
|
-
"python-multipart>=0.0.31",
|
|
144
|
-
]
|
|
145
158
|
|
|
146
159
|
[dependency-groups]
|
|
147
160
|
dev = [
|
|
@@ -157,12 +170,13 @@ dev = [
|
|
|
157
170
|
"pre-commit>=4.5.0",
|
|
158
171
|
"types-PyYAML>=6.0.0",
|
|
159
172
|
"types-requests>=2.31.0",
|
|
160
|
-
"python-dotenv>=1.2.1",
|
|
161
173
|
"httpx2>=2.3.0",
|
|
162
174
|
]
|
|
163
175
|
provider-check = [
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
# scripts/count-tokens.py imports provider-specific count_tokens surfaces;
|
|
177
|
+
# keep lock refreshes inside the API families verified by that script.
|
|
178
|
+
"anthropic>=0.100.0,<2",
|
|
179
|
+
"google-genai>=2.5.0,<3",
|
|
166
180
|
"openai>=2.0.0",
|
|
167
181
|
"tiktoken>=0.12.0",
|
|
168
182
|
]
|
|
@@ -70,7 +70,7 @@ class BackendAdapter(ABC):
|
|
|
70
70
|
|
|
71
71
|
@abstractmethod
|
|
72
72
|
def stop(self, process: ManagedBackendProcess) -> None:
|
|
73
|
-
"""Stop backend
|
|
73
|
+
"""Stop backend, raising when owned teardown cannot be completed.
|
|
74
74
|
|
|
75
75
|
Args:
|
|
76
76
|
process: Managed backend process to stop
|
|
@@ -163,13 +163,14 @@ class BackendManager:
|
|
|
163
163
|
return BackendEnsureResult(process=process, source="start")
|
|
164
164
|
|
|
165
165
|
def stop_backend(self, process_id: str) -> None:
|
|
166
|
-
"""Stop backend and remove from registry.
|
|
166
|
+
"""Stop backend and remove it from the registry after successful teardown.
|
|
167
167
|
|
|
168
168
|
Args:
|
|
169
169
|
process_id: Managed process ID
|
|
170
170
|
|
|
171
171
|
Raises:
|
|
172
172
|
ValueError: If managed process not found
|
|
173
|
+
OSError: If the adapter cannot complete the owned process teardown
|
|
173
174
|
"""
|
|
174
175
|
registry = self.registry_store.read()
|
|
175
176
|
process = registry.processes.get(process_id)
|
|
@@ -11,6 +11,7 @@ so this adapter just passes through whatever is in os.environ.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
|
+
import signal
|
|
14
15
|
import subprocess
|
|
15
16
|
import sys
|
|
16
17
|
import time
|
|
@@ -106,12 +107,16 @@ class LiteLLMAdapter(BackendAdapter):
|
|
|
106
107
|
start_new_session=True, # Detach from parent
|
|
107
108
|
)
|
|
108
109
|
|
|
109
|
-
# Wait for health (timeout 10s)
|
|
110
110
|
if not self._wait_for_health(port, timeout=10):
|
|
111
111
|
try:
|
|
112
|
-
proc.
|
|
113
|
-
except
|
|
114
|
-
pass #
|
|
112
|
+
os.killpg(proc.pid, signal.SIGKILL)
|
|
113
|
+
except ProcessLookupError:
|
|
114
|
+
pass # The complete process group has already exited.
|
|
115
|
+
except OSError as exc:
|
|
116
|
+
raise BackendStartError(
|
|
117
|
+
f"LiteLLM failed to start on port {port}; process-group cleanup failed: {exc}\n"
|
|
118
|
+
f"Check logs: {log_file}"
|
|
119
|
+
) from exc
|
|
115
120
|
raise BackendStartError(f"LiteLLM failed to start on port {port}\nCheck logs: {log_file}")
|
|
116
121
|
|
|
117
122
|
return ManagedBackendProcess(
|
|
@@ -124,18 +129,24 @@ class LiteLLMAdapter(BackendAdapter):
|
|
|
124
129
|
)
|
|
125
130
|
|
|
126
131
|
def stop(self, process: ManagedBackendProcess) -> None:
|
|
127
|
-
"""
|
|
132
|
+
"""Signal the detached LiteLLM process group to stop.
|
|
128
133
|
|
|
129
134
|
Args:
|
|
130
135
|
process: Managed backend process to stop
|
|
136
|
+
|
|
137
|
+
Raises:
|
|
138
|
+
OSError: If the owned process group exists but cannot be signalled.
|
|
131
139
|
"""
|
|
132
140
|
if process.pid is None:
|
|
133
141
|
return
|
|
134
142
|
|
|
135
143
|
try:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
# ``start()`` makes this PID the leader of a new session/process group.
|
|
145
|
+
# Address the recorded group id directly: its leader may already have
|
|
146
|
+
# exited while a worker still owns the port.
|
|
147
|
+
os.killpg(process.pid, signal.SIGTERM)
|
|
148
|
+
except ProcessLookupError:
|
|
149
|
+
pass # The complete process group has already exited.
|
|
139
150
|
|
|
140
151
|
def health_check(self, process: ManagedBackendProcess) -> bool:
|
|
141
152
|
"""Check if LiteLLM backend is healthy.
|
|
@@ -31,10 +31,8 @@ def create_backend_config(
|
|
|
31
31
|
backend_dir = get_forge_home() / "backends" / adapter_type
|
|
32
32
|
backend_dir.mkdir(parents=True, exist_ok=True)
|
|
33
33
|
|
|
34
|
-
# Determine source config
|
|
35
34
|
if source_config is None:
|
|
36
|
-
|
|
37
|
-
defaults_dir = get_defaults_dir() # src/forge/config/defaults/
|
|
35
|
+
defaults_dir = get_defaults_dir()
|
|
38
36
|
source_config = defaults_dir / "backends" / f"{adapter_type}.yaml"
|
|
39
37
|
|
|
40
38
|
if not source_config.exists():
|
|
@@ -82,7 +80,7 @@ def is_backend_config_outdated(adapter_type: str) -> bool:
|
|
|
82
80
|
|
|
83
81
|
default = get_defaults_dir() / "backends" / f"{adapter_type}.yaml"
|
|
84
82
|
if not default.exists():
|
|
85
|
-
return False
|
|
83
|
+
return False
|
|
86
84
|
|
|
87
85
|
installed_digest = hashlib.sha256(installed.read_bytes()).hexdigest()
|
|
88
86
|
default_digest = hashlib.sha256(default.read_bytes()).hexdigest()
|
|
@@ -13,6 +13,7 @@ from urllib.parse import urlparse
|
|
|
13
13
|
|
|
14
14
|
from forge.core.backend_dependency import BackendDependency
|
|
15
15
|
from forge.core.credential_registry import CREDENTIALS, Credential
|
|
16
|
+
from forge.core.identifiers import is_lowercase_identifier
|
|
16
17
|
from forge.core.provider_types import ProviderType
|
|
17
18
|
from forge.core.runtime_vocab import LANE_RUNTIME_IDS
|
|
18
19
|
|
|
@@ -29,7 +30,6 @@ _VALID_ENDPOINT_KINDS = frozenset(get_args(EndpointKind))
|
|
|
29
30
|
_VALID_PROVIDERS = frozenset(get_args(ProviderType))
|
|
30
31
|
_VALID_BILLING_POSTURES = frozenset(get_args(BillingPosture))
|
|
31
32
|
_ENV_VAR_RE = re.compile(r"^[A-Z][A-Z0-9_]*$")
|
|
32
|
-
_SOURCE_ID_RE = re.compile(r"^[a-z0-9][a-z0-9_.-]*$")
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ModelSourceCatalogError(ValueError):
|
|
@@ -208,7 +208,7 @@ def _build_identifier_lookup(sources: Iterable[ModelSource]) -> dict[str, str]:
|
|
|
208
208
|
|
|
209
209
|
|
|
210
210
|
def _validate_source(source: ModelSource) -> None:
|
|
211
|
-
if not
|
|
211
|
+
if not is_lowercase_identifier(source.id):
|
|
212
212
|
raise ModelSourceCatalogError(f"invalid model-source id: {source.id!r}")
|
|
213
213
|
if source.kind not in _VALID_SOURCE_KINDS:
|
|
214
214
|
raise ModelSourceCatalogError(f"invalid model-source kind for {source.id!r}: {source.kind!r}")
|
|
@@ -216,7 +216,7 @@ def _validate_source(source: ModelSource) -> None:
|
|
|
216
216
|
raise ModelSourceCatalogError(f"invalid provider for {source.id!r}: {source.provider!r}")
|
|
217
217
|
if source.billing_posture not in _VALID_BILLING_POSTURES:
|
|
218
218
|
raise ModelSourceCatalogError(f"invalid billing_posture for {source.id!r}: {source.billing_posture!r}")
|
|
219
|
-
if source.backend_kind is not None and not
|
|
219
|
+
if source.backend_kind is not None and not is_lowercase_identifier(source.backend_kind):
|
|
220
220
|
raise ModelSourceCatalogError(f"source {source.id!r} has invalid backend_kind {source.backend_kind!r}")
|
|
221
221
|
|
|
222
222
|
# Credential symmetry by endpoint family: a runtime_native source's auth is
|
|
@@ -240,7 +240,7 @@ def _validate_source(source: ModelSource) -> None:
|
|
|
240
240
|
if runtime_id not in LANE_RUNTIME_IDS:
|
|
241
241
|
raise ModelSourceCatalogError(f"source {source.id!r} has unknown reachable_via runtime {runtime_id!r}")
|
|
242
242
|
for template_name in source.template_names:
|
|
243
|
-
if not
|
|
243
|
+
if not is_lowercase_identifier(template_name):
|
|
244
244
|
raise ModelSourceCatalogError(f"source {source.id!r} has invalid template name {template_name!r}")
|
|
245
245
|
|
|
246
246
|
if source.kind == "local":
|
|
@@ -443,9 +443,8 @@ BUILTIN_MODEL_SOURCES: tuple[ModelSource, ...] = (
|
|
|
443
443
|
),
|
|
444
444
|
# ChatGPT subscription reached through the codex runtime. Endpoint and auth are
|
|
445
445
|
# owned by codex (codex login --device-auth -> chatgpt_tokens), so there is no
|
|
446
|
-
# Forge URL and no Forge credential. Billing is the subscription's quota
|
|
447
|
-
#
|
|
448
|
-
# runtime_native source (epic consumer_lanes, T2).
|
|
446
|
+
# Forge URL and no Forge credential. Billing is the subscription's quota.
|
|
447
|
+
# Only codex can reach this source because that runtime holds the login.
|
|
449
448
|
ModelSource(
|
|
450
449
|
id="chatgpt",
|
|
451
450
|
kind="remote",
|
|
@@ -457,9 +456,8 @@ BUILTIN_MODEL_SOURCES: tuple[ModelSource, ...] = (
|
|
|
457
456
|
),
|
|
458
457
|
# Claude Max/Pro subscription reached through the claude_code runtime. Endpoint
|
|
459
458
|
# and auth are owned by claude -p (keychain OAuth login), so there is no Forge
|
|
460
|
-
# URL and no Forge credential. Billing is the subscription's quota
|
|
461
|
-
#
|
|
462
|
-
# analog of chatgpt (epic consumer_lanes, T0).
|
|
459
|
+
# URL and no Forge credential. Billing is the subscription's quota.
|
|
460
|
+
# Only claude_code can reach this source because that runtime holds the login.
|
|
463
461
|
ModelSource(
|
|
464
462
|
id="claude-max",
|
|
465
463
|
kind="remote",
|
|
@@ -505,9 +503,9 @@ BUILTIN_MODEL_SOURCES: tuple[ModelSource, ...] = (
|
|
|
505
503
|
),
|
|
506
504
|
template_names=("litellm-anthropic-local",),
|
|
507
505
|
),
|
|
508
|
-
#
|
|
509
|
-
#
|
|
510
|
-
# byte-for-byte and
|
|
506
|
+
# Dual-ingress OpenAI route: Codex gets raw Responses passthrough while
|
|
507
|
+
# Claude-backed consumers use the translated Messages handler. A local
|
|
508
|
+
# LiteLLM preserves Responses reasoning byte-for-byte and reports real cost.
|
|
511
509
|
ModelSource(
|
|
512
510
|
id="codex-responses-local",
|
|
513
511
|
kind="local",
|
|
@@ -10,13 +10,14 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
12
|
import sys
|
|
13
|
-
from datetime import datetime
|
|
13
|
+
from datetime import datetime
|
|
14
14
|
|
|
15
15
|
import click
|
|
16
16
|
from rich.console import Console
|
|
17
17
|
from rich.table import Table
|
|
18
18
|
|
|
19
19
|
from forge.cli.output import print_error_with_tip
|
|
20
|
+
from forge.core.metric_formatting import UsdDisplayPolicy, format_usd_micros
|
|
20
21
|
from forge.core.ops.session_context import (
|
|
21
22
|
SessionContextError,
|
|
22
23
|
resolve_session_identifier,
|
|
@@ -27,6 +28,7 @@ from forge.core.ops.usage_summary import (
|
|
|
27
28
|
build_session_activity_summary,
|
|
28
29
|
format_failing_open,
|
|
29
30
|
)
|
|
31
|
+
from forge.core.state import local_period_bounds
|
|
30
32
|
|
|
31
33
|
console = Console()
|
|
32
34
|
_SESSION_LIST_TIP = "Run 'forge session list' to see sessions."
|
|
@@ -80,17 +82,12 @@ def activity_cmd(session: str | None, as_json: bool, period: str) -> None:
|
|
|
80
82
|
_render(summary, period=period)
|
|
81
83
|
|
|
82
84
|
|
|
83
|
-
def _period_start(period: str) -> datetime | None:
|
|
85
|
+
def _period_start(period: str, *, now: datetime | None = None) -> datetime | None:
|
|
84
86
|
"""Return the UTC lower bound for a named local-calendar period."""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
local_midnight = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
90
|
-
return (local_midnight - timedelta(days=local_midnight.weekday())).astimezone(timezone.utc)
|
|
91
|
-
if period == "month":
|
|
92
|
-
return now_local.replace(day=1, hour=0, minute=0, second=0, microsecond=0).astimezone(timezone.utc)
|
|
93
|
-
return None
|
|
87
|
+
if period == "all":
|
|
88
|
+
return None
|
|
89
|
+
start, _ = local_period_bounds(period, now=now)
|
|
90
|
+
return start
|
|
94
91
|
|
|
95
92
|
|
|
96
93
|
def _render(summary: SessionActivitySummary, *, period: str) -> None:
|
|
@@ -193,7 +190,11 @@ def _render(summary: SessionActivitySummary, *, period: str) -> None:
|
|
|
193
190
|
if model_row.cost_micro_usd is None:
|
|
194
191
|
cost = "-"
|
|
195
192
|
else:
|
|
196
|
-
|
|
193
|
+
formatted_cost = format_usd_micros(
|
|
194
|
+
model_row.cost_micro_usd,
|
|
195
|
+
policy=UsdDisplayPolicy.ACTIVITY_DETAIL,
|
|
196
|
+
)
|
|
197
|
+
cost = f"{'~' if model_row.cost_estimated else ''}{formatted_cost}"
|
|
197
198
|
# T5/WS3: the lane the row's usage events ran on. "-" for a downstream-only row
|
|
198
199
|
# (no usage-event source); "mixed" when the command's events disagree.
|
|
199
200
|
if not model_row.runtime and not model_row.billing_mode:
|
|
@@ -232,7 +233,11 @@ def _render(summary: SessionActivitySummary, *, period: str) -> None:
|
|
|
232
233
|
if summary.total_cost_micro_usd is None:
|
|
233
234
|
total_cost = "n/a"
|
|
234
235
|
else:
|
|
235
|
-
|
|
236
|
+
formatted_cost = format_usd_micros(
|
|
237
|
+
summary.total_cost_micro_usd,
|
|
238
|
+
policy=UsdDisplayPolicy.ACTIVITY_DETAIL,
|
|
239
|
+
)
|
|
240
|
+
total_cost = f"{'~' if summary.cost_estimated else ''}{formatted_cost}"
|
|
236
241
|
console.print(
|
|
237
242
|
f"\n[dim]Total:[/dim] {summary.total_events} events · "
|
|
238
243
|
f"{summary.total_input_tokens}/{summary.total_output_tokens} tok · {total_cost}"
|
|
@@ -266,12 +271,3 @@ def _legacy_schema_note(skipped: int) -> str | None:
|
|
|
266
271
|
return None
|
|
267
272
|
plural = "" if skipped == 1 else "s"
|
|
268
273
|
return f"skipped {skipped} downstream telemetry record{plural} from an older Forge schema in this window"
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
def _fmt_usd(micros: int | None) -> str:
|
|
272
|
-
if micros is None:
|
|
273
|
-
return "-"
|
|
274
|
-
dollars = micros / 1_000_000
|
|
275
|
-
if dollars and abs(dollars) < 0.01:
|
|
276
|
-
return f"${dollars:.4f}"
|
|
277
|
-
return f"${dollars:.2f}"
|
|
@@ -173,7 +173,6 @@ def login(credential: str | None, profile: str | None) -> None:
|
|
|
173
173
|
"""
|
|
174
174
|
profile_name = resolve_profile(profile)
|
|
175
175
|
|
|
176
|
-
# Validate credential name
|
|
177
176
|
if credential is not None:
|
|
178
177
|
if credential in RETIRED_NAMES:
|
|
179
178
|
click.secho(RETIRED_NAMES[credential], fg="yellow", err=True)
|
|
@@ -419,7 +418,6 @@ def status(profile: str | None, as_json: bool) -> None:
|
|
|
419
418
|
state = _credential_state(cred, file_secrets, ignore_env, profile_name)
|
|
420
419
|
summary = _capability_summary(cred)
|
|
421
420
|
if state.startswith("configured"):
|
|
422
|
-
# Find primary source for display
|
|
423
421
|
primary_source = state.split("(", 1)[1].rstrip(")") if "(" in state else ""
|
|
424
422
|
configured.append(f" * {cred.name:<18} {summary} ({primary_source})")
|
|
425
423
|
else:
|
|
@@ -1114,17 +1114,21 @@ def delete_cmd(adapter: str, yes: bool) -> None:
|
|
|
1114
1114
|
return
|
|
1115
1115
|
|
|
1116
1116
|
stopped = []
|
|
1117
|
+
stop_failed = False
|
|
1117
1118
|
registry = store.read()
|
|
1118
1119
|
for process in list(registry.processes.values()):
|
|
1119
1120
|
if process.adapter_type == adapter:
|
|
1120
1121
|
try:
|
|
1121
1122
|
_stop_managed_process(process)
|
|
1122
1123
|
stopped.append(process.process_id)
|
|
1123
|
-
except Exception:
|
|
1124
|
-
|
|
1124
|
+
except Exception as e:
|
|
1125
|
+
print_error(f"{process.process_id}: {e}")
|
|
1126
|
+
stop_failed = True
|
|
1125
1127
|
|
|
1126
1128
|
if stopped:
|
|
1127
1129
|
console.print(f"Stopped managed processes: {', '.join(stopped)}")
|
|
1130
|
+
if stop_failed:
|
|
1131
|
+
sys.exit(1)
|
|
1128
1132
|
|
|
1129
1133
|
shutil.rmtree(backend_dir)
|
|
1130
1134
|
console.print(f"[green]Deleted[/green] backend config for '{adapter}'")
|