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.
Files changed (535) hide show
  1. {multi_forge-0.9.4 → multi_forge-1.0.1}/.gitignore +3 -0
  2. {multi_forge-0.9.4 → multi_forge-1.0.1}/PKG-INFO +30 -12
  3. {multi_forge-0.9.4 → multi_forge-1.0.1}/README.md +7 -5
  4. {multi_forge-0.9.4 → multi_forge-1.0.1}/pyproject.toml +49 -35
  5. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/__init__.py +3 -2
  6. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/adapters/litellm.py +19 -8
  7. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/creation.py +2 -4
  8. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/sources.py +11 -13
  9. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/activity.py +18 -22
  10. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/auth.py +0 -2
  11. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/backend.py +6 -2
  12. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/claude.py +33 -21
  13. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/config_cmd.py +305 -34
  14. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/consumer_lane_freeze.py +7 -7
  15. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/editor.py +22 -11
  16. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/extensions.py +151 -64
  17. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/gc.py +26 -1
  18. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/_helpers.py +0 -10
  19. multi_forge-1.0.1/src/forge/cli/hooks/authority.py +127 -0
  20. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_patch.py +11 -4
  21. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_policy.py +19 -6
  22. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/commands.py +115 -40
  23. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/direct_commands.py +208 -104
  24. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/policy.py +21 -12
  25. multi_forge-1.0.1/src/forge/cli/hooks/verification.py +616 -0
  26. multi_forge-0.9.4/src/forge/install/cli.py → multi_forge-1.0.1/src/forge/cli/info.py +23 -33
  27. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/logs.py +16 -3
  28. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/main.py +52 -19
  29. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory.py +17 -18
  30. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory_writer.py +2 -2
  31. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/model.py +0 -2
  32. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/output.py +6 -1
  33. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/policy.py +236 -138
  34. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy.py +215 -142
  35. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy_audit.py +9 -26
  36. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/proxy_costs.py +40 -54
  37. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/runtime.py +2 -2
  38. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/search.py +44 -46
  39. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session.py +32 -49
  40. multi_forge-1.0.1/src/forge/cli/session_authority.py +99 -0
  41. multi_forge-1.0.1/src/forge/cli/session_authority_options.py +44 -0
  42. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_codex.py +29 -1
  43. multi_forge-1.0.1/src/forge/cli/session_fork.py +742 -0
  44. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_lane.py +4 -4
  45. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_lifecycle.py +567 -151
  46. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_manage.py +232 -72
  47. multi_forge-1.0.1/src/forge/cli/session_model.py +135 -0
  48. multi_forge-1.0.1/src/forge/cli/session_repair.py +216 -0
  49. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_resume_modes.py +48 -23
  50. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_rewind.py +34 -0
  51. multi_forge-1.0.1/src/forge/cli/session_route_recovery.py +262 -0
  52. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_supervisor_options.py +3 -24
  53. multi_forge-1.0.1/src/forge/cli/status_line.py +130 -0
  54. multi_forge-1.0.1/src/forge/cli/statusline/__init__.py +9 -0
  55. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/context.py +12 -12
  56. multi_forge-0.9.4/src/forge/cli/status_line.py → multi_forge-1.0.1/src/forge/cli/statusline/formatting.py +60 -713
  57. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/names.py +1 -0
  58. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/palette.py +24 -25
  59. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/registry.py +164 -72
  60. multi_forge-1.0.1/src/forge/cli/statusline/rendering.py +40 -0
  61. multi_forge-1.0.1/src/forge/cli/statusline/sources.py +398 -0
  62. multi_forge-1.0.1/src/forge/cli/statusline/types.py +20 -0
  63. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/trace.py +7 -18
  64. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/workflow.py +95 -113
  65. multi_forge-1.0.1/src/forge/cli/workspace.py +114 -0
  66. multi_forge-1.0.1/src/forge/config/defaults/backends/litellm.yaml +459 -0
  67. multi_forge-1.0.1/src/forge/config/defaults/templates/anthropic-passthrough.yaml +31 -0
  68. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +17 -5
  69. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +8 -5
  70. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +8 -5
  71. multi_forge-1.0.1/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +38 -0
  72. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +10 -1
  73. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +10 -1
  74. multi_forge-1.0.1/src/forge/config/defaults/templates/litellm-gemini.yaml +41 -0
  75. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +13 -4
  76. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +15 -5
  77. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/litellm-openai.yaml +15 -5
  78. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +11 -4
  79. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +10 -0
  80. multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +44 -0
  81. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +3 -2
  82. multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-glm.yaml +34 -0
  83. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +6 -2
  84. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +1 -0
  85. multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +40 -0
  86. multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-openai.yaml +47 -0
  87. multi_forge-1.0.1/src/forge/config/defaults/templates/openrouter-qwen.yaml +48 -0
  88. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/loader.py +57 -30
  89. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/schema.py +278 -48
  90. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/model_catalog.yaml +628 -119
  91. multi_forge-1.0.1/src/forge/core/data/model_practices.yaml +2 -0
  92. multi_forge-1.0.1/src/forge/core/data/model_routes.yaml +1441 -0
  93. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/effort.py +6 -4
  94. multi_forge-1.0.1/src/forge/core/identifiers.py +15 -0
  95. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/_lifecycle.py +148 -32
  96. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/codex_stream.py +3 -3
  97. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/types.py +6 -5
  98. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/lanes.py +14 -16
  99. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/__init__.py +3 -1
  100. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/base.py +1 -1
  101. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/litellm.py +113 -39
  102. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/openai_compat.py +27 -0
  103. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/openrouter.py +29 -9
  104. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/detection.py +0 -2
  105. multi_forge-1.0.1/src/forge/core/llm/openrouter_policy.py +23 -0
  106. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/protocols.py +5 -1
  107. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/types.py +9 -1
  108. multi_forge-1.0.1/src/forge/core/metric_formatting.py +95 -0
  109. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/__init__.py +26 -0
  110. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/catalog.py +3 -4
  111. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/direct_model.py +16 -14
  112. multi_forge-1.0.1/src/forge/core/models/model_practices.py +264 -0
  113. multi_forge-1.0.1/src/forge/core/models/model_reference.py +34 -0
  114. multi_forge-1.0.1/src/forge/core/models/model_routes.py +430 -0
  115. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/models/types.py +8 -3
  116. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/__init__.py +9 -0
  117. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/claude_session.py +428 -156
  118. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_bridge.py +53 -23
  119. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_enrollment.py +1 -1
  120. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_interactive.py +127 -100
  121. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_session.py +195 -48
  122. multi_forge-1.0.1/src/forge/core/ops/codex_thread_index.py +15 -0
  123. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/context.py +9 -42
  124. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/gc.py +60 -20
  125. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/policy.py +66 -7
  126. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/provider_trace.py +2 -4
  127. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/proxy.py +0 -2
  128. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/resolution.py +15 -2
  129. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session.py +33 -2
  130. multi_forge-1.0.1/src/forge/core/ops/session_active.py +23 -0
  131. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session_adopt.py +23 -15
  132. multi_forge-1.0.1/src/forge/core/ops/session_authority.py +407 -0
  133. multi_forge-1.0.1/src/forge/core/ops/session_authority_launch.py +587 -0
  134. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/session_context.py +61 -33
  135. multi_forge-1.0.1/src/forge/core/ops/session_fork_execution.py +887 -0
  136. multi_forge-1.0.1/src/forge/core/ops/session_fork_preflight.py +1028 -0
  137. multi_forge-1.0.1/src/forge/core/ops/session_model.py +438 -0
  138. multi_forge-1.0.1/src/forge/core/ops/session_model_routing.py +883 -0
  139. multi_forge-1.0.1/src/forge/core/ops/session_repair.py +457 -0
  140. multi_forge-1.0.1/src/forge/core/ops/session_routing.py +327 -0
  141. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/transfer.py +13 -10
  142. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/usage_summary.py +37 -29
  143. multi_forge-1.0.1/src/forge/core/ops/workspace.py +86 -0
  144. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/paths.py +1 -3
  145. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/process.py +2 -0
  146. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/__init__.py +2 -2
  147. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/env.py +53 -23
  148. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/headless_json.py +5 -3
  149. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/routing.py +7 -3
  150. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/tagger.py +2 -3
  151. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/throttle.py +17 -18
  152. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/run_id.py +15 -13
  153. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_preflight.py +18 -8
  154. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_preflight_cache.py +8 -4
  155. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/__init__.py +20 -2
  156. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/io.py +4 -3
  157. multi_forge-1.0.1/src/forge/core/state/retention.py +108 -0
  158. multi_forge-1.0.1/src/forge/core/state/timestamps.py +212 -0
  159. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/caps.py +0 -2
  160. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/downstream.py +55 -78
  161. multi_forge-1.0.1/src/forge/core/telemetry/downstream_retention.py +592 -0
  162. multi_forge-1.0.1/src/forge/core/telemetry/jsonl_io.py +90 -0
  163. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/upstream.py +30 -43
  164. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/transcript.py +1 -1
  165. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/typing_helpers.py +6 -7
  166. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/correlation.py +7 -6
  167. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/emit.py +10 -17
  168. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/ledger.py +36 -52
  169. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/measurement.py +18 -14
  170. multi_forge-1.0.1/src/forge/core/wire_shapes.py +30 -0
  171. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/queue.py +113 -11
  172. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/types.py +1 -0
  173. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/codex_hooks.py +182 -8
  174. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hook_dispatcher.py +12 -7
  175. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hook_migration.py +8 -3
  176. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/hooks.py +17 -4
  177. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/installer.py +392 -357
  178. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/models.py +4 -3
  179. multi_forge-1.0.1/src/forge/install/path_policy.py +236 -0
  180. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/preset.py +30 -6
  181. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/runtime_removal.py +36 -31
  182. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/settings_merge.py +47 -61
  183. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_compiler.py +73 -19
  184. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_planning.py +8 -52
  185. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_validation.py +3 -3
  186. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/unmanaged.py +1 -9
  187. multi_forge-1.0.1/src/forge/policy/action_identity.py +76 -0
  188. multi_forge-1.0.1/src/forge/policy/deterministic/registry.py +100 -0
  189. multi_forge-1.0.1/src/forge/policy/diff.py +303 -0
  190. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/engine.py +12 -6
  191. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/plan_check.py +18 -16
  192. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/shadow.py +8 -5
  193. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/shadow_runner.py +4 -3
  194. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/supervisor.py +139 -53
  195. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/verdict.py +65 -22
  196. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/supervisor_lane_degrade.py +1 -1
  197. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/config.py +1 -1
  198. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/handlers.py +34 -18
  199. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/types.py +6 -2
  200. multi_forge-1.0.1/src/forge/proxy/accounting_persistence.py +68 -0
  201. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/audit_logger.py +0 -11
  202. multi_forge-1.0.1/src/forge/proxy/base_client.py +45 -0
  203. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/client_adapter.py +40 -6
  204. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/client_factory.py +46 -129
  205. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/converters.py +116 -58
  206. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/cost_logger.py +32 -49
  207. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/cost_tracker.py +63 -15
  208. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/intercept.py +196 -23
  209. multi_forge-1.0.1/src/forge/proxy/model_routes.py +75 -0
  210. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/passthrough.py +47 -32
  211. multi_forge-1.0.1/src/forge/proxy/passthrough_ingress.py +366 -0
  212. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/provider_trace_logger.py +8 -13
  213. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxies.py +6 -9
  214. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_orchestrator.py +116 -56
  215. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/reasoning.py +18 -0
  216. multi_forge-1.0.1/src/forge/proxy/request_id.py +21 -0
  217. multi_forge-1.0.1/src/forge/proxy/response_headers.py +78 -0
  218. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/responses_ingress.py +12 -5
  219. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/responses_passthrough.py +75 -68
  220. multi_forge-1.0.1/src/forge/proxy/runtime_truth.py +112 -0
  221. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/server.py +549 -480
  222. multi_forge-1.0.1/src/forge/proxy/sse_framing.py +52 -0
  223. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/utils.py +202 -23
  224. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/adversarial.py +19 -35
  225. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/consensus.py +15 -34
  226. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/engine.py +7 -4
  227. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/models.py +30 -42
  228. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/routing.py +48 -90
  229. multi_forge-1.0.1/src/forge/review/worker_preparation.py +132 -0
  230. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/runtime_config.py +254 -6
  231. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/bm25_store.py +69 -6
  232. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/content_store.py +21 -7
  233. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/engine.py +2 -2
  234. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/index_state.py +62 -19
  235. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/store.py +14 -10
  236. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/__init__.py +11 -6
  237. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/active.py +66 -13
  238. multi_forge-1.0.1/src/forge/session/artifacts.py +424 -0
  239. multi_forge-1.0.1/src/forge/session/authority.py +549 -0
  240. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/__init__.py +4 -0
  241. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/invoke.py +51 -5
  242. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/paths.py +42 -20
  243. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/cleanup.py +2 -2
  244. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/codex_invoke.py +12 -2
  245. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/consumer_lanes.py +6 -6
  246. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/context_limit.py +39 -19
  247. multi_forge-1.0.1/src/forge/session/context_rendering.py +48 -0
  248. multi_forge-1.0.1/src/forge/session/events.py +562 -0
  249. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/exceptions.py +41 -8
  250. multi_forge-1.0.1/src/forge/session/git.py +67 -0
  251. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/session_start.py +12 -37
  252. multi_forge-1.0.1/src/forge/session/index.py +849 -0
  253. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/launch.py +21 -0
  254. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/launch_confirmation.py +23 -10
  255. multi_forge-1.0.1/src/forge/session/launchability.py +29 -0
  256. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/manager.py +655 -237
  257. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/memory_writer.py +21 -30
  258. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/model_pin.py +76 -16
  259. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/models.py +107 -18
  260. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/overrides.py +61 -11
  261. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/passport.py +16 -4
  262. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/rewind.py +32 -47
  263. multi_forge-1.0.1/src/forge/session/routing.py +633 -0
  264. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/shadow_curation.py +42 -54
  265. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/store.py +161 -27
  266. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/transfer.py +110 -99
  267. multi_forge-1.0.1/src/forge/session/verification_config.py +34 -0
  268. multi_forge-1.0.1/src/forge/session/workspace.py +205 -0
  269. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/__init__.py +5 -3
  270. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/cleanup.py +43 -11
  271. multi_forge-1.0.1/src/forge/session/worktree/config_copy.py +332 -0
  272. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/worktree/create.py +78 -129
  273. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/container.py +17 -4
  274. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/analyze/forge-skill.yaml +1 -1
  275. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/challenge/forge-skill.yaml +2 -1
  276. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/resources/synthesis.md +4 -4
  277. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/SKILL.md +361 -118
  278. multi_forge-1.0.1/src/skills/qa/resources/checklist/0-enable.md +119 -0
  279. multi_forge-1.0.1/src/skills/qa/resources/checklist/10-resume.md +312 -0
  280. multi_forge-1.0.1/src/skills/qa/resources/checklist/11-config.md +267 -0
  281. multi_forge-1.0.1/src/skills/qa/resources/checklist/12-search.md +146 -0
  282. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/13-policy.md +56 -2
  283. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/14-workflow.md +30 -8
  284. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/15-skills.md +41 -17
  285. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/16-memory.md +9 -3
  286. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/17-info.md +62 -4
  287. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/18-disable.md +55 -19
  288. multi_forge-1.0.1/src/skills/qa/resources/checklist/19-uninstall.md +141 -0
  289. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/2-extension.md +18 -11
  290. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/3-authentication.md +21 -18
  291. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/4-proxy.md +383 -45
  292. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/5-session.md +456 -44
  293. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/6-hook.md +74 -35
  294. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/7-costs.md +207 -72
  295. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/8-status-line.md +63 -15
  296. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/9-direct-commands.md +45 -1
  297. multi_forge-1.0.1/src/skills/qa/resources/checklist.md +105 -0
  298. multi_forge-1.0.1/src/skills/qa/resources/coverage-map.md +58 -0
  299. multi_forge-1.0.1/src/skills/qa/resources/execution-budget.json +29 -0
  300. multi_forge-1.0.1/src/skills/qa/resources/report-template.md +96 -0
  301. multi_forge-1.0.1/src/skills/qa/resources/runtime-matrix.json +58 -0
  302. multi_forge-1.0.1/src/skills/qa/scripts/qa-artifact.py +247 -0
  303. multi_forge-1.0.1/src/skills/qa/scripts/qa-run-metrics.py +335 -0
  304. multi_forge-1.0.1/src/skills/qa/scripts/qa-selection.py +232 -0
  305. multi_forge-1.0.1/src/skills/qa/scripts/start-container.sh +1206 -0
  306. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/scripts/walkthrough-state.py +349 -85
  307. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/forge-skill.yaml +1 -1
  308. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/skills-writing-guide.md +6 -4
  309. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/forge-skill.yaml +1 -1
  310. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/scripts/smoke-test.sh +7 -8
  311. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/forge-skill.yaml +1 -1
  312. multi_forge-1.0.1/src/skills/walkthrough/SKILL.md +408 -0
  313. multi_forge-1.0.1/src/skills/walkthrough/resources/checklist.md +818 -0
  314. multi_forge-1.0.1/src/skills/walkthrough/resources/journey-map.md +55 -0
  315. multi_forge-1.0.1/src/skills/walkthrough/scripts/claude-wrapper.sh +37 -0
  316. multi_forge-1.0.1/src/skills/walkthrough/scripts/cleanup-owned.sh +592 -0
  317. multi_forge-1.0.1/src/skills/walkthrough/scripts/package-identity.py +353 -0
  318. multi_forge-1.0.1/src/skills/walkthrough/scripts/protected-paths.py +207 -0
  319. multi_forge-1.0.1/src/skills/walkthrough/scripts/run-in-repo.sh +192 -0
  320. multi_forge-1.0.1/src/skills/walkthrough/scripts/setup-test-repo.sh +470 -0
  321. multi_forge-1.0.1/src/skills/walkthrough/scripts/walkthrough-report.py +343 -0
  322. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/walkthrough/scripts/walkthrough-state.py +349 -85
  323. multi_forge-0.9.4/src/forge/cli/hooks/verification.py +0 -370
  324. multi_forge-0.9.4/src/forge/cli/session_fork.py +0 -1142
  325. multi_forge-0.9.4/src/forge/cli/session_model_pin.py +0 -108
  326. multi_forge-0.9.4/src/forge/cli/statusline/__init__.py +0 -9
  327. multi_forge-0.9.4/src/forge/config/defaults/backends/litellm.yaml +0 -242
  328. multi_forge-0.9.4/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -31
  329. multi_forge-0.9.4/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -31
  330. multi_forge-0.9.4/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -22
  331. multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -26
  332. multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -23
  333. multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -23
  334. multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -28
  335. multi_forge-0.9.4/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -28
  336. multi_forge-0.9.4/src/forge/core/state/retention.py +0 -79
  337. multi_forge-0.9.4/src/forge/core/state/timestamps.py +0 -65
  338. multi_forge-0.9.4/src/forge/core/telemetry/jsonl_io.py +0 -37
  339. multi_forge-0.9.4/src/forge/policy/deterministic/registry.py +0 -147
  340. multi_forge-0.9.4/src/forge/policy/workflow/__init__.py +0 -17
  341. multi_forge-0.9.4/src/forge/policy/workflow/branches.py +0 -67
  342. multi_forge-0.9.4/src/forge/policy/workflow/config.py +0 -63
  343. multi_forge-0.9.4/src/forge/policy/workflow/divergence.py +0 -113
  344. multi_forge-0.9.4/src/forge/policy/workflow/policy.py +0 -87
  345. multi_forge-0.9.4/src/forge/policy/workflow/stages.py +0 -288
  346. multi_forge-0.9.4/src/forge/proxy/base_client.py +0 -181
  347. multi_forge-0.9.4/src/forge/proxy/model_spec.py +0 -158
  348. multi_forge-0.9.4/src/forge/session/artifacts.py +0 -216
  349. multi_forge-0.9.4/src/forge/session/index.py +0 -602
  350. multi_forge-0.9.4/src/forge/session/worktree/config_copy.py +0 -203
  351. multi_forge-0.9.4/src/skills/qa/resources/checklist/0-enable.md +0 -84
  352. multi_forge-0.9.4/src/skills/qa/resources/checklist/10-resume.md +0 -204
  353. multi_forge-0.9.4/src/skills/qa/resources/checklist/11-config.md +0 -150
  354. multi_forge-0.9.4/src/skills/qa/resources/checklist/12-search.md +0 -64
  355. multi_forge-0.9.4/src/skills/qa/resources/checklist/19-uninstall.md +0 -151
  356. multi_forge-0.9.4/src/skills/qa/resources/checklist.md +0 -132
  357. multi_forge-0.9.4/src/skills/qa/resources/report-template.md +0 -62
  358. multi_forge-0.9.4/src/skills/qa/scripts/start-container.sh +0 -548
  359. multi_forge-0.9.4/src/skills/walkthrough/SKILL.md +0 -602
  360. multi_forge-0.9.4/src/skills/walkthrough/resources/checklist.md +0 -1036
  361. multi_forge-0.9.4/src/skills/walkthrough/scripts/run-in-repo.sh +0 -138
  362. multi_forge-0.9.4/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -205
  363. {multi_forge-0.9.4 → multi_forge-1.0.1}/LICENSE +0 -0
  364. {multi_forge-0.9.4 → multi_forge-1.0.1}/NOTICE +0 -0
  365. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/agents/.gitkeep +0 -0
  366. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/commands/.gitkeep +0 -0
  367. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/__init__.py +0 -0
  368. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/adapters/__init__.py +0 -0
  369. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/registry.py +0 -0
  370. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/__init__.py +0 -0
  371. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/base.py +0 -0
  372. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/backend/remote/openrouter.py +0 -0
  373. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/__init__.py +0 -0
  374. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/codex.py +0 -0
  375. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/guards.py +0 -0
  376. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/__init__.py +0 -0
  377. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/_group.py +0 -0
  378. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
  379. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/protocols.py +0 -0
  380. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
  381. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/memory_report.py +0 -0
  382. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_adopt.py +0 -0
  383. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_memory.py +0 -0
  384. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/session_routing.py +0 -0
  385. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/statusline/throttle.py +0 -0
  386. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/telemetry.py +0 -0
  387. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/cli/transfer.py +0 -0
  388. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/__init__.py +0 -0
  389. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/dataclass_utils.py +0 -0
  390. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/__init__.py +0 -0
  391. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/backends/__init__.py +0 -0
  392. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/config/defaults/templates/__init__.py +0 -0
  393. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/__init__.py +0 -0
  394. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/__init__.py +0 -0
  395. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/capabilities.py +0 -0
  396. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/credentials_file.py +0 -0
  397. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/protocols.py +0 -0
  398. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/secrets.py +0 -0
  399. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/auth/template_secrets.py +0 -0
  400. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/backend_dependency.py +0 -0
  401. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/credential_registry.py +0 -0
  402. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/__init__.py +0 -0
  403. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
  404. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
  405. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
  406. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/__init__.py +0 -0
  407. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/claude.py +0 -0
  408. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/invoker/codex.py +0 -0
  409. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/clients/__init__.py +0 -0
  410. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/credentials.py +0 -0
  411. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/llm/errors.py +0 -0
  412. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/logging.py +0 -0
  413. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/naming.py +0 -0
  414. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/backend_reconcile.py +0 -0
  415. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/ops/codex_adopt.py +0 -0
  416. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/provider_types.py +0 -0
  417. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/cost_tracking.py +0 -0
  418. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/llm_call.py +0 -0
  419. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/proxy.py +0 -0
  420. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/session_runner.py +0 -0
  421. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/reactive/structured_output.py +0 -0
  422. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/__init__.py +0 -0
  423. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
  424. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime/registry.py +0 -0
  425. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/runtime_vocab.py +0 -0
  426. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/exceptions.py +0 -0
  427. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/lock.py +0 -0
  428. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/state/versioned_store.py +0 -0
  429. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/__init__.py +0 -0
  430. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/telemetry/vocabulary.py +0 -0
  431. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/tiers.py +0 -0
  432. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/__init__.py +0 -0
  433. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/billing.py +0 -0
  434. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/usage/vocabulary.py +0 -0
  435. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/core/workqueue/__init__.py +0 -0
  436. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/__init__.py +0 -0
  437. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/doctor.py +0 -0
  438. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/exceptions.py +0 -0
  439. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/module_planning.py +0 -0
  440. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/ownership.py +0 -0
  441. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/project_compat.py +0 -0
  442. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/project_registry.py +0 -0
  443. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/settings_rollback.py +0 -0
  444. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/skill_cache.py +0 -0
  445. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/tracking.py +0 -0
  446. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/install/version.py +0 -0
  447. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/__init__.py +0 -0
  448. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/__init__.py +0 -0
  449. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/base.py +0 -0
  450. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
  451. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/deterministic/tdd.py +0 -0
  452. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/protocols.py +0 -0
  453. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/queries.py +0 -0
  454. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/semantic/__init__.py +0 -0
  455. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/store.py +0 -0
  456. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/__init__.py +0 -0
  457. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/policy/team/prompts.py +0 -0
  458. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/__init__.py +0 -0
  459. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/data_models.py +0 -0
  460. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/error_hints.py +0 -0
  461. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/metrics.py +0 -0
  462. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/ports.py +0 -0
  463. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_identity.py +0 -0
  464. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/proxy_startup.py +0 -0
  465. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/proxy/stream_relay.py +0 -0
  466. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/__init__.py +0 -0
  467. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/__init__.py +0 -0
  468. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
  469. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
  470. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-performance.md +0 -0
  471. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-quick.md +0 -0
  472. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview-security.md +0 -0
  473. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/codereview.md +0 -0
  474. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
  475. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/debate_evaluation.md +0 -0
  476. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/docreview-quick.md +0 -0
  477. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/docreview.md +0 -0
  478. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/resources/thinkdeep.md +0 -0
  479. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/review/synthesis.py +0 -0
  480. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/__init__.py +0 -0
  481. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/exceptions.py +0 -0
  482. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/extractor.py +0 -0
  483. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/search/tokenizer.py +0 -0
  484. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/addendum.py +0 -0
  485. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/cleanup.py +0 -0
  486. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/claude/relocate.py +0 -0
  487. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/codex_handoff.py +0 -0
  488. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/config.py +0 -0
  489. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/effective.py +0 -0
  490. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/__init__.py +0 -0
  491. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/hooks/models.py +0 -0
  492. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/identity.py +0 -0
  493. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/memory_inheritance.py +0 -0
  494. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/plan_resolution.py +0 -0
  495. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/prev_sessions.py +0 -0
  496. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/project_memory.py +0 -0
  497. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/session/validation.py +0 -0
  498. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/__init__.py +0 -0
  499. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/forge/sidecar/docker.py +0 -0
  500. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/analyze/content.md +0 -0
  501. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/challenge/content.md +0 -0
  502. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/content.md +0 -0
  503. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/forge-skill.yaml +0 -0
  504. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/consensus/resources/synthesis.md +0 -0
  505. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/debate/content.md +0 -0
  506. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/debate/forge-skill.yaml +0 -0
  507. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/content.md +0 -0
  508. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/panel/forge-skill.yaml +0 -0
  509. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
  510. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
  511. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/content.md +0 -0
  512. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/claude-4.6.md +0 -0
  513. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/claude-4.8.md +0 -0
  514. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/gemini-3.1.md +0 -0
  515. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/references/gpt-5.5.md +0 -0
  516. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-anthropic.md +0 -0
  517. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-gemini.md +0 -0
  518. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code-openai.md +0 -0
  519. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review/resources/code.md +0 -0
  520. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/content.md +0 -0
  521. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
  522. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
  523. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
  524. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/review-docs/resources/docs.md +0 -0
  525. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/content.md +0 -0
  526. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/smoke-test/forge-skill.yaml +0 -0
  527. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/content.md +0 -0
  528. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-anthropic.md +0 -0
  529. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-gemini.md +0 -0
  530. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code-openai.md +0 -0
  531. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/code.md +0 -0
  532. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
  533. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-gemini.md +0 -0
  534. {multi_forge-0.9.4 → multi_forge-1.0.1}/src/skills/understand/resources/docs-openai.md +0 -0
  535. {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.4
1
+ Metadata-Version: 2.5
2
2
  Name: multi-forge
3
- Version: 0.9.4
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: fastapi>=0.115.14
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[proxy]>=1.88.0
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: pyyaml>=6.0.0
35
- Requires-Dist: rich>=13.9.0
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>=0.31.1
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 `/forge:smoke-test` (Claude) or `$smoke-test` (Codex) for a read-only health check, or
184
- `/forge:walkthrough` for a guided tour in a sandboxed test repo.
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 `/forge:<name>`, Codex invokes `$<name>`: `analyze`,
267
- `challenge`, `consensus`, `debate`, `panel`, `review`, `review-docs`, `smoke-test`, `understand`. Only
268
- `/forge:walkthrough` and `/forge:qa` are Claude-only.
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 `/forge:smoke-test` (Claude) or `$smoke-test` (Codex) for a read-only health check, or
143
- `/forge:walkthrough` for a guided tour in a sandboxed test repo.
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 `/forge:<name>`, Codex invokes `$<name>`: `analyze`,
226
- `challenge`, `consensus`, `debate`, `panel`, `review`, `review-docs`, `smoke-test`, `understand`. Only
227
- `/forge:walkthrough` and `/forge:qa` are Claude-only.
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.9.4"
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
- # `uvloop` is pulled in transitively via `litellm[proxy]` and has no cp314 wheel
11
- # at time of writing; building from source fails on macOS. Lift the upper bound
12
- # once uvloop ships 3.14 wheels. See https://github.com/hapa1i/multi-forge/issues/1
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.0",
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.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
- # Plain `fastapi` (no `[standard]` extras): Forge uses none of websockets/
42
- # uvloop/httptools/multipart/jinja2. `watchfiles` is declared explicitly
43
- # because Forge owns `--reload` (`forge proxy server --reload`).
44
- "fastapi>=0.115.14",
45
- "uvicorn>=0.31.1",
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
- # Floor = the gate-proven version for gemini-3.6-flash (see
48
- # tests/integration/proxy/test_proxy_local_litellm_e2e.py); packaged
49
- # cost-map pricing for that model first ships in the v1.94 line.
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. Keep this exclusion local to the duplicate
112
- # script name; it is not a source-of-truth relationship between the skills.
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
- "anthropic>=0.100.0",
165
- "google-genai>=2.5.0",
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 (best effort).
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.kill()
113
- except OSError:
114
- pass # Process already exited
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
- """Stop LiteLLM backend (best effort).
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
- os.kill(process.pid, 15) # SIGTERM
137
- except (ProcessLookupError, PermissionError):
138
- pass
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
- # Use convention-based path: defaults/backends/{adapter}.yaml
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 # No default to compare against
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 _SOURCE_ID_RE.match(source.id):
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 _SOURCE_ID_RE.match(source.backend_kind):
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 _SOURCE_ID_RE.match(template_name):
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
- # reachable only via codex (the runtime that holds the login). First
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; reachable
461
- # only via claude_code (the runtime that holds the login). The claude_code
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
- # Codex-facing OpenAI Responses passthrough (forge codex start --proxy). A
509
- # local LiteLLM serves /v1/responses upstream, so reasoning is preserved
510
- # byte-for-byte and the x-litellm-response-cost header yields real cost.
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, timedelta, timezone
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
- now_local = datetime.now()
86
- if period == "today":
87
- return now_local.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(timezone.utc)
88
- if period == "week":
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
- cost = f"{'~' if model_row.cost_estimated else ''}{_fmt_usd(model_row.cost_micro_usd)}"
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
- total_cost = f"{'~' if summary.cost_estimated else ''}{_fmt_usd(summary.total_cost_micro_usd)}"
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
- pass
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}'")