diff options
| author | Adam Malczewski <[email protected]> | 2026-06-29 12:13:10 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-29 12:13:10 +0900 |
| commit | 6861a10c30039b4086f11e2b5bfc515844d8a7b3 (patch) | |
| tree | 882e84589dac93524e3f6feea047f0ec3e4a5184 /.dispatch | |
| parent | c1082294dd98cadfecda58d854174610659cadec (diff) | |
| parent | 129bb3be45e1446ce4219d7f656ed0ed6f93a29f (diff) | |
| download | dispatch-web-6861a10c30039b4086f11e2b5bfc515844d8a7b3.tar.gz dispatch-web-6861a10c30039b4086f11e2b5bfc515844d8a7b3.zip | |
Merge branch 'feature/cancel-queued-message' into predev
# Conflicts:
# backend-handoff.md
Diffstat (limited to '.dispatch')
| -rw-r--r-- | .dispatch/transport-contract.reference.md | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md index 0582d94..70d64d9 100644 --- a/.dispatch/transport-contract.reference.md +++ b/.dispatch/transport-contract.reference.md @@ -5,9 +5,20 @@ > permission prompt). Your CODE still imports `@dispatch/transport-contract` normally — this file is for > READING only. > -> **Orchestrator:** SNAPSHOT of `[email protected]` (MCP status + computers + provider concurrency). Regenerate whenever +> **Orchestrator:** SNAPSHOT of `[email protected]` (MCP status + computers + provider concurrency + cancel queued message). Regenerate whenever > it changes. > +> **2026-06-29 update (cancel-queued-message — ADDITIVE, 0.23.0 → 0.24.0):** a per-message +> **cancel** for the steering message queue ships. While a turn is GENERATING and a user +> message is queued (awaiting steering delivery), the client can cancel a single queued +> message by id so it never runs. New `WsClientMessage` member `ChatQueueCancelMessage` +> (`{ type: "chat.queue.cancel"; conversationId; messageId }` — fire-and-forget, idempotent; +> success confirmed by the `message-queue` surface updating, failure as `chat.error`). New +> HTTP path `DELETE /conversations/:id/queue/:messageId` → `QueueCancelResponse` +> (`{ conversationId; cancelled: boolean; queue }`). The cancel is scoped per conversation; +> cancelling a drained/already-cancelled/unknown message is a silent no-op. `@dispatch/wire` +> is unchanged (`QueuedMessage.id` is the cancel target). +> > **2026-06-27 update (concurrency-fixes — ADDITIVE, NO version bump):** the provider concurrency surface gains > (a) a configurable + persisted per-provider release COOLDOWN, and (b) adaptive headroom. `ConcurrencyStatusEntry` > gains FOUR new fields: `cooldownMs: number` (REQUIRED — per-slot release cooldown in ms, default 350; a recycled slot is @@ -560,6 +571,27 @@ export interface QueueResponse { readonly queue: readonly QueuedMessage[]; } +/** + * Response body for + * `DELETE /conversations/:id/queue/:messageId` — cancel (remove) a single + * queued steering message by id so it never runs. + * + * `cancelled` is `true` when a message with the given id was found in the + * conversation's queue and removed (it will never be delivered as steering nor + * carried into a new turn). `cancelled` is `false` when the message was not in + * the queue (already drained/delivered, never existed, unknown conversation) + * OR when the message-queue extension isn't loaded (degraded — feature off). + * `queue` is the post-cancel snapshot (empty when no queue extension is + * loaded). Idempotent — cancelling a message that is no longer queued returns + * `cancelled: false` with HTTP 200 (not an error), so a client may optimistically + * fire-and-forget a cancel and reconcile from the surface. + */ +export interface QueueCancelResponse { + readonly conversationId: string; + readonly cancelled: boolean; + readonly queue: readonly QueuedMessage[]; +} + // ─── Per-conversation LSP status ────────────────────────────────────────────── /** The connection state of a single language server for a workspace. */ @@ -780,6 +812,24 @@ export interface ChatQueueMessage { } /** + * Client → server: cancel (remove) a SINGLE queued steering message by id so + * it never runs. The WebSocket counterpart of the HTTP + * `DELETE /conversations/:id/queue/:messageId` (`QueueCancelResponse`). + * Fire-and-forget: success is confirmed by the message-queue SURFACE updating + * (the cancelled message leaves the snapshot); a failure (missing/empty + * `conversationId` or `messageId`) arrives as a `chat.error`. Idempotent — + * cancelling a message that is no longer queued (already drained/delivered) is + * a silent no-op (no surface update, no error). `messageId` is the stable + * client-visible `QueuedMessage.id` (obtained from the queue surface snapshot + * or the enqueue response). + */ +export interface ChatQueueCancelMessage { + readonly type: "chat.queue.cancel"; + readonly conversationId: string; + readonly messageId: string; +} + +/** * Every client → server WS message: surface ops (`@dispatch/ui-contract`) + chat * ops. A server discriminates on `type`. */ @@ -788,7 +838,8 @@ export type WsClientMessage = | ChatSendMessage | ChatSubscribeMessage | ChatUnsubscribeMessage - | ChatQueueMessage; + | ChatQueueMessage + | ChatQueueCancelMessage; /** * Every server → client WS message: surface ops (`@dispatch/ui-contract`) + chat |
