Skip to content

fix(hub): mint a localId for user messages sent without one - #2069

Open
swear01 wants to merge 1 commit into
tiann:mainfrom
swear01:fix/hub-default-message-localid
Open

swear01 wants to merge 1 commit into
tiann:mainfrom
swear01:fix/hub-default-message-localid

Conversation

@swear01

@swear01 swear01 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #2068.

Result

When a POST /api/sessions/:id/messages request has no localId, the hub mints one. The stored row, the CLI delivery, and the Shared Codex echo now share one identity, so the existing (session_id, local_id) deduplication keeps one row per send instead of two.

This covers every REST sender, including peer messages (#1974), API scripts, and third-party clients. #2024 and #1436 fix their own senders. This change handles the senders that don't.

Behavior change: such sends now follow the normal queue/ack/cancel path, the same one typed web sends already use, instead of being stamped invoked at insert. Session-end sweeps still clear unacknowledged rows. Senders that provide a localId, scheduled sends, and agent-originated rows are unchanged. The client-contract docs are updated to match.

Existing duplicate rows are not removed.

Validation

  • New messageService regression test: two sends without localId deliver distinct ids to the CLI, and an agent echo under the delivered id adds no row. It fails without the change and passes with it.
  • bun typecheck and bun run test passed: all packages, including 1,390 hub tests.
  • Production evidence before the fix: each Codex message sent without localId was stored as a webapp row with localId: null, followed by an identical cli echo. Codex ran it once.

AI disclosure: Claude Code assisted with diagnosis, the change, and verification.

🤖 Generated with Claude Code

A REST send without localId was stored with local_id NULL. Shared Codex
then gave the turn a random clientUserMessageId and echoed the user
message back under it; the hub dedupes only on (session, localId), so
the echo became a second row and the message showed twice.

Mint a localId in MessageService.sendMessage when the request omits one,
so the stored row, the CLI delivery and the agent echo share an
identity. Such sends now take the normal queue/ack/cancel path, like
typed web sends.

Fixes tiann#2068

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions 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.

Review mode: initial

Requirement — Pass

REST sends without localId should produce one stored user message. The reported duplication is supported by the existing storage and Shared Codex echo paths.

Evidence

  • Issue #2068 describes duplicate rows for one send without localId.
  • cli/src/codex/shared/root.ts:118 generates a missing ID; projection.ts:108–114 preserves it in the echo, while hub/src/store/messages.ts:130 only deduplicates messages with matching localIds.

Approach — Pass

Minting the ID before insertion gives storage, CLI delivery, and the echo the same identity. Reusing the existing queue/ack lifecycle is appropriate, and the documentation explains the behavior change.

Evidence

  • hub/src/sync/messageService.ts:890 generates an ID only when one is absent; line 924 delivers the stored ID to the CLI.
  • hub/src/sync/messageService.ts:966–978 already settles immediate queued messages at session end.
  • docs/api/client-contract/pagination.md:153 and :173 document generated IDs and queue/ack semantics.

Code — Reviewed

Reviewed the entire fixed-head merge-base diff and relevant storage, delivery, acknowledgement, and echo handling. No actionable defects found. Verification was read-only; PR code was not executed.

No reportable code issues found.

Testing

Not run (automation; PR code execution is prohibited).

  • The added regression checks distinct generated IDs for separate sends and storage-level deduplication of an echo using the delivered ID.
  • The new test does not exercise a real Shared Codex round trip or generated-ID acknowledgement/cancellation transitions.
  • At inspection, fixed-head CI checks test, integration, and windows-codex-mcp were still running. The PR reports passing local typechecks and tests; those results were not independently verified.

HAPI Bot

@heavygee heavygee added bug Something isn't working area:hub Hub server (API, sync, store) community-pr PR from non-collaborator contributor labels Oct 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:hub Hub server (API, sync, store) bug Something isn't working community-pr PR from non-collaborator contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(hub): user messages sent without localId are stored twice in Shared Codex sessions

2 participants