Skip to content

🤖 fix: send queued message with empty Enter#3592

Merged
ThomasK33 merged 2 commits into
mainfrom
messaging-j0yq
Jun 18, 2026
Merged

🤖 fix: send queued message with empty Enter#3592
ThomasK33 merged 2 commits into
mainfrom
messaging-j0yq

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

Enable pressing Enter in an empty workspace composer to trigger the queued-message “Send now” action, so queued follow-ups can be dispatched immediately without using the mouse.

Background

When a follow-up is queued to send after the active turn, the UI already exposes a “Send now” button in the queued-message banner. Keyboard-only users could queue the follow-up, but then still needed pointer interaction to send it immediately.

Implementation

  • Pass the current queued message and immediate-send callback from ChatPane into the workspace ChatInput.
  • Handle the existing send-message keybind in ChatInput when the workspace composer is empty, a queued message exists, there are no staged attachments or reviews, the user is not editing a prior message, and the layout is not mobile-touch.
  • Keep queued send-now attempts guarded by queued-message id until the queued message changes or clears, while resetting the guard on failed/throwing interrupt attempts.
  • Add a UI regression test that queues a follow-up through the normal send-mode menu, presses Enter twice quickly, and verifies only one queued mock response is sent.

Validation

  • bun x jest tests/ui/chat/sendModeDropdown.test.ts -t "pressing Enter on an empty composer sends the queued message now" --runInBand
  • make static-check
  • PR readiness: Codex comments resolved/approved and required CI checks passed via ./scripts/wait_pr_ready.sh 3592

Risks

Low. The shortcut only fires for workspace chat inputs with an existing queued message, an empty text draft, no attachments, no attached reviews, non-editing mode, and non-mobile touch layout; duplicate send-now attempts are ignored while the queued id is being flushed.


Generated with mux • Model: openai:gpt-5.5 • Thinking: xhigh • Cost: $30.42

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4af7df4595

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/ChatInput/index.tsx
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please take another look after the edit-mode guard fix.

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please review the latest pushed commit; the previous edit-mode thread is addressed and resolved.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41c2e3fb67

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/features/ChatInput/index.tsx
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please review the latest pushed commit; the repeated Enter in-flight guard is addressed and the thread is resolved.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d641c9038e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/components/ChatPane/ChatPane.tsx Outdated
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please review the latest pushed commit; the stale queued-message window is guarded until the queued id changes/clears, and the test now covers a fast second Enter press.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6c527592b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/components/ChatPane/ChatPane.tsx Outdated
Move the queued Enter shortcut into ChatInput's existing key/state path and exercise it through the user-visible queued-send flow.\n\n---\n\n_Generated with `mux` • Model: `openai:gpt-5.5` • Thinking: `xhigh` • Cost: `n/a`_\n\n<!-- mux-attribution: model=openai:gpt-5.5 thinking=xhigh costs=n/a -->

Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please review the latest pushed commit; the guard now resets on interruptStream failure responses as well as thrown errors.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 6beed0c0f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33 ThomasK33 added this pull request to the merge queue Jun 18, 2026
Merged via the queue into main with commit 10b0dbd Jun 18, 2026
22 of 24 checks passed
@ThomasK33 ThomasK33 deleted the messaging-j0yq branch June 18, 2026 12:38
@mux-bot mux-bot Bot mentioned this pull request Jun 18, 2026
jim-fung pushed a commit to jim-fung/mux that referenced this pull request Jul 1, 2026
## Summary

Long-lived auto-cleanup PR maintained by the **Auto-Cleanup Agent**.
Each pass lands at most one extremely low-risk, behavior-preserving
cleanup drawn from recently merged `main` activity, then advances the
checkpoint below. The prior long-lived PR (coder#3559) merged into `main`;
this is its successor.

Auto-cleanup checkpoint: 2af29df

## This pass

Considered the one new `main` commit since the previous checkpoint —
coder#3603 (`feat: add built-in loop skill`). That change adds the advertised
`loop` built-in skill (`src/node/builtinSkills/loop.md`), regenerates
`builtInSkillContent.generated.ts` (generated — off-limits), and extends
the agent-skills discovery test expectations. The
skill-content/generated surface offers no safe hand-edit, so the cleanup
was drawn from the production sibling of that area: the skill-discovery
service `agentSkillsService.ts`.

Landed one behavior-preserving cleanup in `agentSkillsService.ts`:
`readSkillDescriptorFromDir` open-coded the same invalid-skill
diagnostic push — `options?.invalidSkills?.push({ directoryName, scope,
displayPath: skillFilePath, message, hint })` — at six failure sites
(missing/unreadable SKILL.md, SKILL.md-is-a-directory, oversized file,
read failure, invalid descriptor, parse failure). Those six sites share
identical identity fields (`directoryName` / `scope` / `displayPath`)
and differ only in `message` + `hint`. All six now delegate to a single
function-local `pushInvalidSkill(message, hint)` closure that captures
the function-constant identity fields. Behavior-preserving: the pushed
object shape and every per-site `message`/`hint` are unchanged, and the
closure short-circuits on `options?.invalidSkills` exactly as the inline
calls did.

## Branch contents (net diff against `main`)

<details>
<summary>Cleanups carried by this PR (not yet merged)</summary>

- **`agentSkillsService.ts` — dedupe invalid-skill diagnostics.**
coder#3603's new built-in skill lives in the agent-skills discovery area;
`readSkillDescriptorFromDir` open-coded the same
`options?.invalidSkills?.push({ directoryName, scope, displayPath:
skillFilePath, message, hint })` at six failure sites that share
identical identity fields and differ only in `message`/`hint`. All six
now delegate to a single function-local `pushInvalidSkill(message,
hint)` closure. Behavior-preserving (identical pushed shape, messages,
hints, and `options?.invalidSkills` short-circuit).
- **`workspaceGoalService.ts` — dedupe session file path resolution.**
The three private accessors `getFilePath` / `getHistoryFilePath` /
`getBoardFilePath` each open-coded the same `assert(...non-empty
workspaceId)` guard and `path.join(getSessionDir(...), <FILE_CONST>)`.
All three now delegate to a single `resolveSessionFilePath(workspaceId,
fileName)` helper. Behavior-preserving (identical guard + join; accessor
names and callers unchanged).
- **`task.ts` — dedupe plan-agent explore-only error.** The plan-agent
restriction error `In the plan agent you may only spawn agentId:
"explore" tasks.` was open-coded twice (the workspace-turn guard and the
per-launch agent-id guard). Both now reference a single module-level
`PLAN_AGENT_EXPLORE_ONLY_ERROR` constant. Behavior-preserving (identical
string output).
- **`task_await.ts` — dedupe workspace-turn error fallback.** coder#3600's
new workspace-turn await paths open-coded the fallback error message
`"Workspace turn failed"` three times (`<record>.error ?? "Workspace
turn failed"`). All three now reference a single module-level
`WORKSPACE_TURN_DEFAULT_ERROR` constant. Behavior-preserving (identical
string output).
- **`goalToolUtils.tsx` / `SetGoalToolCall.tsx` — dedupe turn-count
pluralization.** coder#3595's new goal toolcards open-coded `` `${n} turn${n
=== 1 ? "" : "s"}` `` in both `formatGoalTurns` and the set_goal
toolcard's `formatOptionalTurnCap`. Both now call a shared
`pluralizeTurns(count)` helper. Behavior-preserving (identical string
output).
- **`heartbeat.ts` — dedupe heartbeat tool success result
construction.** coder#3596's new `heartbeat` tool open-coded the same `{
success: true, action, configured, settings, summary }` success object
three times (`get` / `set` / `unset`), each re-calling `summarize({
action, settings })`. All three now route through a single
`buildSuccessResult(action, settings)` helper that derives `configured`
from `settings`. Behavior-preserving: the per-branch `configured` values
(`settings != null`, `false`, `true`) all equal `settings != null` given
the non-null `Ok` payload type for `set`.
- **`taskService.ts` — dedupe startup recovery candidate listing.**
coder#3594's `TaskService.initialize` open-coded the `awaiting_report` /
`running` candidate filters twice (initial computation and
post-interrupt refresh). Both call sites now reuse a single
`listStartupRecoveryCandidates(sourceConfig)` closure returning `{
awaitingReportTasks, runningTasks }`, matching the existing
`listQueuedTasks` pattern. Behavior-preserving.
- **`ChatPane.tsx` — dedupe send-queued-immediately in-flight guard
clear.** coder#3592 introduced `sendQueuedImmediatelyInFlightRef` to block
duplicate send-now attempts; releasing that guard ("clear it only if it
still points at this attempt") was open-coded twice — once after a
failed interrupt result and once in the `catch` arm. Both call sites now
invoke a single callback-local closure `clearInFlightGuardIfCurrent()`.
Behavior-preserving: the failed-result site's combined
`!interruptResult.success && ref === id` becomes `if
(!interruptResult.success) clearInFlightGuardIfCurrent()` (logically
identical); the `catch` site is a verbatim substitution.
- **`WorkflowRunner.ts` — dedupe `parallelAgents` child-failure abort
handling** into a single batch-local closure
`applyChildFailureToBatch(error)`, shared by the per-run task closure
and the bulk-create path.
- **`ChatInput/index.tsx` — dedupe edit-mode attachment toast** string
`"Attachments cannot be added while editing a message."` into a single
module-level constant `EDIT_MODE_ATTACHMENT_ERROR_MESSAGE`, replacing
three inline copies across the add-attachment / paste / drop guards.

</details>

<details>
<summary>Prior passes (landed via coder#3559, merged)</summary>

- Replaced an inline terminal-status check in
`WorkflowService.interruptRunOnAbort` with the
`isTerminalWorkflowRunStatus` helper.
- Deduplicated the `workspaceId`-only metadata JSON Schema repeated by
three id-targeted host actions in `workspaceHostActions.ts` into
`WORKSPACE_ID_ONLY_INPUT_SCHEMA`.
- Extracted a shared `BlockedBadge` component in
`WorkflowDefinitionToolCall.tsx`.
- Deduplicated the repeated `argsSchema.properties must be an object`
error string in `workflowArgs.ts`.
- Deduplicated mocked-module specifier strings in
`useModelsFromSettings.test.ts`.
- Removed a redundant `toggleWorkflowExpanded` wrapper in
`WorkflowRunToolCall.tsx`.
- Deduplicated `SAVE_AUTOMATION_ERROR_MESSAGE` /
`REMOVE_AUTOMATION_ERROR_MESSAGE` in `AutomationModal.tsx`.
- Replaced a duplicated `"wfr_"` literal in `WorkflowRunner.ts` with
`isWorkflowRunTaskId()`.

</details>

## Validation

- `make static-check` — ESLint, both `tsgo` typecheck configs, and
Prettier all pass on the rebased tree. The only failing step is
`fmt-shell-check`, which requires `shfmt` (not installed in this
environment); no shell scripts were touched, so CI (which has `shfmt`)
is unaffected.
- `bun test src/node/services/agentSkills/agentSkillsService.test.ts` —
20 pass, 0 fail (covers skill discovery + the invalid-skill diagnostics
path this pass deduped); `builtInLoopSkill.test.ts` — 1 pass.

---

_Generated with `mux` • Model: `anthropic:claude-opus-4-8` • Thinking:
`xhigh`_

<!-- mux-attribution: model=anthropic:claude-opus-4-8 thinking=xhigh -->

---------

Co-authored-by: mux-bot[bot] <264182336+mux-bot[bot]@users.noreply.github.com>
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