Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d087565
chore(deps): bump subswitch 0.2.0 -> 0.3.0
dean0x Aug 31, 2026
b547db3
fix(post-install): merge settings.json instead of overwriting on init…
dean0x Aug 31, 2026
fa1e28e
fix(proxy): gate env-var stripping on proxy.json existence (#313)
dean0x Aug 31, 2026
f5edf41
fix(proxy): evaluate foreign-env check before adopted early-return (#…
dean0x Aug 31, 2026
236392e
fix(ensure-proxy): attempt binPath re-resolution before warning (#313)
dean0x Aug 31, 2026
e3cea21
chore(test): update subswitch version pin to 0.3.0 in packaging guard
dean0x Aug 31, 2026
4a9c323
refactor: extract loop-invariant hook obj and drop tombstone comment …
dean0x Aug 31, 2026
f73c956
fix(proxy): never gate proxy-hook removal on the env-strip evidence (…
dean0x Aug 31, 2026
ae57dd3
fix(proxy): strip routing-config keys the 0.3.0 relay rejects (#313)
dean0x Aug 31, 2026
83db71e
fix(post-install): drop the vestigial settings prompt, guard merge sh…
dean0x Aug 31, 2026
0d11808
test(post-install): pin installSettings parse-failure and wiring beha…
dean0x Aug 31, 2026
da49bff
docs(knowledge): update external-model-routing feature knowledge base
dean0x Aug 31, 2026
69b963e
chore(deps): bump subswitch 0.3.0 -> 0.4.0
dean0x Sep 1, 2026
ba09de9
fix(proxy): align subswitch integration with 0.4.0 config surface
dean0x Sep 1, 2026
c98bd06
fix(proxy): stop injecting anthropic.connectTimeoutMs into generated …
dean0x Sep 1, 2026
34aee33
fix(proxy): complete subswitch 0.4.0 legacy-key strip list (#313)
dean0x Sep 1, 2026
93f14ef
docs: sync 0.4.0 alignment across CLAUDE.md, KNOWLEDGE.md, index.md, …
dean0x Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 96 additions & 29 deletions .devflow/features/external-model-routing/KNOWLEDGE.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .devflow/features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
- **resolve-pipeline** — src/assets/commands/resolve.mds, src/assets/agents/triage.md, src/assets/agents/code.md, src/core/plugins.ts, src/assets/commands/code-review.mds — Use when modifying /resolve or /code-review convergence logic, adding or changing Triage disposition rules (including DUPLICATE collapsing), adjusting Code-agent operating modes (issue-fix/validation-fix), touching the resolution-summary.md parser contract, changing the Verification Gate retry loop, understanding how DIFF_FILES flows from git validate-branch into blast-radius triage, or working on traceability operations (fetch-review-threads, resolve-review-threads, post-resolution-summary, check-merge-readiness, THREAD_MAP). Keywords: resolve, triage, disposition matrix, blast-radius, FIX_NOW, FIX_SEPARATE, TECH_DEBT, FALSE_POSITIVE, BY_DESIGN, ESCALATED, DUPLICATE, duplicate-grouping, duplicates-collapse, duplicate_of, resolution-summary, convergence parser, DIFF_FILES, issue-fix, validation-fix, Verification Gate, manage-debt, COMPLIANCE_SKILL_INSTALLED, TRACEABILITY DEGRADED, fetch-review-threads, THREAD_MAP, post-resolution-summary, Third-Party Threads, check-merge-readiness, ext-N, D7, D9, PF-024.
- **installer-shadowing** — src/targets/claude-code/installer.ts, src/targets/claude-code/legacy.ts, src/cli/commands/init.ts, src/cli/commands/init-seed.ts, src/cli/commands/uninstall.ts, src/cli/commands/rules.ts, src/cli/commands/skills.ts, src/cli/commands/flags.ts, src/cli/flags-view, src/cli/tui, src/core/plugins.ts, src/core/assets.ts, src/core/paths.ts, src/core/manifest.ts, src/core/flags.ts, src/core/feature-config.ts, src/core/orphan-sweep.ts, src/core/migrations.ts, src/cli/commands/compliance-prompts.ts — Use when modifying the install pipeline (installViaFileCopy, installAllRules, composeScripts, InstallReport), adding or changing skill/rule shadow override logic, touching uninstall scope (enumerateUserDevFlowContent, removeDevFlowInstallArtifacts, resolveDevflowDirCleanup, installArtifactPaths, sweepDevflowNamespaces, resolveProjectDataCleanup) or install-artifact cleanup, extending the CLI skills/rules/flags management commands, working with asset directory accessors (rulesDir, skillsDir, commandsDir) and package-root resolution, modifying the init seeding layer (resolveInitSeed, resolveSeedFeatures, resolveSeedFlags, resolveSeedPlugins, --reset, FlagsRecord, knownPlugins, readConfigIfPresent, resolveExistingViewMode, getAllCommandNames, proxy), working on the flags TUI (FlagsViewState, FlagRow, buildFlagRows, collectFlagRecord, effectiveDisplay, blurb, inline mode, RunTuiSpec screen) or the flags CLI (createFlagsCommand, lookupFlag, persistFlagConfig, formatFlagValue), or working on the compliance wizard step (shouldRunComplianceStep, runComplianceStep, modePromptShown, CompliancePromptIO). Keywords: installViaFileCopy, installAllRules, composeScripts, InstallReport, RuleInstallOutcome, SkillShadowState, RuleShadowState, shadow, unshadow, validateSkillShadow, validateRuleShadow, seedRuleShadow, prefixSkillName, unprefixSkillName, devflow:, skills, rules, uninstall, EISDIR, enumerateUserDevFlowContent, removeDevFlowInstallArtifacts, resolveDevflowDirCleanup, installArtifactPaths, enumerateDryRunExtras, sweepDevflowNamespaces, resolveProjectDataCleanup, runDryRunPhase, runSelectivePhaseForScope, runFullPhaseForScope, runCleanupPhase, getPackageRoot, isContainedIn, rulesDir, skillsDir, agentsDir, commandsDir, scriptsDir, LEGACY_SKILL_NAMES, sweepOrphanedAssets, SweepResult, sweepOrphans, sweepFailures, SweepFailure, mdFileName, mdEntryName, orphan sweep, getAllSkillNames, getAllCommandNames, getAllAgentNames, DELETED_PLUGIN_NAMES, EXCLUDED, resolveInitSeed, resolveSeedFeatures, resolveSeedFlags, resolveSeedPlugins, resolveResetGatedInputs, applyCliToggles, FlagsRecord, FlagsRecordValue, getDefaultFlagsRecord, parseManifestFlags, migrateLegacyFlagsToRecord, sanitizeFlagsRecord, coerceFlagValue, parseFlagValueInput, neutralValueOf, isNeutral, countActiveFlags, readViewMode, knownPlugins, readConfigIfPresent, resolveExistingViewMode, resolveFinalViewMode, reset, init-seed, proxy, reapplyAgentMapping, revertExternalAgents, agent-models.json, proxy.json, proxy-routing.json, proxy.pid, applyDisableToSettings, buildRealPreflightDeps, canonicalise-agent-keys-v1, AnyMigration, migrations.json, compliance-prompts, shouldRunComplianceStep, CompliancePromptIO, runComplianceStep, modePromptShown, createFlagsCommand, lookupFlag, persistFlagConfig, FlagsViewState, FlagRow, buildFlagRows, collectFlagRecord, buildStops, cycleForward, cycleBackward, sanitizeCell, padToVisible, truncateVisible, effectiveDisplay, EffectiveDisplay, formatFlagValue, blurb, FlagDefCommon, INLINE_MARGIN, cursorUp, RunTuiSpec, screen, inline.
- **learning-capture-system** — src/assets/scripts/hooks, src/assets/agents/learning.md, src/cli/commands/learning.ts, src/core/feature-config.ts, src/core/learning-tuning-config.ts, src/hud/components/learning-counts.ts, src/assets/commands/_partials — Use when modifying capture hooks (capture-prompt/capture-turn/capture-question), the learning or memory pending-turns queues, the Learning agent (src/assets/agents/learning.md), the session-start-context learning directive, the feature-config toggles, the learning tuning config, the decisions content files (decisions.md/pitfalls.md/index.md) or their ledger ops, or the devflow learning CLI. Keywords: capture-prompt, capture-turn, capture-question, queue-append, pending-turns, memory-worker, Learning agent, learning directive, LEARNING MAINTENANCE, DEVFLOW_BG_UPDATER, learning-lock, queue_read_gates, decisions_load, DECISIONS_CONTEXT, feature-config, config.json, learning.json, decisions-ledger, assign-anchor, retire-anchor, refresh-anchor, render-decisions, staged-write CAS, WORKING-MEMORY.md.new, segmentDetails, amendments, is-hex-sha, verify_and_swap, compute_commits_since_note, divergence guard, isSafeRawBody.
- **external-model-routing** — src/core/proxy-state.ts, src/core/external-models.ts, src/core/agent-models.ts, src/core/agent-state.ts, src/core/agent-frontmatter.ts, src/core/codex-auth-inspect.ts, src/core/model-discovery.ts, src/core/cache.ts, src/core/proxy-log.ts, src/cli/commands/proxy.ts, src/cli/commands/agents.ts, src/cli/agents-view, src/cli/tui — Use when working on the proxy lifecycle (enable/disable/status/preflight), the ensure-proxy hook, per-agent model mapping, agent frontmatter rewriting, or the agents TUI. Keywords: proxy, external-model-routing, GPT, agent-models, ensure-proxy, frontmatter, devflow proxy, devflow agents, subswitch, ANTHROPIC_BASE_URL, dormancy, reapplyAgentMapping.
- **external-model-routing** — src/core/proxy-state.ts, src/core/external-models.ts, src/core/agent-models.ts, src/core/agent-state.ts, src/core/agent-frontmatter.ts, src/core/codex-auth-inspect.ts, src/core/model-discovery.ts, src/core/cache.ts, src/core/proxy-log.ts, src/cli/commands/proxy.ts, src/cli/commands/agents.ts, src/cli/agents-view, src/cli/tui — Use when working on the proxy lifecycle (enable/disable/status/preflight), the ensure-proxy hook, per-agent model mapping, agent frontmatter rewriting, or the agents TUI. Keywords: proxy, external-model-routing, GPT, agent-models, ensure-proxy, frontmatter, devflow proxy, devflow agents, subswitch, ANTHROPIC_BASE_URL, dormancy, reapplyAgentMapping, proxyJsonExists, applyProxyTeardownToSettings, D-STRIP-1, mergeDevflowSettingsTemplate, subswitch 0.4.0.
- **compliance-feature** — src/core/compliance.ts, src/targets/claude-code/compliance-install.ts, src/cli/commands/compliance.ts, src/assets/skills/compliance, src/assets/rules/compliance.md, src/assets/agents/git.md, src/assets/commands/code-review.mds, src/assets/commands/plan.mds, src/assets/commands/implement.mds, src/assets/commands/resolve.mds, src/assets/commands/release.md — Use when adding or modifying the compliance feature (framework registry, converge contract, CLI, rule stamping), changing how host commands resolve COMPLIANCE_SKILL_INSTALLED, modifying traceability operations in the Git agent (learn-conventions, issue-first, thread resolution, shipped markers, release evidence), or extending the D4 DEGRADED contract. Keywords: compliance, COMPLIANCE_SKILL_INSTALLED, convergeComplianceArtifacts, convergeFromManifest, frameworks, FEATURE_OWNED_SKILLS, traceability, D4, D9, gather-release-evidence, conventions.md, resolve-review-threads, ensure-traceable-issue, stamper, manifest-group, ComplianceFeatureState.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- **Routing runtime pinned to `subswitch@0.4.0`** (from `0.2.0`). Over-window Anthropic-bound request bodies are now streamed upstream instead of being rejected by the relay, so long prompts no longer fail at the proxy; only translated (Codex) routes still return `413 request_too_large`. `buildRoutingConfigJson` no longer injects `anthropic.connectTimeoutMs` — the relay's own default (10 s, connect-only) governs; user-set values are preserved as before. The injection was a 0.2.0-era workaround artifact that outlived its purpose once the key's semantics were narrowed to DNS+TCP connect only.

### Fixed
- **`proxy-routing.json` upgrade safety**: `buildRoutingConfigJson` now strips all seven `limits.*` sub-keys that `subswitch@0.4.0` promotes to hard startup errors: `limits.maxBodyBytes` (renamed `limits.maxBufferedBodyBytes`), `limits.maxUpstreamSockets` (moved to `anthropic.maxUpstreamSockets`), `limits.streamIdleTimeoutMs` (moved to `providers.codex.streamIdleTimeoutMs`), `limits.requestTimeoutMs` (moved to `providers.codex.requestTimeoutMs`), and `limits.maxSseEventBytes` (moved to `providers.codex.maxSseEventBytes`), joining the existing strips for `anthropic.streamIdleTimeoutMs`, `limits.connectTimeoutMs`, and `limits.maxConcurrentRequests`. A hand-edited config carrying any of these keys would have killed the relay on every session start — spawned by the `ensure-proxy` hook, with no route back. The `limits` block is now omitted entirely when all its sub-keys are stripped (matching the `anthropic` block's existing omit-when-empty behaviour).

**Upgrade**: no action required. If you hand-edited `~/.devflow/proxy-routing.json` to set any of the seven stripped `limits.*` keys, move their values to the 0.4.0 target paths (`limits.maxBufferedBodyBytes`, `anthropic.maxUpstreamSockets`, or the appropriate `providers.codex.*` key); the next `devflow proxy --enable` drops the stale keys for you.

---

## [2.3.0] - 2026-08-31
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Debug logs stored at `~/.devflow/logs/{project-slug}/`.

Knowledge write-back is in-command (not a background pipeline): gated by `devflow knowledge --enable/--disable` (flips `knowledge` in feature config); Knowledge agent writes directly at workflow end.

**External Model Routing (Devflow Proxy)**: Routes Devflow agents through GPT models via an OpenAI/Codex subscription using a local relay. Feature state is manifest-gated (like ambient/hud/rules, per ADR-001): `manifest.features.proxy` is the source of truth; `~/.devflow/proxy.json` holds runtime authority (enabled, port, binPath, configPath, resolvedAt, devflowVersion). `~/.devflow/proxy-routing.json` holds the routing config (port only — bare `{port}` object; the 0.2.0 routing runtime rejects unrecognised keys). The `ensure-proxy` hook (SessionStart + UserPromptSubmit, registered/removed by `addProxyHooks`/`removeProxyHooks`) auto-starts the relay when a session begins. `ANTHROPIC_BASE_URL` and `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT` are injected into (and stripped from) `settings.json` at CLI enable/disable time via `applyProxyEnv`/`stripProxyEnv`, not by the hook (the window var is removed unconditionally on strip; the URL delete is port-scoped to avoid clobbering foreign gateways). Toggle via `devflow proxy --enable/--disable/--status` or via the Advanced init wizard. Enabling runs `runProxyPreflight` (4 checks: bin, codex auth, port, settings), spawns the relay, then gates on a post-spawn doctor verification against the live relay (doctor requires a running relay to pass); on doctor failure the enable rolls back, killing the relay only if it spawned it. Init runs the same preflight but never spawns or runs doctor — the first session's ensure-proxy hook starts the relay; on init preflight failure: warning + force-disabled, init never aborted (avoids PF-009). Disabling reverts agent frontmatter to Claude defaults but preserves the model mapping for re-enable. Default OFF; Advanced-only — never part of Recommended defaults.
**External Model Routing (Devflow Proxy)**: Routes Devflow agents through GPT models via an OpenAI/Codex subscription using a local relay. Feature state is manifest-gated (like ambient/hud/rules, per ADR-001): `manifest.features.proxy` is the source of truth; `~/.devflow/proxy.json` holds runtime authority (enabled, port, binPath, configPath, resolvedAt, devflowVersion). `~/.devflow/proxy-routing.json` holds the routing config; `buildRoutingConfigJson` writes port-only on a fresh write (no `anthropic` block injected — the relay's own default governs); user-set keys in `logLevel`/`anthropic`/`providers`/`limits` blocks are preserved from existing content; the 0.4.0 routing runtime rejects unrecognised top-level keys and hard-fails on registered legacy keys — `anthropic.streamIdleTimeoutMs`, `limits.connectTimeoutMs`, `limits.maxConcurrentRequests`, `limits.maxBodyBytes`, `limits.maxUpstreamSockets`, `limits.streamIdleTimeoutMs`, `limits.requestTimeoutMs`, `limits.maxSseEventBytes` — which `buildRoutingConfigJson` strips. The `ensure-proxy` hook (SessionStart + UserPromptSubmit, registered/removed by `addProxyHooks`/`removeProxyHooks`) auto-starts the relay when a session begins; hook removal is unconditional on disable. `ANTHROPIC_BASE_URL` and `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT` are injected into (and stripped from) `settings.json` at CLI enable/disable time via `applyProxyEnv`/`applyProxyTeardownToSettings`, not by the hook; env stripping is evidence-gated on `~/.devflow/proxy.json` existing (`proxyJsonExists()`); the URL delete is port-scoped to avoid clobbering foreign gateways; preflight refuses a foreign `ANTHROPIC_BASE_URL` before adopting a healthy relay; the `ensure-proxy` hook re-resolves a missing `binPath` before warning. Toggle via `devflow proxy --enable/--disable/--status` or via the Advanced init wizard. Enabling runs `runProxyPreflight` (4 checks: bin, codex auth, port, settings), spawns the relay, then gates on a post-spawn doctor verification against the live relay (doctor requires a running relay to pass); on doctor failure the enable rolls back, killing the relay only if it spawned it. Init runs the same preflight but never spawns or runs doctor — the first session's ensure-proxy hook starts the relay; on init preflight failure: warning + force-disabled, init never aborted (avoids PF-009). Disabling reverts agent frontmatter to Claude defaults but preserves the model mapping for re-enable. Default OFF; Advanced-only — never part of Recommended defaults.

**Per-Agent Model Configuration**: User overrides to agent model assignments persist in `~/.devflow/agent-models.json` (deviations only — absent entry = shipped default). `reapplyAgentMapping` runs after every `devflow init` post-install to re-apply user overrides to freshly copied agent files. `revertExternalAgents` reverts all agents to shipped defaults (called on proxy disable and before agent removal on uninstall). GPT model assignments are **dormant** when routing is off — they are stored in `agent-models.json` but not written to agent frontmatter until routing is enabled. Manage via `devflow agents` TUI or `devflow agents --list/--set/--reset`. Core source files: `src/core/agent-frontmatter.ts` (pure rewrite engine), `src/core/agent-models.ts` (schema + apply/revert), `src/core/external-models.ts` (CLAUDE_MODEL_ALIASES, isClaudeModelName, isDormantExternalModel — leaf module), `src/core/model-discovery.ts` (discoverExternalModels, getExternalModelsCached, cache-warming), `src/core/cache.ts` (cache read/write, 0700/0600 permissions, parseRawEnvelope), `src/core/proxy-log.ts` (scrubChildEnv, openProxyLog, relay env allowlisting), `src/core/proxy-state.ts` (state I/O), `src/cli/commands/proxy.ts` (CLI + hook wiring), `src/cli/commands/agents.ts` (CLI), `src/cli/agents-view/` (TUI — state, render, terminal; thin adapter over the shared `src/cli/tui/` driver).

Expand Down Expand Up @@ -199,7 +199,7 @@ Per-project runtime files live under `.devflow/`:

~/.devflow/
├── proxy.json # Proxy runtime state (enabled, port, binPath, configPath, resolvedAt, devflowVersion) — global, not per-project
├── proxy-routing.json # Routing config (port only — bare {port} object) read by the ensure-proxy hook
├── proxy-routing.json # Routing config (port-only by default; user-set anthropic/logLevel/providers/limits preserved) read by the ensure-proxy hook
├── proxy.pid # Relay PID — written by CLI enable and by the ensure-proxy hook spawn (transient)
├── .proxy-spawn.lock/ # Hook spawn lock dir — prevents concurrent session double-spawn (transient)
├── agent-models.json # Per-agent model overrides (deviations only; absent = shipped default)
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@clack/prompts": "^0.9.1",
"commander": "^12.0.0",
"picocolors": "^1.1.1",
"subswitch": "0.2.0"
"subswitch": "0.4.0"
},
"devDependencies": {
"@mdscript/mds": "0.2.0",
Expand Down
Loading