Skip to content

feat(desktop): harness-agnostic config bridge#887

Merged
wpfleger96 merged 32 commits into
mainfrom
wpfleger/phase4-config-bridge
Jun 29, 2026
Merged

feat(desktop): harness-agnostic config bridge#887
wpfleger96 merged 32 commits into
mainfrom
wpfleger/phase4-config-bridge

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Adds a harness-agnostic config provenance surface that reads agent configuration from all available sources and exposes it to the frontend with full provenance — where each value came from.

Agent config was fragmented across four uncoordinated mechanisms with no single surface to see what a running agent would actually use. The silent failure mode was concrete: Wes's goose personas failed because active_provider in ~/.config/goose/config.yaml was invisible to Buzz and overrode our injected model. The bridge surfaces that ambient config, establishes a clear precedence order, and exposes write-back metadata so the UI can route writes to the cheapest live mechanism.

This PR ships a read-only config surface. No write capability ships here — ConfigWriteMechanism is retained as read-only display metadata used in provenanceSentence() for origin badge display, not as a write mechanism. Write dispatch lands in a follow-on PR.

  • New config_bridge module with per-runtime config file readers: YAML for goose (~/.config/goose/config.yaml), JSON for Claude Code (~/.claude/settings.json + ~/.claude.json), TOML for Codex (~/.codex/config.toml). 25+ unit tests covering all formats, malformed files, and missing files.
  • Four-tier precedence: Buzz-explicit > ACP configOptions from session/new > env vars > config file on disk. Pre-spawn surfaces tier 2 only; post-spawn adds ACP tiers.
  • session_config_captured observer event emitted after session/new (and after apply_model_switch so the desktop caches the post-switch state); put_agent_session_config Tauri command populates SessionConfigCache in AppState. parse_models handles both the object shape ({currentModelId, availableModels}) and legacy array shape from session/new. Stable agents that report model via configOptions (category=model) are also surfaced.
  • get_agent_config_surface Tauri command. Config file "extra" fields are extracted by walking the user's config directly (config-driven iteration), not by consulting a vendored schema — all keys the user has set are surfaced automatically regardless of whether the schema defines them (intentional: supports arbitrary keys like env vars). Note: this currently applies to Claude and Codex; Goose extras are still hardcoded (tracked in goose.rs TODO).
  • AgentConfigPanel component with origin badges (Buzz/ACP/ConfigFile/Env), collapsible advanced section, and sources footer showing all four tier statuses. Override/strikethrough rendering shows superseded config values inline. Read-only fields display an info icon with tooltip.
  • ModelPicker shows provenance label when config surface data is available post-spawn (best-effort, non-blocking).
  • Playwright E2E screenshot spec covering 7 scenarios (goose, claude-code, codex, pre-spawn, overrides, advanced section, sources footer) for visual review.
  • is_required: bool on NormalizedField (Rust) / isRequired: boolean on NormalizedField (TypeScript), populated from a new required_normalized_fields slice on KnownAcpRuntime. The UI does not yet act on isRequired; that wiring lands in a follow-on PR.
  • spawn_key_refusal guard restored at the top of build_deploy_payload — provider deploy now fails closed when the private key is unavailable, matching local spawn behavior.
  • Session config cache cleared for exited agents in sync_managed_agent_processes callers — crashed/restarted agents no longer keep stale ACP tiers.
  • Persona-snapshotted model re-tagged as PersonaDefault when record.model == persona_model and no live override is active, so persona-created agents show the correct origin.
  • record.provider checked as fallback in build_provider_field so records with only the structured provider field (no env var) surface the correct provider.
  • ModelPicker invalidates the managed agents query after updateManagedAgent; session_config_captured triggers immediate config-surface query invalidation via a callback slot in useManagedAgentObserverBridge.

Stack: #794 → this PR

@wpfleger96 wpfleger96 force-pushed the wpfleger/phase3b-normalized-config branch 2 times, most recently from 66de98f to 60f95a2 Compare June 6, 2026 01:02
@wpfleger96 wpfleger96 changed the title feat(desktop): phase 4 — harness-agnostic config bridge feat(desktop): harness-agnostic config bridge Jun 6, 2026
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase3b-normalized-config branch 6 times, most recently from de81ed1 to 0c44d4e Compare June 9, 2026 17:57
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch from 44cc07f to 678b871 Compare June 9, 2026 18:02
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase3b-normalized-config branch from 0c44d4e to ba28ea0 Compare June 9, 2026 20:31
Base automatically changed from wpfleger/phase3b-normalized-config to main June 9, 2026 20:42
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch 2 times, most recently from c392c34 to 9939a10 Compare June 11, 2026 19:24
wpfleger96 pushed a commit that referenced this pull request Jun 11, 2026
wpfleger96 pushed a commit that referenced this pull request Jun 11, 2026
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch from 3228e7a to 139ada2 Compare June 11, 2026 20:16
wpfleger96 pushed a commit that referenced this pull request Jun 11, 2026
wpfleger96 pushed a commit that referenced this pull request Jun 11, 2026
wpfleger96 pushed a commit that referenced this pull request Jun 11, 2026
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch 5 times, most recently from 69e6bc8 to fba912f Compare June 16, 2026 21:33
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch 3 times, most recently from ac3a45e to 69d2c4e Compare June 17, 2026 04:19
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 27 commits June 29, 2026 16:44
The any-unsupported-fails-fast counting for a live model switch was locked
inside the sendLiveSwitch useCallback in ModelPicker, verified by read but
not unit-pinned. Extract the counting (remaining decrement, immediate
unsupported reject, resolve-once, unsubscribe-once, timeout fallback) into a
pure awaitLiveSwitchOutcome helper with the relay subscription, per-channel
sends, and timeout scheduler injected. The component wires the real
subscribeControlResults / window timer / dispatch; behavior is unchanged.

The helper is node:test-drivable with synthetic frames and a manual clock.
The masking-guard test fails against a first-ack-resolves variant and passes
against the shipped fail-fast logic.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The interim settled===false checks used a single await Promise.resolve()
drain, which the outcome.then callback outruns by one microtask tick, so
the guard passed even against a first-ack-resolves variant. Drain five
ticks before each interim check so it deterministically regresses an early
resolve.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
A genuine-explicit agent (own record model, no persona) that live-switched
mid-session rendered nowhere: resolve_config_surface passed no override
baseline for an explicit record, so apply_runtime_override early-returned
and the panel showed the stale record model as primary with the live model
struck through — display contradicting reality.

Carry the override baseline as a typed (value, origin) pair end-to-end so
the secondary is tagged by its true source (BuzzExplicit for the record
case, PersonaDefault for personas) instead of a hardcoded PersonaDefault.
Build the record-model baseline only when model_overridden is true, so a
persona edited mid-life still does not false-positive. On a live pick equal
to the baseline, yield a clean single-value field rather than passing the
pre-polluted base through (build_model_field independently sets an
AcpConfigOption secondary for the record-plus-live-session case).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The live-acp-vs-record override is now exclusively apply_runtime_override's
job, gated on model_overridden. build_model_field's acp-derived secondary
predated that gate: with record_model=Some(X) and acp_model=Some(Y) it
populated overridden_value=Some(Y) unconditionally, and that row passed
straight through apply_runtime_override's !model_overridden early-return —
surfacing a live override before any switch was applied.

Collapse the secondary to express only the static record-vs-file precedence
(a Buzz-explicit model shadowing a config-file model); drop all acp_model
references from it.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The v4 screenshots captured the old origin-badge UI. The shipped surface
now renders inline provenance sentences ("Set in Buzz", "Inherited from
persona", "Live override (this session only)", etc.), a folded config panel,
and a non-struck persona baseline for runtime overrides.

Re-grounds the screenshot scenarios to the sentence-based render and adds a
multiOriginSurface fixture (one distinct origin per row) so the provenance-
sentences shot witnesses multiple distinct sentences in one frame instead of
duplicating the folded-panel capture.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…tooltips

The agent side-panel Configuration section rendered with AgentConfigPanel's
own dense header (small inline icon, muted heading) inside the ProfileFieldGroup
card, so it read as bolted on rather than part of the card. Re-skin the section
header to the panel's ProfileFieldRow convention (circular icon badge, px-4 row
rhythm, foreground label) so it reads as one more section; the divider stays.

Long config values truncate correctly but had no way to reveal the full text.
Add a native title attribute carrying the untruncated value on the truncated
NormalizedRow/AdvancedRow values, guarded so null placeholders get no tooltip —
matching the existing ProfileFieldRow title convention. Covers both the side
panel and the Agents nav menu since both embed the same AgentConfigPanel.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The truncated NormalizedRow override span (config-file system prompt
in the dual-prompt case) rendered inside the truncate container with no
title, so its full text was unreadable on hover — the same gap the
primary value tooltip already closes.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Main's d256935 added agent_command_override, persona_source_version,
and provider to ManagedAgentRecord. Two test fixture initializers on
this branch were missing them, causing E0063 compile errors in CI.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Opens the user-profile-panel for a managed agent by clicking its avatar
in the #agents channel, waits for the Configuration section to render,
and captures the full panel. Covers the isBot && isOwner path that
renders the Configuration footer inside ProfileSummaryView.

Co-authored-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
… extraction

codex.rs and claude.rs previously enumerated a fixed set of config keys
in their extra blocks, silently dropping any field not in the list as
new options were added upstream. This meant settings like
service_tier, plan_mode_reasoning_effort, and alwaysThinkingEnabled
were invisible in the config surface.

Check in JSON Schema snapshots for both runtimes and introduce
schema_walker::extract_schema_fields, which walks the schema's
top-level properties and surfaces every key the user has actually set.
Scalars stringify directly; arrays become '[N items]'; objects flatten
one level as key.subkey = value. Normalized fields (model, provider,
mode, thinking_effort, etc.) are passed via a skip list to avoid
double-counting.

Also adds schema_version (fetched_at from versions.json) to
RuntimeFileConfig and threads it through to ConfigSourceReport as
config_schema_version so the frontend can surface which schema snapshot
was used.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…d, add SHA-change warnings, consolidate VERSIONS_JSON

schema_walker.rs: expand doc comment to explain that object subkeys are
iterated from the config value (not filtered against schema properties),
making arbitrary keys like env vars visible. Also move VERSIONS_JSON
embed and schema_version() lookup here so codex.rs and claude.rs share
one embed instead of two identical constants.

claude.rs: add inline comment on provider_locked insert to make clear
it is a Buzz-synthesized annotation, not a field from the user's file.

codex.rs: remove duplicate VERSIONS_JSON constant and parse_codex_schema_version();
delegate to schema_walker::schema_version("codex").

refresh-harness-schemas.sh: read old SHAs from versions.json before
overwriting, then print a warning line for any harness whose SHA changed.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…ld extraction

The schema-driven approach (extract_schema_fields + vendored JSON Schema
snapshots) had a fundamental flaw: a drifted snapshot is functionally
equivalent to a hardcoded field list. Any new config key added upstream
would be silently invisible until someone ran refresh-harness-schemas.sh.
The committed schema files also triggered Biome linting failures on CI.

Replace with config-driven iteration: walk the user's own config object
directly, surface every key they have set, skip only the normalized fields
extracted into typed struct fields (model, provider, mode, etc.). No schema
required, no vendored JSON, no drift, no Biome issue.

extract_schema_fields(schema_json, config, skip) → extract_config_fields(config, skip)

Remove: CODEX_SCHEMA, CLAUDE_SCHEMA, VERSIONS_JSON constants; schemas/
directory (codex.config.schema.json, claude-code-settings.schema.json,
versions.json); schema_version() function; schema_version field on
RuntimeFileConfig; config_schema_version field on ConfigSourceReport;
refresh-harness-schemas.sh script.

The skip lists in codex.rs and claude.rs are the only harness-specific
coupling that remains — they map harness config keys to normalized struct
fields. That coupling is intentional and load-bearing.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… config bridge

Two bugs in the agent config panel:

1. agent_config.rs resolved runtime_meta via record.agent_command (the
   create-time snapshot, never updated). When a user changes an agent's
   harness in the UI the change goes to the persona's runtime field, not
   agent_command. Both get_agent_config_surface and write_agent_config_field
   now call effective_agent_command (the same resolution the spawn path uses)
   so the config bridge dispatches to the correct harness.

2. reader.rs built the advanced section exclusively from file_config.extra
   (tier 2b). Any env vars in record.env_vars beyond the four normalized
   fields (model, provider, thinking effort, system prompt) were invisible.
   read_config_surface now appends remaining record.env_vars to advanced as
   BuzzExplicit / RespawnWithEnvVar fields, skipping keys already covered by
   normalized fields and keys already present in file_config.extra.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…flattening

Four display correctness fixes in the config bridge:

1. types.rs: add HarnessConstraint variant to ConfigOrigin. The locked
   provider branch in reader.rs was using EnvVar which caused the UI to
   show "From environment variable" for Alia's Anthropic provider — it is
   a harness constraint, not a user-set env var.

2. reader.rs: use ConfigOrigin::HarnessConstraint in the provider_locked
   branch so Claude Code's locked Anthropic provider displays correctly.

3. goose.rs: infer provider="databricks" when DATABRICKS_HOST is present
   but no explicit GOOSE_PROVIDER or active_provider is set. This surfaces
   the effective provider for agents on the Databricks OAuth path (Paul,
   Duncan) who previously showed no provider in the panel.

4. codex.rs: default provider="openai" when model_provider is absent.
   Codex uses OpenAI implicitly when no provider is configured; Thufir
   previously showed no provider.

5. schema_walker.rs: recurse two levels deep instead of one. Objects at
   depth 2 are now flattened as "key.subkey.subsubkey" rather than "{...}".
   This correctly surfaces codex [projects."<path>"] trust_level entries
   and [tui.model_availability_nux] model keys. Depth 3+ still shows "{...}".

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…e types

Add "harnessConstraint" to the ConfigOrigin union in types.ts to match
the new Rust variant added in 786bbe5. Add a provenanceSentence case
in AgentConfigPanel.tsx so the UI shows "Locked by harness" instead of
falling through to an unhandled case.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… values

Three fixes:

1. goose.rs: remove dead nested.host branch from Databricks inference arm.
   The nested struct is Some only when active_provider is Some, but arm 2
   already returns Some(active_provider) in that case — arm 3 never executes
   when active_provider is set. The nested.host condition was always false
   when reached. Only the flat DATABRICKS_HOST check is needed.

2. schema_walker.rs: emit "{...}" for empty inner objects instead of
   silently dropping the key. The previous two-level walker iterated over
   an empty object's entries and produced zero output, making the key
   disappear entirely. Reverts the claude.rs test workaround (pre-commit: {}
   now correctly asserts hooks.pre-commit = "{...}").

3. schema_walker.rs: join scalar arrays comma-separated instead of showing
   "[N items]". Adds format_array() helper: when all elements are scalars
   (string, bool, number, null), joins them with ", "; when any element is
   a nested object or array, falls back to "[N items]". This surfaces
   tui.status_line as its actual values rather than "[6 items]".

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Mark normalized config fields as required per harness so the UI can
eventually prompt users to set the minimum viable config before an
agent will work. Required-ness is harness-specific: buzz-agent and
goose require model + provider; claude code and codex have no
user-configurable required fields.

Adds required_normalized_fields to KnownAcpRuntime, threads is_required
through the Rust reader/writer, and mirrors isRequired in the TypeScript
NormalizedField type and e2e bridge fixtures.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…e, fix ModelPicker double-fetch

Remove the entire write path (writer.rs, write_agent_config_field command,
WriteConfigTarget/WriteConfigFieldRequest/WriteConfigResult types) — no
frontend consumer exists yet. is_writable removed from NormalizedField and
ConfigField; write_via/ConfigWriteMechanism kept for provenanceSentence.

Extract resolve_with_override() in reader.rs to deduplicate the tier-picking
and override-tracking pattern shared by build_provider_field, build_mode_field,
and build_thinking_field.

Replace imperative getAgentConfigSurface call in ModelPicker with
useAgentConfigSurface hook so React Query deduplicates when panel and picker
are both rendered for the same agent.

Drop provider_locked extra insert from claude.rs — build_provider_field
already handles the locked state via runtime_meta.provider_locked.

Remove isRunning prop from AgentConfigPanel (unused) and its two call sites.

Replace 4 hardcoded pubkey constants in buildMockConfigSurface with the
ALICE_PUBKEY/BOB_PUBKEY/CHARLIE_PUBKEY/OUTSIDER_PUBKEY constants already
defined in the same file.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
High-1: restore spawn_key_refusal guard at top of build_deploy_payload.
The doc comment said 'fails closed' but there was no actual guard — a
keyring outage would serialize private_key_nsec: "" into the payload.

High-3: move session_config_captured emit to after apply_model_switch.
The emit now fires post-switch so the desktop caches the applied model,
not the pre-switch desired state. modelOverridden is false on the
unsupported arm so the panel doesn't show a stale override badge.

High-4: surface acp_model for stable agents. Stable agents report model
via configOptions (category=model, current_value) rather than the models
field. acp_model now falls back to find_config_option_value(c, "model")
so their current model is surfaced in the panel.

High-5: clear SessionConfigCache for exited agents. sync_managed_agent_processes
now returns (bool, Vec<String>) — the exited pubkeys — so callers can
clear the session cache for each agent that exited. Covers both the
process-exit loop and the stale-pid cleanup loop.

High-6: re-tag persona-snapshotted model as PersonaDefault. Persona-created
agents have record.model set at create time from the persona snapshot, so
had_model is true even though the model came from the persona. Re-tag
when record.model == persona_model, no live override, and a persona is
linked (non-persona agents with an explicit model keep BuzzExplicit).

Medium-1/2: update stale comments in build_deploy_payload. The doc comment
said env/model/provider were pinned and never read live; the inline comment
said 'same precedence as local spawn'. Both are now accurate.

Medium-3: add record.provider as fallback in build_provider_field. The
reader only checked record.env_vars for the provider env var key; records
with only the structured record.provider field (no env var) now surface
the correct provider in the panel.

Medium-8: invalidate managed agents query after updateManagedAgent in
ModelPicker. Persisted model changes were stale until the next poll.

Medium-9: invalidate config-surface query after session_config_captured.
Added a callback slot (setSessionConfigCapturedCallback) wired up by
useManagedAgentObserverBridge so React Query invalidates immediately
instead of waiting for the 30s poll.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
agent_config.rs (get_agent_config_surface) and agent_models.rs
(get_agent_models, update_managed_agent) were calling
sync_managed_agent_processes but not clearing the session cache for
agents that exited. The panel could serve stale config/model state for
a stopped agent until one of the other covered paths ran. Destructure
the returned (bool, Vec<String>) and call state.clear_session_cache for
each exited pubkey at all three sites.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Rebase onto origin/main (4d61969). Resolve conflicts in lib.rs
(add discover_agent_models alongside config-bridge commands),
check-file-sizes.mjs (take main's discovery.rs limit), and
e2eBridge.ts (keep both discover_agent_models and
get_agent_config_surface mock cases).

Bump file-size limits for agents.rs (1397→1437) and discovery.rs
(1085→1111) to cover config-bridge command additions. Apply
rustfmt fixes.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Re-introduce resolve_effective_prompt_model_provider (runtime.rs) and
resolve_persona_env (env_vars.rs) deleted from main in 826d735
("Revamp new agent dialog #1201"). agent_config.rs and the provider
deploy path in agents.rs still call these; they were added as part of the
config-bridge feature and belong on this branch.

Restore AgentInfoFocusedView, DiagnosticsFocusedView, and
AgentInstructionsFocusedView exports in UserProfilePanelSections.tsx,
and add missing optional props (canAddToChannel, isActionPending,
onAddToChannel, etc.) that UserProfilePanel.tsx still imports/passes.
Our branch only touched config-bridge sections of this file, not these
unrelated exports — the rebase merged away the existing code.

Bump file-size limits in check-file-sizes.mjs for runtime.rs (+26 lines
for the re-added helper) and UserProfilePanelSections.tsx (+136 lines for
the restored focused-view components).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Our branch's flat layout diverged from main's tab-based panel structure
(Info | Runtime | Channels | Memories) introduced in the agent dialog
revamp. Restore main's ProfileSummaryView with all props, tab bar,
agent primary actions, persona instantiation, and focused views.

Integrate AgentConfigPanel into the Runtime tab content — shown below
the runtime configuration fields when the viewer owns the agent.

Removes branch-specific flat layout helpers (buildPublicFields,
buildOwnerFields, runtimeLabel, copyToClipboard, ProfileFieldGroup,
ProfileFieldRow, ProfileIngressRow) that duplicated functionality
now provided by UserProfilePanelFields and UserProfilePanelTabs.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Screenshots now open the agent profile from a channel message and
navigate to the Runtime tab, matching the current tab-based layout.
Added writeVia fields to mock config surfaces to prevent TypeError
when provenanceSentence accesses writeVia.type for envVar origins.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The method was superseded by requeue_cancelled_batch() during the
rebase onto main (which brought in the steering infrastructure).
Only its own unit tests called it; production code uses the
free-function helper instead.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…w pattern

Each normalized field now renders with a circular icon badge matching
the existing Runtime tab fields (Model→Cpu, Provider→Server,
Mode→Activity, Thinking→Brain, MaxTokens→Hash, Context→Layers,
SystemPrompt→MessageSquare). Removes the redundant "Configuration"
section header — the card boundary + per-field icons make the section
self-evident.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the wpfleger/phase4-config-bridge branch from 420d806 to 9540b94 Compare June 29, 2026 20:48
@wpfleger96 wpfleger96 merged commit c65989a into main Jun 29, 2026
22 checks passed
@wpfleger96 wpfleger96 deleted the wpfleger/phase4-config-bridge branch June 29, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants