Skip to content

Wire agent runner into product: nudge endpoint, activity feed, agent catalog#21

Merged
inhaq merged 3 commits into
mainfrom
pi-agent-ui-integration
Jun 15, 2026
Merged

Wire agent runner into product: nudge endpoint, activity feed, agent catalog#21
inhaq merged 3 commits into
mainfrom
pi-agent-ui-integration

Conversation

@inhaq

@inhaq inhaq commented Jun 15, 2026

Copy link
Copy Markdown
Owner

This pull request was created by @kiro-agent on behalf of @inhaq 👻

Comment with /kiro fix to address specific feedback or /kiro all to address everything.
Learn about Kiro Web


The three optional follow-ons from the deep-dive, making the Tier 2/3 agent-runner capabilities reachable end-to-end. Stacked on #20 (base = pi-agent-tier3); retarget down the stack as the parents merge.

1. Steering over HTTP (server /nudge + threading)

  • Threads onSteer through createRolesRunnerActor/RunnerCritic, which pass it as req.steering on every runner call, so a runner's live steer(text) handle bubbles up. session forwards it via the existing options spread.
  • The server keeps a per-session latest steer handle and adds POST /api/runs/:id/nudge ({text}) to inject guidance into the in-flight agent: 202 on success, 400 (no text), 409 (nothing steerable — e.g. a CLI/HTTP backend), behind the same bearer-token guard as the rest of /api. The handle is cleared on settle.

2. Desktop live tool feed (runner_activity)

  • The run view renders runner_activity events (which arrive over the existing store-event SSE channel) as a concise per-tool feed in the engine log — you can watch the actor read/edit files in real time.

3. Desktop native-agent catalog (kind: "agent")

  • settings.ts gains agent providers (Anthropic / OpenAI / Google) that emit { kind: "agent", options: { provider, apiKeyEnv } } runner profiles with valid pi-ai model ids; they edit files directly.
  • main.ts note/label logic updated so agent providers show their API-key affordance and the "edits files directly" note correctly (previously only http/cli were handled).

4. Desktop nudge control

  • api.nudgeRun + a Nudge input/button in the run view (Enter to send) post to the new endpoint, surfacing 409 when the active backend isn't steerable.

Testing

  • Server: nudge inject (202 + steer invoked), 400 no-text, 409 non-steerable/unknown, 401 no-token.
  • Role: RunnerActor registers the runner's live steer handle via onSteer.
  • Engine suite 201 tests green; engine tsc --noEmit clean; desktop tsc --noEmit clean.

Notes

  • Latest-wins steer handle: a nudge targets whichever runner call is currently active (fine for the actor-critic loop; with parallel tasks it targets the most recent).
  • Agent model ids are validated against pi-ai's bundled catalog; an unknown id surfaces as a runner error rather than a crash.
  • Activity events ride the existing SSE event type (data.type = "runner_activity"), so no server transport change was needed.

Summary by CodeRabbit

  • New Features

    • Added nudge control to in-flight runs for interactive text input during execution.
    • Added native agent runner support with Anthropic, OpenAI, and Google presets.
    • Enhanced engine logs to display tool activity with start and end events.
  • Improvements

    • Improved API key validation logic for different runner configurations.

…ent catalog

Make the Tier 2/3 agent-runner capabilities reachable end-to-end.

1) Steering over HTTP. Thread onSteer through createRoles -> RunnerActor/
   RunnerCritic, which pass it as req.steering on every runner call so a
   runner's live steer handle bubbles up. session forwards it (via the
   existing RunGoalOptions/CreateRolesOptions spread); the server keeps a
   per-session latest steer handle and adds POST /api/runs/:id/nudge to
   inject guidance into the in-flight agent (202), with 400 (no text) /
   409 (nothing steerable) and the same token guard as the rest of /api.
   The handle is cleared on settle.

2) Desktop live tool feed. The run view renders runner_activity events
   (which arrive via the existing store-event SSE channel) as a concise
   per-tool feed in the engine log, so you can watch the actor read/edit
   files in real time.

3) Desktop native-agent catalog. settings.ts gains kind "agent"
   providers (Anthropic/OpenAI/Google) that emit { kind:"agent",
   options:{ provider, apiKeyEnv } } runner profiles with pi-ai model
   ids; they edit files directly. main.ts note/label logic updated so
   agent providers show their API-key affordance and "edits files"
   note correctly.

4) Desktop nudge control. api.nudgeRun + a Nudge input/button in the run
   view post to the new endpoint (Enter to send), surfacing 409 when the
   active backend isn't steerable.

Tests: server nudge (inject / 400 / 409 / 401) and the role-level steer
registration. Engine suite 201 green; engine + desktop typecheck clean.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 427091f8-8417-4073-a88f-7c5cad800082

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd34b2 and 58edb2b.

📒 Files selected for processing (3)
  • desktop/src/main.ts
  • desktop/src/settings.ts
  • src/server/server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/server.ts

📝 Walkthrough

Walkthrough

Adds live "nudge" steering for in-flight runs: a new POST /api/runs/:id/nudge server endpoint accepts text, looks up a per-session steer callback stored in a steerers map, and forwards the text to the active runner. onSteer callbacks are threaded through RunnerActorOptions, RunnerCriticOptions, and CreateRolesOptions. The desktop UI gains a nudge input row and sendNudge() logic. Separately, a new "agent" PresetKind is introduced with three native agent presets (Anthropic, OpenAI, Google) and updated profileFor() profile generation.

Changes

Live Steering / Nudge Feature

Layer / File(s) Summary
Steering callback contracts
src/adapters/runnerRoles.ts, src/adapters/roleBindings.ts
Adds optional onSteer callback to RunnerActorOptions, RunnerCriticOptions, and CreateRolesOptions; wires it into RunnerActor and RunnerCritic constructor calls in roleBindings.
RunnerActor and RunnerCritic steerRegister implementation
src/adapters/runnerRoles.ts
RunnerActor and RunnerCritic store steerRegister from opts.onSteer and pass a steering option into every runner.run call, including initial, retry, selfReview, and review paths.
Server steerers map and POST /api/runs/:id/nudge endpoint
src/server/server.ts
Adds a steerers map keyed by sessionId, wires onSteer into runGoalImpl, implements the nudge route with 202/400/409 responses, and removes the steer callback on run settle().
Desktop nudgeRun API client and monitor UI
desktop/src/api.ts, desktop/src/main.ts
Adds nudgeRun API client function; adds a nudge input row and sendNudge() to the run monitor UI with concurrent-nudge guard and engine log entries; extends message handler to log runner_activity tool start/end events.
Steering tests
test/runnerRoles.test.ts, test/server.test.ts
New runnerRoles test verifies RunnerActor exposes the steer handle via onSteer; new server suite validates nudge injection, input validation, steerability/session checks, and auth enforcement.

Agent Provider Kind

Layer / File(s) Summary
Agent PresetKind, RunnerPreset, PRESETS catalog, and profileFor
desktop/src/settings.ts
Adds "agent" to PresetKind, extends RunnerPreset with optional agentProvider, inserts three native agent presets (Anthropic, OpenAI, Google) with editsFiles: true, and adds the agent profile-building branch in profileFor().
Desktop UI adjustments for agent presets
desktop/src/main.ts
Broadens keyMissing() to use apiKeyEnv for non-CLI presets, updates the "Add env" action env-var selection, and revises the writer-role warning text to recommend CLI/agent writers.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Desktop UI)
    participant nudgeRun as nudgeRun() API Client
    participant Server as POST /api/runs/:id/nudge
    participant steerers as steerers map
    participant RunnerActor as RunnerActor (runner.run)

    User->>nudgeRun: sendNudge(sessionId, text)
    nudgeRun->>Server: POST /api/runs/{sessionId}/nudge { text }
    Server->>Server: validate text non-empty
    Server->>steerers: lookup steer fn for sessionId
    alt no steer registered
        Server-->>nudgeRun: 409 Conflict
        nudgeRun-->>User: show error in plan area
    else steer registered
        Server->>RunnerActor: steer(text)
        Server-->>nudgeRun: 202 Accepted
        nudgeRun-->>User: clear input, log "you → nudge"
    end
    Note over Server,steerers: on run settle(), steerers.delete(sessionId)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • inhaq/loopwright#18: Introduces req.steering and agent.steer — the direct predecessor to this PR's onSteer wiring and /api/runs/:id/nudge endpoint.
  • inhaq/loopwright#20: Implements the in-flight steering/nudge callback and runner_activity events that this PR exposes via the server endpoint and desktop UI.
  • inhaq/loopwright#4: Touches the same createRoles/RunnerActor/RunnerCritic role-binding layer that this PR extends with onSteer threading.

Poem

🐇 Hop hop, a nudge in flight,
A whisper sent to steer just right.
The runner hears the text I've typed,
The agent kind — three providers, hyped!
With steer callbacks and maps so clean,
The loopwright loops most bright and keen! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main product-level features: agent runner integration (nudge endpoint, activity feed, agent catalog). It is concise, specific, and reflects the primary objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pi-agent-ui-integration

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/src/main.ts`:
- Around line 707-711: The nudgeInput element in the h("input", ...) creation
lacks an explicit accessible name for screen readers. Add an aria-label
attribute to the input element with a descriptive name like "Nudge the agent" to
provide a stable accessible name that screen readers can reliably announce,
independent of the placeholder text.
- Around line 735-752: The sendNudge function can be invoked multiple times
concurrently from rapid Enter key presses or button clicks while a request is
still in flight, causing duplicate nudges. Add a flag (e.g., isNudgeSending or
isSending) to track whether sendNudge is currently executing. At the start of
the sendNudge function, check if this flag is true and return early if it is.
Set the flag to true when entering sendNudge and set it back to false in the
finally block to ensure it is reset after the request completes. This prevents
re-entrancy and ensures only one nudge request can be in flight at a time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a2890db8-488c-4aee-98a5-ce994240c09a

📥 Commits

Reviewing files that changed from the base of the PR and between 27703bb and 1cd34b2.

📒 Files selected for processing (8)
  • desktop/src/api.ts
  • desktop/src/main.ts
  • desktop/src/settings.ts
  • src/adapters/roleBindings.ts
  • src/adapters/runnerRoles.ts
  • src/server/server.ts
  • test/runnerRoles.test.ts
  • test/server.test.ts

Comment thread desktop/src/main.ts
Comment thread desktop/src/main.ts
Base automatically changed from pi-agent-tier3 to pi-native-agent-runner June 15, 2026 08:19
Base automatically changed from pi-native-agent-runner to main June 15, 2026 08:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
desktop/src/main.ts (1)

355-373: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

CLI availability falls back to “missing” when command detection is unknown.

If detectCommands() fails, installed stays {}; then commandMissing() returns true for every CLI preset. That contradicts the intended “unknown — don’t flag missing” behavior and produces false warning states.

Suggested fix
   let storedKeys = new Set<string>();
   let knowKeys = false;
   let installed: Record<string, boolean> = {};
+  let knowInstalled = false;
   if (isTauri()) {
@@
     try {
       installed = await detectCommands(["codex", "kiro-cli", "gh"]);
+      knowInstalled = true;
     } catch {
       /* unknown — don't flag CLI presets as missing */
     }
   }

   /** Whether a preset's command is known-missing (desktop only). */
   function commandMissing(p: RunnerPreset): boolean {
-    return p.kind === "cli" && isTauri() && installed[p.command ?? ""] !== true;
+    return p.kind === "cli" && isTauri() && knowInstalled && installed[p.command ?? ""] !== true;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/src/main.ts` around lines 355 - 373, When detectCommands() fails, the
installed object remains as an empty object {}, causing commandMissing() to
incorrectly return true for all CLI presets because installed[p.command ?? ""]
evaluates to undefined !== true. To fix this, introduce a boolean flag (such as
commandsDetected) that tracks whether detectCommands() completed successfully,
setting it to true only when the try block succeeds. Then modify the
commandMissing() function to return true only when both commandsDetected is true
AND installed[p.command ?? ""] !== true, ensuring that CLI presets are not
flagged as missing when command detection is unknown.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/src/main.ts`:
- Around line 1421-1423: The env resolution logic in the PRESETS loop only
handles "http-responses" kind presets via apiKeyEnv, while "agent" presets
incorrectly fall through to requiresEnv (which is undefined for agents), causing
them to be skipped by the continue statement. Update the conditional expression
that determines which environment variable property to check (currently checking
preset.kind === "http-responses" ? preset.apiKeyEnv : preset.requiresEnv) to
include proper handling for "agent" presets by adding them to the condition with
their appropriate environment variable property.

In `@desktop/src/settings.ts`:
- Around line 268-271: The legacy migration logic in the settings.ts file is
performing an identity lookup instead of translating provider ids to preset ids.
In the ternary expression that sets presetId, replace the direct use of
v.provider with a proper provider-to-preset translation. The current code uses
v.provider directly as the presetId, but legacy { provider, model } payloads
contain old provider names that won't match the new preset id format. Implement
a mapping or translation function to convert the legacy provider value to the
corresponding new preset id before passing it to getPreset().

---

Outside diff comments:
In `@desktop/src/main.ts`:
- Around line 355-373: When detectCommands() fails, the installed object remains
as an empty object {}, causing commandMissing() to incorrectly return true for
all CLI presets because installed[p.command ?? ""] evaluates to undefined !==
true. To fix this, introduce a boolean flag (such as commandsDetected) that
tracks whether detectCommands() completed successfully, setting it to true only
when the try block succeeds. Then modify the commandMissing() function to return
true only when both commandsDetected is true AND installed[p.command ?? ""] !==
true, ensuring that CLI presets are not flagged as missing when command
detection is unknown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 427091f8-8417-4073-a88f-7c5cad800082

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd34b2 and 58edb2b.

📒 Files selected for processing (3)
  • desktop/src/main.ts
  • desktop/src/settings.ts
  • src/server/server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/server.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
desktop/src/main.ts (1)

355-373: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

CLI availability falls back to “missing” when command detection is unknown.

If detectCommands() fails, installed stays {}; then commandMissing() returns true for every CLI preset. That contradicts the intended “unknown — don’t flag missing” behavior and produces false warning states.

Suggested fix
   let storedKeys = new Set<string>();
   let knowKeys = false;
   let installed: Record<string, boolean> = {};
+  let knowInstalled = false;
   if (isTauri()) {
@@
     try {
       installed = await detectCommands(["codex", "kiro-cli", "gh"]);
+      knowInstalled = true;
     } catch {
       /* unknown — don't flag CLI presets as missing */
     }
   }

   /** Whether a preset's command is known-missing (desktop only). */
   function commandMissing(p: RunnerPreset): boolean {
-    return p.kind === "cli" && isTauri() && installed[p.command ?? ""] !== true;
+    return p.kind === "cli" && isTauri() && knowInstalled && installed[p.command ?? ""] !== true;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/src/main.ts` around lines 355 - 373, When detectCommands() fails, the
installed object remains as an empty object {}, causing commandMissing() to
incorrectly return true for all CLI presets because installed[p.command ?? ""]
evaluates to undefined !== true. To fix this, introduce a boolean flag (such as
commandsDetected) that tracks whether detectCommands() completed successfully,
setting it to true only when the try block succeeds. Then modify the
commandMissing() function to return true only when both commandsDetected is true
AND installed[p.command ?? ""] !== true, ensuring that CLI presets are not
flagged as missing when command detection is unknown.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/src/main.ts`:
- Around line 1421-1423: The env resolution logic in the PRESETS loop only
handles "http-responses" kind presets via apiKeyEnv, while "agent" presets
incorrectly fall through to requiresEnv (which is undefined for agents), causing
them to be skipped by the continue statement. Update the conditional expression
that determines which environment variable property to check (currently checking
preset.kind === "http-responses" ? preset.apiKeyEnv : preset.requiresEnv) to
include proper handling for "agent" presets by adding them to the condition with
their appropriate environment variable property.

In `@desktop/src/settings.ts`:
- Around line 268-271: The legacy migration logic in the settings.ts file is
performing an identity lookup instead of translating provider ids to preset ids.
In the ternary expression that sets presetId, replace the direct use of
v.provider with a proper provider-to-preset translation. The current code uses
v.provider directly as the presetId, but legacy { provider, model } payloads
contain old provider names that won't match the new preset id format. Implement
a mapping or translation function to convert the legacy provider value to the
corresponding new preset id before passing it to getPreset().

---

Outside diff comments:
In `@desktop/src/main.ts`:
- Around line 355-373: When detectCommands() fails, the installed object remains
as an empty object {}, causing commandMissing() to incorrectly return true for
all CLI presets because installed[p.command ?? ""] evaluates to undefined !==
true. To fix this, introduce a boolean flag (such as commandsDetected) that
tracks whether detectCommands() completed successfully, setting it to true only
when the try block succeeds. Then modify the commandMissing() function to return
true only when both commandsDetected is true AND installed[p.command ?? ""] !==
true, ensuring that CLI presets are not flagged as missing when command
detection is unknown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 427091f8-8417-4073-a88f-7c5cad800082

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd34b2 and 58edb2b.

📒 Files selected for processing (3)
  • desktop/src/main.ts
  • desktop/src/settings.ts
  • src/server/server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/server.ts
🛑 Comments failed to post (2)
desktop/src/main.ts (1)

1421-1423: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Agent presets are skipped in “Presets by environment key”.

At Line 1422, env resolution handles only http-responses via apiKeyEnv; "agent" presets fall through to requiresEnv (undefined) and are excluded from the catalog.

Suggested fix
-      const env = preset.kind === "http-responses" ? preset.apiKeyEnv : preset.requiresEnv;
+      const env = preset.kind === "cli" ? preset.requiresEnv : preset.apiKeyEnv;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/src/main.ts` around lines 1421 - 1423, The env resolution logic in
the PRESETS loop only handles "http-responses" kind presets via apiKeyEnv, while
"agent" presets incorrectly fall through to requiresEnv (which is undefined for
agents), causing them to be skipped by the continue statement. Update the
conditional expression that determines which environment variable property to
check (currently checking preset.kind === "http-responses" ? preset.apiKeyEnv :
preset.requiresEnv) to include proper handling for "agent" presets by adding
them to the condition with their appropriate environment variable property.
desktop/src/settings.ts (1)

268-271: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Legacy migration is doing identity lookup instead of provider→preset translation.

At Line 269, v.provider is used directly as presetId. For legacy { provider, model } payloads from the old provider/model shape, provider ids won’t reliably match new preset ids, so migrated writer/reviewer choices can silently fall back to defaults.

Suggested fix
 function coerceChoice(value: unknown, fallback: ModelChoice): ModelChoice {
   if (value === null || typeof value !== "object") return { ...fallback };
   const v = value as Record<string, unknown>;
-  // Tolerate the legacy { provider, model } shape by mapping provider->preset.
-  const presetId = typeof v.preset === "string" ? v.preset : typeof v.provider === "string" ? v.provider : "";
+  // Tolerate the legacy { provider, model } shape by mapping provider->preset.
+  const legacyProvider = typeof v.provider === "string" ? v.provider : "";
+  const providerToPreset: Record<string, string> = {
+    // Keep this in sync with actual v1 provider ids.
+    openai: "openai-responses",
+    kiro: "kiro-cli",
+    codex: "codex-cli",
+    anthropic: "anthropic-agent",
+    google: "google-agent",
+  };
+  const presetId =
+    typeof v.preset === "string"
+      ? v.preset
+      : providerToPreset[legacyProvider] ?? legacyProvider;
   const preset = getPreset(presetId);
   if (!preset) return { ...fallback };
   const model = typeof v.model === "string" ? v.model : "";
   return { preset: preset.id, model };
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

function coerceChoice(value: unknown, fallback: ModelChoice): ModelChoice {
  if (value === null || typeof value !== "object") return { ...fallback };
  const v = value as Record<string, unknown>;
  // Tolerate the legacy { provider, model } shape by mapping provider->preset.
  const legacyProvider = typeof v.provider === "string" ? v.provider : "";
  const providerToPreset: Record<string, string> = {
    // Keep this in sync with actual v1 provider ids.
    openai: "openai-responses",
    kiro: "kiro-cli",
    codex: "codex-cli",
    anthropic: "anthropic-agent",
    google: "google-agent",
  };
  const presetId =
    typeof v.preset === "string"
      ? v.preset
      : providerToPreset[legacyProvider] ?? legacyProvider;
  const preset = getPreset(presetId);
  if (!preset) return { ...fallback };
  const model = typeof v.model === "string" ? v.model : "";
  return { preset: preset.id, model };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/src/settings.ts` around lines 268 - 271, The legacy migration logic
in the settings.ts file is performing an identity lookup instead of translating
provider ids to preset ids. In the ternary expression that sets presetId,
replace the direct use of v.provider with a proper provider-to-preset
translation. The current code uses v.provider directly as the presetId, but
legacy { provider, model } payloads contain old provider names that won't match
the new preset id format. Implement a mapping or translation function to convert
the legacy provider value to the corresponding new preset id before passing it
to getPreset().

@inhaq inhaq merged commit bdc238d into main Jun 15, 2026
8 checks passed
@inhaq inhaq deleted the pi-agent-ui-integration branch June 15, 2026 14:11
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.

1 participant