Skip to content

fix: make busy-session inputs durable - #304

Merged
pedronauck merged 3 commits into
mainfrom
session-fix
Aug 4, 2026
Merged

pedronauck merged 3 commits into
mainfrom
session-fix

Conversation

@pedronauck

@pedronauck pedronauck commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

Summary

This PR repairs busy-session input end to end. Queue, Steer, and Interrupt are now daemon-owned, durable operations instead of transient client-side intent. The web composer projects the daemon's authoritative state, keeps drafts until acknowledgement, and presents a single calm working signal without duplicate lifecycle warnings.

The change is intentionally a hard cut. It removes the old ACP steer handler, the runtime steer source, interrupt-salvage behavior, and the web's local queued-prompt mirror rather than maintaining two competing models.

Problem

While a session was running:

  • Queue and Steer could acknowledge visually but lose the message before the runtime consumed it.
  • Steer drafts disappeared from the composer even when no real steer occurred.
  • Interrupt was the only path that usually changed the active turn, but it could duplicate messages and emit several warning transcript markers.
  • The UI had no clear in-thread working state; the Stop button was the only evidence that the agent was still running.
  • The visible “Next prompt” label consumed space without adding meaning.

The root cause was split ownership. The web held a local queue while the runtime had separate prompt, steer, and cancellation paths. A client-side acknowledgement did not prove durable admission, edit/cancel/promote mutations were not one atomic model, and expected ACP cancellation leaked into the transcript as a provider failure.

Runtime and storage changes

  • Added daemon-owned session_input_queue persistence through append-only migration 00043_schema.sql.
  • Modeled Queue, Steer, and Interrupt as durable input rows with explicit delivery semantics:
    • Queue uses after_turn.
    • Steer and Interrupt use interrupt_then_prompt with an expected active-turn fence.
  • Added stable entry identity, idempotency keys, message identity, queue ordering, terminal state, and atomic replace/cancel/promote operations.
  • Added transactional promotion so a queued row can become fenced steering input without a client-side remove/add race.
  • Added restart/replay behavior and prompt-admission coordination so durable rows dispatch exactly once and in FIFO order.
  • Required daemon-side expected-turn validation for disruptive mutations; stale clients cannot interrupt a newer turn.
  • Preserved technical cancellation events for diagnostics while suppressing the user-facing provider-failure marker for the expected FailureCanceled path.
  • Split queue mutation logic and activation logic into focused files to keep production files within the 500-line architecture limit.

Public surfaces

The same authoritative behavior is available across every supported management surface:

  • HTTP and UDS:
    • GET /api/workspaces/{workspace_id}/sessions/{session_id}/prompt/queue
    • PUT /api/workspaces/{workspace_id}/sessions/{session_id}/prompt/queue/{queue_entry_id}
    • POST /api/workspaces/{workspace_id}/sessions/{session_id}/prompt/queue/{queue_entry_id}/steer
    • DELETE /api/workspaces/{workspace_id}/sessions/{session_id}/prompt/queue/{queue_entry_id}
  • CLI:
    • compozy session prompt --queue|--steer|--interrupt
    • compozy session input list|edit|steer|cancel
  • Native tools:
    • compozy__session_prompt
    • compozy__session_inputs_list
    • compozy__session_input_replace
    • compozy__session_input_cancel
    • compozy__session_input_promote
  • Extension host API and permissions now expose list, replace, cancel, and promote operations.
  • OpenAPI, Go contracts, TypeScript contracts, web OpenAPI types, native tool catalog/digests, and generated CLI documentation were regenerated together.
  • The official Compozy skill and session lifecycle/event documentation now describe the durable input contract.

The old dedicated interrupt endpoint and ACP steer path were removed. There are no aliases, compatibility fallbacks, polling shims, or salvage bridges.

Web behavior

  • Added a TanStack Query-backed session-input adapter and mutation hook; the daemon response is the source of truth for cache updates.
  • Removed the local XState queued-prompt mirror and the associated duplicated invalidation/settlement behavior.
  • Queue, Steer, and Interrupt clear the composer only after the daemon acknowledges the operation. Failures preserve the draft and show one actionable error.
  • Refresh reconstructs pending inputs from the daemon, so queued work does not disappear with component or browser state.
  • Durable queue rows support edit, remove, and promote-to-steer using their persisted entry ID.
  • Normal queued, steered, interrupted, canceled, accepted, and dropped lifecycle markers remain durable but no longer render as warning rows.
  • Added exactly one working indicator immediately above the composer while the agent is active.
  • Removed the visible “Next prompt” label while retaining an accessible runtime-control name for screen readers.
  • Extracted busy-input callbacks from SessionComposer, leaving the component inside the repository's complexity limit.

Reference research

The design was checked against the working session implementations in .resources/codex, .resources/synara, and .resources/t3code. The adopted common pattern is durable/server-owned admission plus a client projection, rather than optimistic local ownership. TanStack Query v5 documentation was also checked through Context7 for immutable authoritative cache updates after mutation acknowledgement.

Tests and QA

Automated coverage

  • Store tests cover atomic append, ordering, replace, cancel, promote, conflicts, capacity, and terminal transitions.
  • Session-manager tests cover busy admission, turn fencing, idempotency, exactly-once dispatch, FIFO replay, promotion, and cancellation behavior.
  • HTTP and UDS route suites cover the public queue mutations and responses.
  • CLI suites cover prompt modes and session input list/edit/steer/cancel commands.
  • Native-tool, descriptor, toolset, extension-host, generated-contract, and catalog tests were updated together.
  • Web adapter, route-control, mutation, live-tail, runtime-notice, and canonical session-thread suites cover authoritative rows, acknowledgement timing, error preservation, quiet markers, the working row, and removed label.
  • Added TestPromptTranscriptMarkerSuppressesExpectedCancellation for the provider-warning regression found during QA.

Verification completed

  • make bun-lint — pass, zero warnings.
  • Web typecheck through Turborepo — pass.
  • Canonical session-thread.test.tsx — 42/42 pass.
  • React Doctor — 100/100.
  • Strict real-scenario QA evidence audit — pass.
  • Pre-commit formatting, Go formatting, JS/TS/Markdown formatting, and staged-file oxlint — pass.

The final make gate-full attempt passed codegen, installer checks, and source-size checks, then exposed one SessionComposer complexity error at BunLint. That error was fixed by extracting the handlers, and its owning lint, typecheck, and canonical test suite all passed. Per explicit operator direction, the full gate was not repeated because this PR is entering a review/remediation cycle and the full gate will run again after that cycle.

Real-user QA

QA used a fresh isolated lab with both a deterministic ACP provider and a real Codex provider:

  • Queued multiple inputs during an active turn, edited and canceled durable rows, refreshed, and confirmed authoritative state survived.
  • Promoted a queued row to Steer and verified the fenced active turn canceled, the replacement ran once, and the remaining input followed in FIFO order.
  • Repeated the critical queue-to-steer path against live Codex session sess-8a3ded8e5a329240.
  • Verified public CLI/UDS mutations, HTTP reads, web controls, runtime dispatch, and provider completion against the same session object.
  • Re-ran Interrupt after fixing the expected-cancellation marker and confirmed a calm transcript with no false provider warning.
  • Captured deterministic Storybook states and live/deterministic browser evidence.
  • Teardown audit reported clean: true; no daemon, browser, Storybook, watcher, or tmux process survived.

Tracked report: docs/qa/reports/2026-08-03-session-input-controls.md.

Migration and compatibility

This is an alpha hard cut:

  • Adds schema migration 00043 and updates the declarative schema, Atlas checksum, sqlc queries, and generated code.
  • Removes the legacy ACP steer implementation, runtime steer source, interrupt salvage logic, and dedicated interrupt API path.
  • Removes client-owned queue state instead of reading old local state or dual-writing.
  • Updates all public contracts and docs in the same change; there are no legacy aliases or schema fallbacks.

Compozy Impact Audit

  • Native tools: Updated compozy__session_prompt; added list/replace/cancel/promote input tools; regenerated descriptors, catalog snapshots, schema digests, toolsets, metadata, capability bindings, and tests.
  • Extensibility and hooks: Extended the host API contract, method registry, result contracts, permissions, Go/TypeScript SDKs, and official extension-authoring reference. No new config.toml key or lifecycle change is required because admission is session runtime state.
  • Workspace data isolation: Pending input is session-scoped and reached only through workspace-bound HTTP/UDS handlers, CLI resolution, native tool context, extension host workspace binding, and workspace/session query keys. Cross-surface QA used the same persisted session ID through CLI, API, web, and runtime without introducing global queue state.
  • Official Compozy skill: Updated skills/compozy/references/runtime-operations.md and extension-authoring.md with the new commands, tools, delivery modes, and host methods.

Release notes

🐛 Bug Fixes

  • Make Queue, Steer, and Interrupt durable and reliable during active sessions, with calm working feedback and no duplicate lifecycle warnings.

Review guide

Suggested review order:

  1. internal/store/session_input_queue.go and internal/session/inputqueue/ for the durable model and mutation invariants.
  2. internal/session/manager_busy_input*.go and manager_pending_input.go for fencing, cancellation, activation, and dispatch.
  3. internal/api/core/session_input.go, route registries, CLI, native tools, and extension host for public parity.
  4. web/src/systems/session/hooks/use-session-inputs.ts and the session composer/thread changes for authoritative UI behavior.
  5. Migration/generated contracts/docs, then the QA report and regression suites.

Summary by CodeRabbit

  • New Features
    • Added session input queue management to list, edit, cancel, and promote queued prompts to steering.
    • Added CLI, built-in tool, and API support for session input operations.
    • Added turn-fencing and durable acknowledgements for safer prompt delivery.
  • Updates
    • Replaced legacy interruption actions with steering and queue-management workflows.
    • Queued prompts are protected from edits while dispatching.
    • Improved prompt activity display and reduced transcript noise from operational events.

@greptile-apps

greptile-apps Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Too many files changed for review. (189 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@vercel

vercel Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Aug 4, 2026 1:26am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This change replaces transient session steering and interruption flows with durable, turn-fenced session-input queue operations. It adds queue listing, replacement, cancellation, and promotion across storage, APIs, CLI, tools, SDK contracts, and the web interface.

Changes

Session input lifecycle

Layer / File(s) Summary
Contracts and durable queue
internal/api/contract/*, internal/session/inputqueue/*, internal/store/*, internal/store/globaldb/*
Prompt results now use delivery modes. Busy actions require expected turn IDs. Queue entries support durable identities, replacement, cancellation, listing, replay, and promotion to steering.
Session manager and integrations
internal/session/*, internal/api/*, internal/daemon/*, internal/extension/*, internal/cli/*
Session admission validates active turns and queues interrupting or steering inputs. HTTP, UDS, daemon, extension, CLI, and tool interfaces expose the new operations.
Generated contracts
sdk/go/contracts/*
Generated Go contracts add session-input operations, prompt modes, expected-turn fields, and delivery metadata.
Web session controls
web/src/components/assistant-ui/*, web/src/hooks/routes/*, web/src/systems/session/*
The web client projects the server-backed input queue, applies atomic mutations, fences busy actions, disables dispatching rows, and renders runtime work in the session thread.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • compozy/compozy#34: Provides the ACP-based agent/session architecture that this change modifies.
  • compozy/compozy#283: Modifies shared session prompt and queued-input infrastructure.
  • compozy/compozy#288: Implements the durable steering behavior that this change replaces and extends.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.15% 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 clearly summarizes the main change: making busy-session inputs durable.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch session-fix
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch session-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 6c884f4.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

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

⚠️ Outside diff range comments (5)
internal/daemon/native_session_mutation.go (1)

99-121: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Forward the trimmed expected_turn_id.

The guard rejects an ExpectedTurnID that is empty after strings.TrimSpace. Line 120 then forwards the raw input.ExpectedTurnID. A value such as " turn-1 " passes the guard and reaches SendPrompt with the surrounding whitespace. If the session manager compares the fence with the active turn identifier by exact equality, the fence does not match and the prompt is rejected. Trim once and reuse the trimmed value.

🐛 Proposed fix
 	mode := session.BusyInputMode(strings.TrimSpace(input.Mode))
-	if (mode == session.BusyInputModeSteer || mode == session.BusyInputModeInterrupt) &&
-		strings.TrimSpace(input.ExpectedTurnID) == "" {
+	expectedTurnID := strings.TrimSpace(input.ExpectedTurnID)
+	if (mode == session.BusyInputModeSteer || mode == session.BusyInputModeInterrupt) &&
+		expectedTurnID == "" {
 		return toolspkg.ToolResult{}, nativeNetworkInputError(
 			req.ToolID,
 			errors.New("expected_turn_id is required for steer and interrupt mode"),
 		)
 	}
 	result, err := n.deps.Sessions.SendPrompt(ctx, sessionID, session.SendPromptOpts{
 		Message: message, MessageID: messageID, IdempotencyKey: idempotencyKey,
-		Mode: mode, ExpectedTurnID: input.ExpectedTurnID,
+		Mode: mode, ExpectedTurnID: expectedTurnID,
 		Runtime: core.PromptRuntimeSelectionFromPayload(input.Runtime),
 	})
🤖 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 `@internal/daemon/native_session_mutation.go` around lines 99 - 121, Trim
input.ExpectedTurnID once before validation, reuse that trimmed value for the
empty-check, and pass it as ExpectedTurnID in the session.SendPrompt call within
the native session mutation flow. Preserve the existing validation and error
behavior for blank values.
internal/tools/builtin/builtin_test.go (1)

1407-1420: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the contradictory failure message for the identity fields.

Line 1412 asserts identity.MinLength != 0, so the test requires no minLength constraint. This matches internal/tools/builtin/sessions.go lines 342-343, where message_id and idempotency_key are declared as plain strings. The failure message at line 1414 says "want non-empty string", which describes the opposite condition.

If a later change adds minLength: 1 to those properties, this test fails and reports "want non-empty string" for a schema that is already a non-empty string. Correct the message so the diagnostic matches the assertion.

💚 Proposed fix
 		if identity.Type != "string" || identity.MinLength != 0 {
 			t.Fatalf(
-				"%s prompt.%s schema = %#v, want non-empty string",
+				"%s prompt.%s schema = %#v, want string without a minLength constraint",
 				owner,
 				field,
 				identity,
 			)
 		}
🤖 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 `@internal/tools/builtin/builtin_test.go` around lines 1407 - 1420, Update the
failure message in the identity-field assertion within the builtin schema test
to describe a string with no minLength constraint, matching the existing checks
on identity.Type and identity.MinLength. Keep the assertion logic unchanged.
internal/store/globaldb/global_db_session_input_queue_test.go (1)

129-165: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the test name with the behaviour the body verifies.

The name says "Should dispatch current-generation steer before queued input". The body stages two steers, steer-old at generation 0 and steer-new at the advanced generation, and never enqueues a queued input. The assertions at lines 166-182 verify generation fencing between the two steers and the one-shot lease. They do not verify priority between a steer and a queued entry.

A reader who trusts this name would believe steer-before-queue ordering is covered. Either rename the test to describe generation fencing, or enqueue a queued input so the stated priority is actually asserted.

As per path instructions: "Verify tests can fail when business logic changes" and "Ensure tests verify behavior outcomes, not just function calls".

💚 Option 1: rename to match the body
-	t.Run("Should dispatch current-generation steer before queued input", func(t *testing.T) {
+	t.Run("Should dispatch only the current-generation steer and lease it once", func(t *testing.T) {
💚 Option 2: enqueue a queued input so the name holds

Insert before the first claim at line 166:

if _, _, err := globalDB.EnqueueSessionInput(ctx, store.SessionInputQueueInsert{
	ID:                "inq-after-steer",
	SessionID:         sessionID,
	Mode:              store.SessionInputQueueModeQueue,
	Text:              "queued input",
	SessionGeneration: generation,
	QueueCap:          10,
	Now:               now.Add(2500 * time.Millisecond),
}); err != nil {
	t.Fatalf("EnqueueSessionInput() error = %v", err)
}

The existing assertion that steer-new is claimed first then proves the steer outranks the queued entry.

🤖 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 `@internal/store/globaldb/global_db_session_input_queue_test.go` around lines
129 - 165, Rename the test case around the visible generation-fencing assertions
to describe that it dispatches the current-generation steer while excluding the
stale steer; do not claim steer-before-queued-input priority unless the test
also enqueues and asserts a queued entry is dispatched afterward.

Source: Path instructions

internal/session/manager_busy_input.go (1)

306-319: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Activation failures leave durable queue entries behind in three paths. Each path persists or promotes a queue entry, then activates it. When activation returns an error, the code returns the error without removing the entry, so the daemon keeps an interrupt_then_prompt entry whose target turn was never interrupted. cancelUndeliverableInput already exists for this purpose and detaches the cancellation with context.WithoutCancel.

  • internal/session/manager_busy_input.go#L306-L319: wrap the activateInterruptingInput error from StageSteer with m.cancelUndeliverableInput(ctx, session.ID, entry.ID, err).
  • internal/session/manager_busy_input.go#L346-L358: wrap the activateInterruptingInput error from EnqueueInterrupt with the same call.
  • internal/session/manager_pending_input.go#L121-L123: wrap the ensureInterruptingInputActivated error from PromoteToSteer with the same call.
🤖 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 `@internal/session/manager_busy_input.go` around lines 306 - 319, Activation
failures leave durable interrupting-input entries behind; update all three
activation paths to cancel the undeliverable entry before returning the error.
In internal/session/manager_busy_input.go lines 306-319, wrap the
activateInterruptingInput error after StageSteer with m.cancelUndeliverableInput
using session.ID and entry.ID; apply the same change at lines 346-358 after
EnqueueInterrupt, and in internal/session/manager_pending_input.go lines 121-123
after PromoteToSteer wraps ensureInterruptingInputActivated. Preserve
cancellation’s detached-context behavior through the existing helper.
internal/session/manager_prompt_admission.go (1)

282-299: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

interruptAdmittedPrompt does not guard a nil input queue.

enqueueAdmittedBusyPrompt at line 205 and stageAdmittedSteerPrompt at line 244 both return ErrPromptInProgress when m.inputQueue is nil. The interrupt path calls m.inputQueue.EnqueueAdmittedInterrupt directly. EnqueueAdmittedInterrupt reads service configuration through prepareAdmittedEntry, so a nil service dereferences a nil pointer and panics inside the daemon request path.

Add the same guard that the two sibling paths use.

🛡️ Proposed guard
 ) (SendPromptResult, error) {
+	if m.inputQueue == nil {
+		return SendPromptResult{}, ErrPromptInProgress
+	}
 	previousTurnID, err := requireExpectedActiveTurn(session, req.expectedTurnID)
 	if err != nil {
 		return SendPromptResult{}, err
 	}
🤖 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 `@internal/session/manager_prompt_admission.go` around lines 282 - 299, Update
interruptAdmittedPrompt to check m.inputQueue for nil before calling
EnqueueAdmittedInterrupt, matching the guards in enqueueAdmittedBusyPrompt and
stageAdmittedSteerPrompt; return ErrPromptInProgress through the same existing
error path when the queue is unavailable.
🧹 Nitpick comments (17)
web/src/hooks/routes/__tests__/use-session-page-controls.test.tsx (1)

224-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a negative test for the missing turn fence.

This test proves the positive path: an active turn ID reaches the steer and interrupt mutations. The new guards in use-session-page-controls.ts (Lines 137 and 164) also block both operations when activeTurnId is empty. No test covers that branch, so a regression that drops the guard would still pass.

Add a case that renders with makeSession("active") (no turn ID) and asserts that steerPromptMutation.mutateAsync and interruptPromptMutation.mutateAsync are not called.

💚 Suggested test
+  it("Should block steer and interrupt when no active turn fence exists", async () => {
+    routeHookMocks.auiState.thread.isRunning = true;
+    const { result } = renderControls(makeSession("active"));
+
+    await act(async () => {
+      await result.current.handleSteerPrompt("new constraint");
+      await result.current.handleInterruptPrompt("replace the work");
+    });
+
+    expect(routeHookMocks.steerPromptMutation.mutateAsync).not.toHaveBeenCalled();
+    expect(routeHookMocks.interruptPromptMutation.mutateAsync).not.toHaveBeenCalled();
+  });
🤖 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 `@web/src/hooks/routes/__tests__/use-session-page-controls.test.tsx` around
lines 224 - 246, Add a negative test alongside the existing active-turn test
that renders controls with makeSession("active") and no activeTurnId, invokes
handleSteerPrompt and handleInterruptPrompt, and asserts both
steerPromptMutation.mutateAsync and interruptPromptMutation.mutateAsync are not
called.
web/src/components/assistant-ui/session-composer.tsx (1)

223-237: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider gating the queued-row Steer control with the same fence.

Lines 224 and 237 disable the direct Steer and Interrupt buttons when busyInputFenceAvailable is false. The queued-row Steer button in SessionComposerQueuedPrompts is not gated by this flag. handleSteerQueuedPrompt in web/src/hooks/routes/use-session-page-controls.ts (Line 190) returns early when activeTurnId is empty, so a user click on an enabled row control produces no result and no message.

Pass the fence state into the queued-prompt strip so the row control reflects the same precondition.

♻️ Suggested wiring
             <SessionComposerQueuedPrompts
               prompts={queuedPrompts}
               onSteer={onSteerQueuedPrompt!}
               onEdit={handleEditQueuedPrompt}
               onRemove={handleRemoveQueuedPrompt}
-              disabled={isBusyInputPending}
+              disabled={isBusyInputPending}
+              steerDisabled={!busyInputFenceAvailable}
             />

SessionComposerQueuedPrompts then applies steerDisabled only to the Steer control.

🤖 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 `@web/src/components/assistant-ui/session-composer.tsx` around lines 223 - 237,
Pass busyInputFenceAvailable from SessionComposer into
SessionComposerQueuedPrompts, and have the queued-row Steer control use it as
steerDisabled. Keep the existing gating for direct Steer and Interrupt buttons
unchanged, and apply the new disabled state only to the queued Steer action.
web/src/hooks/routes/use-session-page-controls.ts (1)

79-85: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize the derived queuedPrompts array.

Lines 79-85 build a new array on every render. The hook returns this array, and SessionThread forwards it to SessionComposer as the queuedPrompts prop. Every render of the session page therefore produces a new prop identity for the composer and for useSessionBusyInputActions. The previous store-owned value kept a stable identity between updates.

Derive the projection with useMemo keyed on the query data.

♻️ Suggested change
-  const queuedPrompts: QueuedPrompt[] =
-    sessionInputs.data?.inputs.map(input => ({
-      id: input.id,
-      mode: input.mode,
-      status: input.status,
-      text: input.text,
-    })) ?? [];
+  const queuedPrompts: QueuedPrompt[] = useMemo(
+    () =>
+      sessionInputs.data?.inputs.map(input => ({
+        id: input.id,
+        mode: input.mode,
+        status: input.status,
+        text: input.text,
+      })) ?? EMPTY_QUEUED_PROMPTS,
+    [sessionInputs.data]
+  );

Add import { useMemo } from "react"; and a module-level const EMPTY_QUEUED_PROMPTS: QueuedPrompt[] = [];.

🤖 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 `@web/src/hooks/routes/use-session-page-controls.ts` around lines 79 - 85,
Memoize the derived queuedPrompts projection in the useSessionPageControls hook
using useMemo, keyed on sessionInputs.data, and reuse a module-level
EMPTY_QUEUED_PROMPTS fallback so the returned array identity remains stable when
no inputs exist. Add the required React import and preserve the existing field
mapping.
internal/api/httpapi/handlers_test.go (1)

2735-2736: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add t.Parallel() to the outer test function.

TestSessionInputHandlersExposeAuthoritativeQueueMutations does not call t.Parallel(). The subtests share the mutable captures replaceOpts and promoteOpts, so the subtests must stay sequential. The outer function can still run in parallel with other tests.

♻️ Proposed change
 func TestSessionInputHandlersExposeAuthoritativeQueueMutations(t *testing.T) {
+	t.Parallel()
 	now := time.Date(2026, 8, 3, 18, 0, 0, 0, time.UTC)

As per coding guidelines: "use t.Run("Should …"), default t.Parallel".

🤖 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 `@internal/api/httpapi/handlers_test.go` around lines 2735 - 2736, Add
t.Parallel() at the beginning of
TestSessionInputHandlersExposeAuthoritativeQueueMutations, while leaving its
subtests sequential because they share the mutable replaceOpts and promoteOpts
captures.

Source: Coding guidelines

internal/daemon/native_session_input.go (2)

79-99: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Validate the body fields before the workspace and session lookup.

sessionInputReplace resolves the workspace and verifies session ownership first, then validates text, message_id, and idempotency_key. sessionPrompt in internal/daemon/native_session_mutation.go validates the required strings first and resolves the workspace afterwards. Aligning the order removes a workspace resolution and a session lookup for malformed requests. sessionInputPromote has the same ordering.

🤖 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 `@internal/daemon/native_session_input.go` around lines 79 - 99, In the
sessionInputReplace flow, validate text, message_id, and idempotency_key with
requiredNativeString before calling nativeSessionInputMutationScope; then
perform the workspace/session lookup and replacement unchanged. Apply the same
validation-before-lookup ordering to sessionInputPromote, matching
sessionPrompt.

60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the loop variable to avoid shadowing input.

The loop variable input shadows the decoded request struct input declared at line 48. The code is correct, but the two values have different types and meanings.

♻️ Proposed change
 	payloads := make([]contract.SessionInputPayload, 0, len(inputs))
-	for _, input := range inputs {
-		payloads = append(payloads, core.SessionInputPayloadFromSession(input))
+	for _, pending := range inputs {
+		payloads = append(payloads, core.SessionInputPayloadFromSession(pending))
 	}
🤖 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 `@internal/daemon/native_session_input.go` around lines 60 - 63, Rename the
range variable in the payload-building loop within the native session input
handler so it no longer shadows the decoded request struct input; update the
corresponding SessionInputPayloadFromSession argument while preserving the
existing behavior.
internal/daemon/native_tools_test.go (1)

5603-5634: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for the missing expected_turn_id guard.

This test covers the success path for steer mode with expected_turn_id set. sessionPrompt in internal/daemon/native_session_mutation.go lines 99-106 rejects steer and interrupt mode when expected_turn_id is blank. No test exercises that rejection. Add a case that calls ToolIDSessionPrompt with "mode":"steer" and no expected_turn_id, and assert the invalid-input reason plus zero SendPrompt calls.

Do you want me to generate that test case?

As per path instructions: "Verify tests can fail when business logic changes".

🤖 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 `@internal/daemon/native_tools_test.go` around lines 5603 - 5634, Add a test
case alongside the existing session_prompt steer-mode success test that calls
ToolIDSessionPrompt with mode "steer" but omits expected_turn_id, then assert
the invalid-input error reason and verify SendPrompt is not called. Ensure the
test fails if the expected_turn_id validation in sessionPrompt is removed or
bypassed.

Source: Path instructions

internal/api/core/session_prompt.go (1)

24-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use shared status constants or define them with SendPromptResult.

internal/session defines BusyInputModeQueue, BusyInputModeInterrupt, and BusyInputModeSteer constants, but SendPromptResult.Status uses the string literals "queued", "steering", and "interrupting" directly. Add exported status constants and use them from internal/session/manager_busy_input.go and internal/api/core/session_prompt.go so a rename cannot silently return 200 instead of 202.

🤖 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 `@internal/api/core/session_prompt.go` around lines 24 - 27, Define exported
status constants alongside SendPromptResult for the queued, steering, and
interrupting values, then replace the corresponding string literals in
internal/session/manager_busy_input.go and the status check in session_prompt.go
with those constants. Preserve the existing 202 behavior for these statuses and
the Replayed condition.
internal/extension/contract/host_api.go (1)

161-164: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider one shared mode type across request and result.

SessionsPromptParams.Mode uses apicontract.PromptMode. SessionPromptResult.Mode and SessionInput.Mode use session.BusyInputMode (see internal/extension/contract/host_api_results.go lines 56 and 78). Extension authors then see two different Go types for the same wire concept, and internal/extension/host_api_sessions.go line 123 converts through string. Align both sides on one contract type if the wire values are identical.

🤖 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 `@internal/extension/contract/host_api.go` around lines 161 - 164, Use one
shared mode type for the request and result contracts: update
SessionPromptResult.Mode and SessionInput.Mode to use apicontract.PromptMode,
matching SessionsPromptParams.Mode, and remove the unnecessary string conversion
in the related session handling flow.
internal/extension/host_api_sessions.go (1)

292-314: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a forward runtime-payload conversion helper.

internal/api/contract/prompt_runtime_selection.go only exposes PromptRuntimeSelectionFromPayload, while internal/extension/host_api_sessions.go still initializes PromptRuntimeSelectionPayload field by field. Add an exported forward conversion in the contract package and call it here so future payload changes stay in one place.

🤖 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 `@internal/extension/host_api_sessions.go` around lines 292 - 314, Add an
exported conversion helper in prompt_runtime_selection.go that converts the
contract’s PromptRuntimeSelectionPayload into the internal runtime selection
type, complementing PromptRuntimeSelectionFromPayload. Update
hostAPISessionInputFromPending to use this helper instead of constructing
PromptRuntimeSelectionPayload field by field, while preserving nil handling.
internal/store/globaldb/global_db_session_prompt_admission_queue.go (1)

284-312: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the admission status strings into named constants.

"queued", "interrupting", and "steering" are inline literals here. They form a wire-visible vocabulary that the HTTP, UDS, CLI, and web layers project. The output schema in internal/tools/builtin/sessions.go line 339 declares status as a plain string with no enum, so nothing pins these values. A typo or a divergent literal in another layer would not fail a build or a test.

Declare the values next to the other session input constants in internal/store/session_input_queue.go and reference them from every producer and consumer.

♻️ Proposed refactor

Add the constants in internal/store/session_input_queue.go:

const (
	SessionPromptAdmissionStatusQueued       = "queued"
	SessionPromptAdmissionStatusInterrupting = "interrupting"
	SessionPromptAdmissionStatusSteering     = "steering"
)

Then use them here:

-	status := "queued"
+	status := store.SessionPromptAdmissionStatusQueued
 	if entry.Mode == store.SessionInputQueueModeInterrupt {
-		status = "interrupting"
+		status = store.SessionPromptAdmissionStatusInterrupting
 	}
 	return store.SessionPromptAdmissionResult{
-		Status:          "steering",
+		Status:          store.SessionPromptAdmissionStatusSteering,
 		Mode:            store.SessionInputQueueModeSteer,
🤖 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 `@internal/store/globaldb/global_db_session_prompt_admission_queue.go` around
lines 284 - 312, The admission status strings are inline literals and need a
shared wire-level vocabulary. Define SessionPromptAdmissionStatusQueued,
SessionPromptAdmissionStatusInterrupting, and
SessionPromptAdmissionStatusSteering alongside the session input constants in
session_input_queue.go, then replace the literals in queuedPromptAdmissionResult
and stagedPromptAdmissionResult and update every other producer and consumer to
reference these constants.
internal/tools/builtin/builtin_test.go (1)

1492-1529: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split the requiresExpectedTurn flag from the output-schema choice.

requiresExpectedTurn controls two unrelated things. At line 1517 it selects the expected_turn_id assertion. At line 1526 it also selects which output envelope the helper verifies. Because of that coupling, assertSessionInputPromoteSchema must call assertSessionPromptMutationOutputSchema itself at line 1489, while assertSessionInputReplaceSchema depends on the helper's implicit branch. The responsibility split between caller and helper is asymmetric and is not visible from the parameter name.

A future mutation tool that requires expected_turn_id and returns the input envelope cannot be expressed with this signature.

Keep the input assertions in the helper and move the output assertion to each caller.

♻️ Proposed refactor
 func assertSessionInputReplaceSchema(t *testing.T, descriptor toolspkg.Descriptor) {
 	t.Helper()
 	assertSessionInputMutationSchema(t, descriptor, []string{
 		"idempotency_key", "message_id", "queue_entry_id", "session_id", "text", "workspace",
 	}, []string{"session_id", "queue_entry_id", "text", "message_id", "idempotency_key"}, false)
+	assertSessionInputOutputSchema(t, descriptor.ID.String()+" output", descriptor.OutputSchema)
 }
 	if requiresExpectedTurn {
 		var expectedTurn nativeObjectSchema
 		if err := json.Unmarshal(input.Properties["expected_turn_id"], &expectedTurn); err != nil {
 			t.Fatalf("%s expected_turn_id schema unmarshal error = %v", descriptor.ID, err)
 		}
 		if expectedTurn.Type != "string" || expectedTurn.MinLength != 1 {
 			t.Fatalf("%s expected_turn_id schema = %#v, want non-empty string", descriptor.ID, expectedTurn)
 		}
 	}
-	if !requiresExpectedTurn {
-		assertSessionInputOutputSchema(t, descriptor.ID.String()+" output", descriptor.OutputSchema)
-	}
 }
🤖 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 `@internal/tools/builtin/builtin_test.go` around lines 1492 - 1529, Remove the
output-schema branching from assertSessionInputMutationSchema, leaving it
responsible only for input and expected_turn_id assertions. Update each caller,
including assertSessionInputPromoteSchema and assertSessionInputReplaceSchema,
to explicitly invoke the appropriate output-schema assertion so expected_turn_id
validation is independent of the returned envelope.
internal/session/inputqueue/queue.go (1)

243-248: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider one insert-parameter struct instead of adjacent positional strings.

newInsert now takes sessionID, text, mode, delivery, and targetTurnID as five adjacent strings, and prepareAdmittedEntry forwards four of them. A swapped argument compiles without error and writes wrong queue metadata. A small parameter struct removes that risk and keeps the call sites readable.

♻️ Sketch of the parameter struct
+type insertSpec struct {
+	SessionID    string
+	Text         string
+	Mode         string
+	Delivery     string
+	TargetTurnID string
+	Generation   int64
+	Runtime      store.SessionInputRuntime
+}
+
-func (s *Service) newInsert(
-	sessionID string,
-	text string,
-	mode string,
-	delivery string,
-	targetTurnID string,
-	generation int64,
-	runtime store.SessionInputRuntime,
-) (store.SessionInputQueueInsert, error) {
+func (s *Service) newInsert(spec insertSpec) (store.SessionInputQueueInsert, error) {

Also applies to: 358-366

🤖 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 `@internal/session/inputqueue/queue.go` around lines 243 - 248, Replace the
adjacent string parameters for newInsert and prepareAdmittedEntry with a single
named parameter struct containing sessionID, text, mode, delivery, and
targetTurnID. Update prepareAdmittedEntry and all newInsert call sites to
populate and consume the struct by field, preserving the existing queue metadata
values and behavior.
internal/session/manager_busy_input_test.go (1)

1812-1814: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add t.Parallel() to the new top-level test function.

TestManagerPendingInputPromotionReplay marks only the subtest as parallel. The neighboring suites, such as TestManagerPromptAdmissionReplay at line 222 and TestManagerBusyInputPromptAdmissionReplay at line 330, call t.Parallel() on the outer function too.

As per coding guidelines: "use t.Run("Should …"), default t.Parallel".

♻️ Proposed change
 func TestManagerPendingInputPromotionReplay(t *testing.T) {
+	t.Parallel()
+
 	t.Run("Should replay promotion after the replacement turn starts without interrupting it again", func(t *testing.T) {
🤖 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 `@internal/session/manager_busy_input_test.go` around lines 1812 - 1814, Add
t.Parallel() at the start of TestManagerPendingInputPromotionReplay, before its
subtest declaration, matching the parallel execution pattern used by the
neighboring top-level test functions.

Source: Coding guidelines

internal/session/inputqueue/mutation.go (1)

15-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider one shared mutation helper for Replace and PromoteToSteer.

Both methods repeat the same nine steps and differ only in mode, delivery, target turn, and the store call. A single private helper keeps the replay, insert-building, and identity assignment in one place, and it reduces the seven-parameter signatures.

♻️ Sketch of a shared helper
type mutationSpec struct {
	sessionID      string
	entryID        string
	text           string
	targetTurnID   string
	messageID      string
	idempotencyKey string
	mode           string
	delivery       string
}

func (s *Service) mutate(
	ctx context.Context,
	spec mutationSpec,
	apply func(context.Context, string, string, store.SessionInputQueueInsert) (store.SessionInputQueueEntry, bool, error),
) (store.SessionInputQueueEntry, bool, error) {
	// trim, replay, build insert, then call apply
}
🤖 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 `@internal/session/inputqueue/mutation.go` around lines 15 - 112, Extract the
shared mutation flow from Replace and PromoteToSteer into a private mutate
helper, using a mutationSpec to carry their differing session, entry, text,
target-turn, identity, mode, and delivery values. Keep trimming, replayMutation,
Get, newInsert, replacement identity assignment, and error propagation in the
helper, while passing the operation-specific store behavior through the apply
callback. Update both methods to construct the appropriate spec and delegate to
mutate.
internal/store/globaldb/queries/session_input.sql (1)

15-15: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Anchor dispatch order to the store constant.

internal/store/globaldb/queries/session_input.sql has the same 'interrupt_then_prompt' literal in GetNextDispatchableSessionInput, PeekNextSessionInput, and ListPendingSessionInputs. Add one store test that inserts an older queued input and a newer SessionInputDeliveryInterruptThenPrompt input, then checks that the interrupting input is the one dispatched. If the literal and store.SessionInputDeliveryInterruptThenPrompt drift later, the test fails before runtime priority is lost.

🤖 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 `@internal/store/globaldb/queries/session_input.sql` at line 15, Replace the
duplicated 'interrupt_then_prompt' SQL literal in
GetNextDispatchableSessionInput, PeekNextSessionInput, and
ListPendingSessionInputs with the store constant
SessionInputDeliveryInterruptThenPrompt, and add one store test inserting an
older queued input plus a newer interrupt-then-prompt input that verifies the
interrupting input is dispatched first.
internal/store/globaldb/schema/definitions/20_sessions.sql (1)

183-183: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Make the pending index match the queue ordering.

idx_session_input_queue_pending is created as (session_id, status, delivery, enqueued_at, id), while dispatch queries order by CASE delivery ..., enqueued_at, and id. A plain delivery column does not match that CASE delivery ordering, so add the expression to the covered query/index chain or switch the queues to delivery DESC ordering so the index can serve dispatch.

🤖 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 `@internal/store/globaldb/schema/definitions/20_sessions.sql` at line 183,
Update idx_session_input_queue_pending and the corresponding dispatch query
ordering so they use the same delivery ordering semantics: either index the CASE
delivery expression used by dispatch, or change the queue ordering to delivery
DESC consistently. Preserve the existing enqueued_at and id ordering and ensure
the index column/order matches the final query exactly.
🤖 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 `@internal/cli/client_test.go`:
- Around line 238-250: Update internal/cli/client_test.go lines 238-250 in the
promote request fixture to return the interrupt_then_prompt delivery value and
assert the decoded response delivery matches
contract.PromptDeliveryInterruptThenPrompt. Update internal/cli/session_test.go
lines 2055-2058 to return contract.PromptDeliveryInterruptThenPrompt for the
promoted result and assert that delivery in the command output.

In `@internal/cli/session_test.go`:
- Around line 1849-1852: Replace the raw err.Error()/strings.Contains checks
with the project’s ErrorContains or ErrorAs helper in
internal/cli/session_test.go at lines 1849-1852, 1918-1920, 2107-2108, and
2135-2137. Keep each assertion specific to the missing interrupt fence, missing
steer fence, missing promotion fence, and removed --cancel flag error text
respectively.

In `@internal/extension/contract/host_api_results.go`:
- Line 57: Update the delivery field in the host API result contract to use the
concrete prompt delivery/source enum represented by PromptTurnSourceUser,
PromptTurnSourceNetwork, or PromptTurnSourceSynthetic, rather than the opaque
PromptDelivery domain type; ensure validation accepts and serialization
preserves those concrete values.

In `@internal/session/manager_busy_input_test.go`:
- Around line 206-208: The two assertions access the concurrently updated cancel
counter without synchronization. In internal/session/manager_busy_input_test.go
at lines 206-208 and 1888-1890, add a managerCancelCalls(h) helper that locks
h.driver.mu while reading cancelCalls, then use it for both the zero-cancel and
one-cancel assertions.

In `@internal/session/manager_prompt_lifetime_test.go`:
- Around line 207-221: Wrap the existing test logic in
TestPromptTranscriptMarkerSuppressesExpectedCancellation with a t.Run subtest
named using the “Should …” pattern, and move t.Parallel() into that subtest
while preserving the current cancellation assertions.

In `@internal/store/globaldb/global_db_session_input_queue_mutation.go`:
- Around line 164-175: Update the replay handling around
getSessionInputQueueEntry and sameSessionInputMutation so a replacement is
acknowledged only when the stored entry remains queued. Reject canceled,
dispatching, or sent entries by returning
store.ErrSessionInputQueueEntryNotQueued, while preserving the existing conflict
error for mismatched mutations and the normal replay path for queued entries.

In `@internal/store/globaldb/schema/migrations/00043_schema.sql`:
- Line 7: Update migration 00043 around the new_session_input_queue row-copy so
legacy session_input_queue fields, including delivery and target_turn_id, are
migrated without loss; prefer altering the existing schema rather than
rebuilding it, or explicitly map every legacy column and preserve its per-row
value instead of relying on defaults.

In `@web/src/components/assistant-ui/hooks/use-session-busy-input-actions.ts`:
- Around line 63-70: Update the action flow around handler and
describeComposerActionError so synchronous throws from
handler(trimmedComposerText) are converted into the same promise chain before
the existing catch, ensuring they trigger the failure toast while preserving
abort handling and success behavior.
- Around line 75-87: Update the edit flow around editingQueuedPrompt and
onReplaceQueuedPrompt so an existing queued prompt never falls through to
onQueuePrompt when replacement is unavailable. Require onReplaceQueuedPrompt
before entering edit mode, or return after reporting an error if it is absent;
preserve the existing atomic replacement path when the handler exists.

In `@web/src/hooks/routes/use-session-page-controls.ts`:
- Around line 189-220: The queued-prompt handlers generate IDs directly through
globalThis.crypto.randomUUID(), which can throw in non-secure contexts. Update
handleSteerQueuedPrompt and handleReplaceQueuedPrompt to use the existing shared
ID helper that provides a secure-context fallback for both idempotency_key and
message_id, preserving the current mutation and error-handling behavior.

In `@web/src/systems/session/hooks/use-session-actions.ts`:
- Line 270: Update the interrupt and steer request flows in the session actions
hook to validate that expectedTurnId is present and non-empty before making
either request. Reject or return early when the fence is missing, and only
include the validated value as expected_turn_id; never omit it or substitute an
empty string. Apply the change to both request construction sites referenced
near the interrupt and steer actions.

---

Outside diff comments:
In `@internal/daemon/native_session_mutation.go`:
- Around line 99-121: Trim input.ExpectedTurnID once before validation, reuse
that trimmed value for the empty-check, and pass it as ExpectedTurnID in the
session.SendPrompt call within the native session mutation flow. Preserve the
existing validation and error behavior for blank values.

In `@internal/session/manager_busy_input.go`:
- Around line 306-319: Activation failures leave durable interrupting-input
entries behind; update all three activation paths to cancel the undeliverable
entry before returning the error. In internal/session/manager_busy_input.go
lines 306-319, wrap the activateInterruptingInput error after StageSteer with
m.cancelUndeliverableInput using session.ID and entry.ID; apply the same change
at lines 346-358 after EnqueueInterrupt, and in
internal/session/manager_pending_input.go lines 121-123 after PromoteToSteer
wraps ensureInterruptingInputActivated. Preserve cancellation’s detached-context
behavior through the existing helper.

In `@internal/session/manager_prompt_admission.go`:
- Around line 282-299: Update interruptAdmittedPrompt to check m.inputQueue for
nil before calling EnqueueAdmittedInterrupt, matching the guards in
enqueueAdmittedBusyPrompt and stageAdmittedSteerPrompt; return
ErrPromptInProgress through the same existing error path when the queue is
unavailable.

In `@internal/store/globaldb/global_db_session_input_queue_test.go`:
- Around line 129-165: Rename the test case around the visible
generation-fencing assertions to describe that it dispatches the
current-generation steer while excluding the stale steer; do not claim
steer-before-queued-input priority unless the test also enqueues and asserts a
queued entry is dispatched afterward.

In `@internal/tools/builtin/builtin_test.go`:
- Around line 1407-1420: Update the failure message in the identity-field
assertion within the builtin schema test to describe a string with no minLength
constraint, matching the existing checks on identity.Type and
identity.MinLength. Keep the assertion logic unchanged.

---

Nitpick comments:
In `@internal/api/core/session_prompt.go`:
- Around line 24-27: Define exported status constants alongside SendPromptResult
for the queued, steering, and interrupting values, then replace the
corresponding string literals in internal/session/manager_busy_input.go and the
status check in session_prompt.go with those constants. Preserve the existing
202 behavior for these statuses and the Replayed condition.

In `@internal/api/httpapi/handlers_test.go`:
- Around line 2735-2736: Add t.Parallel() at the beginning of
TestSessionInputHandlersExposeAuthoritativeQueueMutations, while leaving its
subtests sequential because they share the mutable replaceOpts and promoteOpts
captures.

In `@internal/daemon/native_session_input.go`:
- Around line 79-99: In the sessionInputReplace flow, validate text, message_id,
and idempotency_key with requiredNativeString before calling
nativeSessionInputMutationScope; then perform the workspace/session lookup and
replacement unchanged. Apply the same validation-before-lookup ordering to
sessionInputPromote, matching sessionPrompt.
- Around line 60-63: Rename the range variable in the payload-building loop
within the native session input handler so it no longer shadows the decoded
request struct input; update the corresponding SessionInputPayloadFromSession
argument while preserving the existing behavior.

In `@internal/daemon/native_tools_test.go`:
- Around line 5603-5634: Add a test case alongside the existing session_prompt
steer-mode success test that calls ToolIDSessionPrompt with mode "steer" but
omits expected_turn_id, then assert the invalid-input error reason and verify
SendPrompt is not called. Ensure the test fails if the expected_turn_id
validation in sessionPrompt is removed or bypassed.

In `@internal/extension/contract/host_api.go`:
- Around line 161-164: Use one shared mode type for the request and result
contracts: update SessionPromptResult.Mode and SessionInput.Mode to use
apicontract.PromptMode, matching SessionsPromptParams.Mode, and remove the
unnecessary string conversion in the related session handling flow.

In `@internal/extension/host_api_sessions.go`:
- Around line 292-314: Add an exported conversion helper in
prompt_runtime_selection.go that converts the contract’s
PromptRuntimeSelectionPayload into the internal runtime selection type,
complementing PromptRuntimeSelectionFromPayload. Update
hostAPISessionInputFromPending to use this helper instead of constructing
PromptRuntimeSelectionPayload field by field, while preserving nil handling.

In `@internal/session/inputqueue/mutation.go`:
- Around line 15-112: Extract the shared mutation flow from Replace and
PromoteToSteer into a private mutate helper, using a mutationSpec to carry their
differing session, entry, text, target-turn, identity, mode, and delivery
values. Keep trimming, replayMutation, Get, newInsert, replacement identity
assignment, and error propagation in the helper, while passing the
operation-specific store behavior through the apply callback. Update both
methods to construct the appropriate spec and delegate to mutate.

In `@internal/session/inputqueue/queue.go`:
- Around line 243-248: Replace the adjacent string parameters for newInsert and
prepareAdmittedEntry with a single named parameter struct containing sessionID,
text, mode, delivery, and targetTurnID. Update prepareAdmittedEntry and all
newInsert call sites to populate and consume the struct by field, preserving the
existing queue metadata values and behavior.

In `@internal/session/manager_busy_input_test.go`:
- Around line 1812-1814: Add t.Parallel() at the start of
TestManagerPendingInputPromotionReplay, before its subtest declaration, matching
the parallel execution pattern used by the neighboring top-level test functions.

In `@internal/store/globaldb/global_db_session_prompt_admission_queue.go`:
- Around line 284-312: The admission status strings are inline literals and need
a shared wire-level vocabulary. Define SessionPromptAdmissionStatusQueued,
SessionPromptAdmissionStatusInterrupting, and
SessionPromptAdmissionStatusSteering alongside the session input constants in
session_input_queue.go, then replace the literals in queuedPromptAdmissionResult
and stagedPromptAdmissionResult and update every other producer and consumer to
reference these constants.

In `@internal/store/globaldb/queries/session_input.sql`:
- Line 15: Replace the duplicated 'interrupt_then_prompt' SQL literal in
GetNextDispatchableSessionInput, PeekNextSessionInput, and
ListPendingSessionInputs with the store constant
SessionInputDeliveryInterruptThenPrompt, and add one store test inserting an
older queued input plus a newer interrupt-then-prompt input that verifies the
interrupting input is dispatched first.

In `@internal/store/globaldb/schema/definitions/20_sessions.sql`:
- Line 183: Update idx_session_input_queue_pending and the corresponding
dispatch query ordering so they use the same delivery ordering semantics: either
index the CASE delivery expression used by dispatch, or change the queue
ordering to delivery DESC consistently. Preserve the existing enqueued_at and id
ordering and ensure the index column/order matches the final query exactly.

In `@internal/tools/builtin/builtin_test.go`:
- Around line 1492-1529: Remove the output-schema branching from
assertSessionInputMutationSchema, leaving it responsible only for input and
expected_turn_id assertions. Update each caller, including
assertSessionInputPromoteSchema and assertSessionInputReplaceSchema, to
explicitly invoke the appropriate output-schema assertion so expected_turn_id
validation is independent of the returned envelope.

In `@web/src/components/assistant-ui/session-composer.tsx`:
- Around line 223-237: Pass busyInputFenceAvailable from SessionComposer into
SessionComposerQueuedPrompts, and have the queued-row Steer control use it as
steerDisabled. Keep the existing gating for direct Steer and Interrupt buttons
unchanged, and apply the new disabled state only to the queued Steer action.

In `@web/src/hooks/routes/__tests__/use-session-page-controls.test.tsx`:
- Around line 224-246: Add a negative test alongside the existing active-turn
test that renders controls with makeSession("active") and no activeTurnId,
invokes handleSteerPrompt and handleInterruptPrompt, and asserts both
steerPromptMutation.mutateAsync and interruptPromptMutation.mutateAsync are not
called.

In `@web/src/hooks/routes/use-session-page-controls.ts`:
- Around line 79-85: Memoize the derived queuedPrompts projection in the
useSessionPageControls hook using useMemo, keyed on sessionInputs.data, and
reuse a module-level EMPTY_QUEUED_PROMPTS fallback so the returned array
identity remains stable when no inputs exist. Add the required React import and
preserve the existing field mapping.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c4d6e386-bd32-4efa-88f0-6bed098a48b9

📥 Commits

Reviewing files that changed from the base of the PR and between dc65b22 and 7cfea4e.

⛔ Files ignored due to path filters (26)
  • docs/qa/_seeds/feature-stories/01_analysis_runtime-sessions.md is excluded by !**/*.md
  • docs/qa/_seeds/feature-stories/feature-status.csv is excluded by !**/*.csv
  • docs/qa/_seeds/final-qa/_children/11-api-cli-parity.md is excluded by !**/*.md
  • docs/qa/reports/2026-08-03-session-input-controls.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-session-transcript-calm-grammar.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-019.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-059.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-session-lifecycle-affordances.md is excluded by !**/*.md
  • internal/store/globaldb/schema/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
  • internal/tools/builtin/testdata/native-tool-catalog.json is excluded by !**/*.json
  • openapi/compozy.json is excluded by !**/*.json
  • packages/site/content/docs/cli/session/index.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/input/cancel.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/input/edit.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/input/index.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/input/list.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/input/meta.json is excluded by !**/*.json
  • packages/site/content/docs/cli/session/input/steer.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/session/meta.json is excluded by !**/*.json
  • packages/site/content/docs/cli/session/prompt.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/sessions/events.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/sessions/lifecycle.mdx is excluded by !**/*.mdx
  • sdk/typescript/src/generated/contracts.ts is excluded by !**/generated/**, !**/generated/**
  • skills/compozy/references/extension-authoring.md is excluded by !**/*.md
  • skills/compozy/references/runtime-operations.md is excluded by !**/*.md
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
📒 Files selected for processing (142)
  • internal/acp/agent_process.go
  • internal/acp/client.go
  • internal/acp/handlers.go
  • internal/acp/handlers_session_state.go
  • internal/acp/handlers_steer.go
  • internal/acp/handlers_steer_test.go
  • internal/acp/handlers_test.go
  • internal/acp/start_process.go
  • internal/acp/types.go
  • internal/api/contract/prompt.go
  • internal/api/contract/prompt_input.go
  • internal/api/core/interfaces.go
  • internal/api/core/session_input.go
  • internal/api/core/session_manager_stub_test.go
  • internal/api/core/session_prompt.go
  • internal/api/core/session_prompt_dispatch.go
  • internal/api/core/session_workspace.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/prompt.go
  • internal/api/httpapi/session_routes.go
  • internal/api/spec/registry_sessions.go
  • internal/api/testutil/session_stub.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/prompt.go
  • internal/api/udsapi/session_routes.go
  • internal/cli/client_session_api.go
  • internal/cli/client_session_input.go
  • internal/cli/client_session_types.go
  • internal/cli/client_sessions.go
  • internal/cli/client_test.go
  • internal/cli/helpers_test.go
  • internal/cli/session_command.go
  • internal/cli/session_input.go
  • internal/cli/session_input_output.go
  • internal/cli/session_prompt_events.go
  • internal/cli/session_prompt_output.go
  • internal/cli/session_test.go
  • internal/daemon/daemon_test.go
  • internal/daemon/native_session_input.go
  • internal/daemon/native_session_mutation.go
  • internal/daemon/native_tool_binding_groups.go
  • internal/daemon/native_tools_test.go
  • internal/daemon/session_manager_factory.go
  • internal/daemon/session_steer_source.go
  • internal/extension/contract/host_api.go
  • internal/extension/contract/host_api_method_registry.go
  • internal/extension/contract/host_api_results.go
  • internal/extension/contract/host_api_test.go
  • internal/extension/contract/permissions.go
  • internal/extension/host_api.go
  • internal/extension/host_api_contract_types.go
  • internal/extension/host_api_methods.go
  • internal/extension/host_api_prompt_projection.go
  • internal/extension/host_api_sessions.go
  • internal/extension/host_api_test.go
  • internal/extension/host_api_workspace_binding.go
  • internal/extensionprotocol/host_api.go
  • internal/extensionprotocol/host_api_test.go
  • internal/session/inputqueue/mutation.go
  • internal/session/inputqueue/queue.go
  • internal/session/manager.go
  • internal/session/manager_busy_input.go
  • internal/session/manager_busy_input_activation.go
  • internal/session/manager_busy_input_test.go
  • internal/session/manager_busy_input_types.go
  • internal/session/manager_input_dispatch.go
  • internal/session/manager_interrupt_salvage.go
  • internal/session/manager_pending_input.go
  • internal/session/manager_prompt_admission.go
  • internal/session/manager_prompt_admission_identity.go
  • internal/session/manager_prompt_event_observation.go
  • internal/session/manager_prompt_input_event.go
  • internal/session/manager_prompt_lifetime_test.go
  • internal/session/manager_types.go
  • internal/session/session.go
  • internal/session/synthetic_prompt.go
  • internal/store/globaldb/global_db_session_input_queue.go
  • internal/store/globaldb/global_db_session_input_queue_mutation.go
  • internal/store/globaldb/global_db_session_input_queue_scan.go
  • internal/store/globaldb/global_db_session_input_queue_terminal.go
  • internal/store/globaldb/global_db_session_input_queue_test.go
  • internal/store/globaldb/global_db_session_prompt_admission.go
  • internal/store/globaldb/global_db_session_prompt_admission_queue.go
  • internal/store/globaldb/queries/session_input.sql
  • internal/store/globaldb/schema/definitions/20_sessions.sql
  • internal/store/globaldb/schema/migrations/00043_schema.sql
  • internal/store/globaldb/sqlcgen/models.go
  • internal/store/globaldb/sqlcgen/session_input.sql.go
  • internal/store/session_input_queue.go
  • internal/store/session_prompt_admission.go
  • internal/toolmeta/native_entries.go
  • internal/toolmeta/registry_test.go
  • internal/tools/builtin/builtin_test.go
  • internal/tools/builtin/sessions.go
  • internal/tools/builtin/toolsets.go
  • internal/tools/builtin_ids.go
  • sdk/go/contracts/host_contracts_gen.go
  • sdk/go/contracts/host_methods_gen.go
  • sdk/go/contracts/types_015_gen.go
  • sdk/go/contracts/types_016_gen.go
  • sdk/go/contracts/types_017_gen.go
  • sdk/go/contracts/types_018_gen.go
  • sdk/go/contracts/types_019_gen.go
  • sdk/go/contracts/types_020_gen.go
  • sdk/go/contracts/types_021_gen.go
  • sdk/go/contracts/types_022_gen.go
  • sdk/go/contracts/types_023_gen.go
  • sdk/go/contracts/types_024_gen.go
  • sdk/go/contracts/types_025_gen.go
  • sdk/go/contracts/types_026_gen.go
  • sdk/go/contracts/types_027_gen.go
  • web/src/components/assistant-ui/__tests__/session-thread.test.tsx
  • web/src/components/assistant-ui/hooks/use-session-busy-input-actions.ts
  • web/src/components/assistant-ui/session-composer-queued-prompts.tsx
  • web/src/components/assistant-ui/session-composer.tsx
  • web/src/components/assistant-ui/session-thread-states.tsx
  • web/src/components/assistant-ui/session-thread-viewport.tsx
  • web/src/components/assistant-ui/session-thread.tsx
  • web/src/components/assistant-ui/session-working-row.tsx
  • web/src/hooks/routes/__tests__/session-page-controls-store.test.ts
  • web/src/hooks/routes/__tests__/use-session-page-controls.test.tsx
  • web/src/hooks/routes/session-page-controls-effects.ts
  • web/src/hooks/routes/session-page-controls-store.ts
  • web/src/hooks/routes/use-session-page-controls.ts
  • web/src/systems/os/apps/session/session-window-content.tsx
  • web/src/systems/session/adapters/__tests__/session-api.test.ts
  • web/src/systems/session/adapters/session-api.ts
  • web/src/systems/session/adapters/session-input-api.ts
  • web/src/systems/session/components/__tests__/runtime-activity-notice.test.tsx
  • web/src/systems/session/components/runtime-activity-notice.logic.ts
  • web/src/systems/session/components/runtime-activity-notice.tsx
  • web/src/systems/session/components/session-prompt-runtime-selector.tsx
  • web/src/systems/session/hooks/__tests__/use-session-actions.test.tsx
  • web/src/systems/session/hooks/__tests__/use-session-live-tail.test.tsx
  • web/src/systems/session/hooks/use-session-actions.ts
  • web/src/systems/session/hooks/use-session-inputs.ts
  • web/src/systems/session/index.ts
  • web/src/systems/session/lib/query-keys.ts
  • web/src/systems/session/lib/query-options.ts
  • web/src/systems/session/lib/queued-prompt.ts
  • web/src/systems/session/lib/session-query-invalidation.ts
  • web/src/systems/session/types.ts
💤 Files with no reviewable changes (16)
  • internal/acp/handlers.go
  • internal/daemon/session_manager_factory.go
  • internal/acp/types.go
  • internal/session/manager_types.go
  • internal/cli/client_sessions.go
  • internal/acp/start_process.go
  • internal/daemon/session_steer_source.go
  • internal/acp/agent_process.go
  • internal/session/manager_interrupt_salvage.go
  • internal/acp/handlers_session_state.go
  • internal/acp/handlers_test.go
  • internal/session/manager.go
  • internal/acp/handlers_steer_test.go
  • internal/acp/client.go
  • internal/acp/handlers_steer.go
  • web/src/components/assistant-ui/session-thread-viewport.tsx

Comment thread internal/cli/client_test.go Outdated
Comment thread internal/cli/session_test.go Outdated
Comment thread internal/extension/contract/host_api_results.go Outdated
Comment thread internal/session/manager_busy_input_test.go Outdated
Comment thread internal/session/manager_prompt_lifetime_test.go
Comment thread internal/store/globaldb/schema/migrations/00043_schema.sql
Comment thread web/src/components/assistant-ui/hooks/use-session-busy-input-actions.ts Outdated
Comment thread web/src/hooks/routes/use-session-page-controls.ts
Comment thread web/src/systems/session/hooks/use-session-actions.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
web/src/components/assistant-ui/session-composer.tsx (1)

64-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a fail-closed fence default.

Both default fallbacks for busyInputFenceAvailable treat missing active-turn state as available. Change the default to false or require the prop; otherwise Steer and Interrupt can render enabled when the caller or component receives no active-turn state.

🤖 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 `@web/src/components/assistant-ui/session-composer.tsx` at line 64, Change both
fallback defaults for busyInputFenceAvailable in the session composer
configuration to false, including the visible default assignment, so missing
active-turn state disables Steer and Interrupt. Preserve explicitly provided
prop values.

Source: Coding guidelines

internal/store/globaldb/global_db_session_input_queue_test.go (1)

346-408: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Verify replacement preserves FIFO position.

This test has no later queued entry. It passes if replacement moves behind a later input because it only checks that one entry remains. Enqueue a follower before replacement, then assert that ClaimNextSessionInput claims the replacement before the follower.

As per coding guidelines, tests must focus on critical workflow behavior and verify behavior outcomes.

🤖 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 `@internal/store/globaldb/global_db_session_input_queue_test.go` around lines
346 - 408, The replacement test should verify FIFO position, not only
replacement identity and pending count. In the test around ReplaceSessionInput,
enqueue a follower after the original but before replacement, then use
ClaimNextSessionInput to assert the replacement is claimed first and the
follower remains next; preserve the existing replay and conflict assertions.

Sources: Coding guidelines, Path instructions

🤖 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.

Outside diff comments:
In `@internal/store/globaldb/global_db_session_input_queue_test.go`:
- Around line 346-408: The replacement test should verify FIFO position, not
only replacement identity and pending count. In the test around
ReplaceSessionInput, enqueue a follower after the original but before
replacement, then use ClaimNextSessionInput to assert the replacement is claimed
first and the follower remains next; preserve the existing replay and conflict
assertions.

In `@web/src/components/assistant-ui/session-composer.tsx`:
- Line 64: Change both fallback defaults for busyInputFenceAvailable in the
session composer configuration to false, including the visible default
assignment, so missing active-turn state disables Steer and Interrupt. Preserve
explicitly provided prop values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc46325c-892a-41fb-b2d8-adcb4a7053f8

📥 Commits

Reviewing files that changed from the base of the PR and between 7cfea4e and 7189997.

⛔ Files ignored due to path filters (8)
  • .codex/scripts/coderabbit-review-graphql.sh is excluded by !.codex/**
  • docs/qa/bugs/BUG-20260803-prompt-cancel-warning-noise.md is excluded by !**/*.md
  • docs/qa/reports/2026-08-03-session-input-coderabbit.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-session-transcript-calm-grammar.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-019.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-059.md is excluded by !**/*.md
  • internal/store/globaldb/schema/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
  • sdk/typescript/src/generated/contracts.ts is excluded by !**/generated/**, !**/generated/**
📒 Files selected for processing (57)
  • internal/api/contract/contract_test.go
  • internal/api/contract/prompt_runtime_selection.go
  • internal/api/core/session_input.go
  • internal/api/core/session_prompt.go
  • internal/api/httpapi/handlers_test.go
  • internal/cli/client_test.go
  • internal/cli/session_test.go
  • internal/daemon/native_session_input.go
  • internal/daemon/native_session_mutation.go
  • internal/daemon/native_tools_test.go
  • internal/extension/contract/host_api_results.go
  • internal/extension/host_api_sessions.go
  • internal/extension/host_api_test.go
  • internal/session/inputqueue/mutation.go
  • internal/session/inputqueue/queue.go
  • internal/session/manager_busy_input.go
  • internal/session/manager_busy_input_activation.go
  • internal/session/manager_busy_input_test.go
  • internal/session/manager_pending_input.go
  • internal/session/manager_prompt_admission.go
  • internal/session/manager_prompt_lifetime_test.go
  • internal/store/globaldb/global_db_session_input_queue_test.go
  • internal/store/globaldb/global_db_session_prompt_admission.go
  • internal/store/globaldb/global_db_session_prompt_admission_queue.go
  • internal/store/globaldb/queries/session_input.sql
  • internal/store/globaldb/queries/session_prompt_admission.sql
  • internal/store/globaldb/schema/definitions/20_sessions.sql
  • internal/store/globaldb/schema/migrations/00044_schema.sql
  • internal/store/globaldb/sqlcgen/session_input.sql.go
  • internal/store/globaldb/sqlcgen/session_prompt_admission.sql.go
  • internal/store/session_prompt_admission.go
  • internal/tools/builtin/builtin_test.go
  • sdk/go/contracts/types_005_gen.go
  • sdk/go/contracts/types_006_gen.go
  • sdk/go/contracts/types_007_gen.go
  • sdk/go/contracts/types_008_gen.go
  • sdk/go/contracts/types_009_gen.go
  • sdk/go/contracts/types_010_gen.go
  • sdk/go/contracts/types_011_gen.go
  • sdk/go/contracts/types_012_gen.go
  • sdk/go/contracts/types_013_gen.go
  • sdk/go/contracts/types_014_gen.go
  • sdk/go/contracts/types_015_gen.go
  • sdk/go/contracts/types_019_gen.go
  • web/src/components/assistant-ui/__tests__/session-thread.test.tsx
  • web/src/components/assistant-ui/hooks/use-session-busy-input-actions.ts
  • web/src/components/assistant-ui/session-composer-queued-prompts.tsx
  • web/src/components/assistant-ui/session-composer.tsx
  • web/src/hooks/routes/__tests__/use-session-page-controls.test.tsx
  • web/src/hooks/routes/use-session-page-controls.ts
  • web/src/lib/client-id.ts
  • web/src/systems/network/hooks/network-message-builders.ts
  • web/src/systems/session/components/__tests__/runtime-activity-notice.test.tsx
  • web/src/systems/session/components/runtime-activity-notice.tsx
  • web/src/systems/session/hooks/__tests__/use-session-actions.test.tsx
  • web/src/systems/session/hooks/use-session-actions.ts
  • web/src/systems/session/lib/session-prompt-chat-transport.ts
🚧 Files skipped from review as they are similar to previous changes (28)
  • web/src/systems/session/components/runtime-activity-notice.tsx
  • internal/daemon/native_session_mutation.go
  • internal/session/manager_busy_input_activation.go
  • web/src/systems/session/hooks/tests/use-session-actions.test.tsx
  • internal/store/globaldb/global_db_session_prompt_admission_queue.go
  • internal/session/manager_prompt_lifetime_test.go
  • internal/extension/contract/host_api_results.go
  • internal/cli/client_test.go
  • web/src/components/assistant-ui/hooks/use-session-busy-input-actions.ts
  • internal/store/globaldb/schema/definitions/20_sessions.sql
  • internal/api/core/session_input.go
  • internal/session/manager_pending_input.go
  • internal/api/core/session_prompt.go
  • web/src/components/assistant-ui/session-composer-queued-prompts.tsx
  • internal/session/manager_prompt_admission.go
  • internal/tools/builtin/builtin_test.go
  • internal/store/globaldb/queries/session_input.sql
  • internal/api/httpapi/handlers_test.go
  • internal/extension/host_api_sessions.go
  • internal/cli/session_test.go
  • internal/session/inputqueue/mutation.go
  • internal/daemon/native_tools_test.go
  • internal/session/manager_busy_input.go
  • web/src/hooks/routes/use-session-page-controls.ts
  • internal/daemon/native_session_input.go
  • web/src/systems/session/components/tests/runtime-activity-notice.test.tsx
  • internal/extension/host_api_test.go
  • internal/store/globaldb/sqlcgen/session_input.sql.go

This branch was successfully deployed

1 active deployment
Preview — 7189997e Deployed Aug 4, 2026 by vercel[bot]
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