@coder/ai-sdk-agent 0.11.8 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -196,6 +196,13 @@ interface ChatMessage {
196
196
  role: ChatMessageRole;
197
197
  content?: ChatMessagePart[];
198
198
  usage?: ChatMessageUsage;
199
+ /**
200
+ * The id of the queue entry ({@link ChatQueuedMessage.id}) this user
201
+ * message was promoted from. Set only on messages promoted out of the chat's
202
+ * queue (coder/coder#29859); omitted for direct sends, edits and synthetic
203
+ * rows, and by older servers that don't record it — absent means unknown.
204
+ */
205
+ queued_message_id?: number;
199
206
  }
200
207
  interface ChatErrorPayload {
201
208
  message: string;
@@ -1461,7 +1468,8 @@ interface TurnTranslatorOptions {
1461
1468
  * correlation protocol. `id`/`content` come from the response's
1462
1469
  * `queued_message`; either may be absent on unexpected wire shapes, which
1463
1470
  * degrades matching (no confirmation / match-any) but never widens
1464
- * attribution beyond the first observed user message.
1471
+ * attribution beyond the first observed user message. Without `id`, exact
1472
+ * `queued_message_id` anchoring is unavailable and the heuristic applies.
1465
1473
  */
1466
1474
  queuedSubmission?: {
1467
1475
  id?: number;
package/dist/index.js CHANGED
@@ -1865,7 +1865,9 @@ var TurnTranslator = class {
1865
1865
  if (!message) break;
1866
1866
  if (message.id > this.#maxMessageId) this.#maxMessageId = message.id;
1867
1867
  if (message.id <= this.#turnCursor) break;
1868
- if (message.role === "user" && this.#matchesQueuedContent(gate, message)) {
1868
+ const exact = this.#exactQueuedMatch(gate, message);
1869
+ if (exact === true) return this.#anchorQueuedTurn(message.id);
1870
+ if (exact === void 0 && message.role === "user" && this.#matchesQueuedContent(gate, message)) {
1869
1871
  if (gate.queuedId === void 0) return this.#anchorQueuedTurn(message.id);
1870
1872
  gate.candidateId = message.id;
1871
1873
  gate.buffered = [];
@@ -1910,6 +1912,19 @@ var TurnTranslator = class {
1910
1912
  }
1911
1913
  return [];
1912
1914
  }
1915
+ /**
1916
+ * The EXACT verdict for a snapshot (#119): `true`/`false` when it is a
1917
+ * user message stamped with `queued_message_id` and the submission response
1918
+ * gave us the entry id to compare; `undefined` (no exact evidence — use the
1919
+ * heuristic) otherwise. Only a numeric stamp counts, so an out-of-contract
1920
+ * `null` degrades to the heuristic instead of rejecting our own message.
1921
+ */
1922
+ #exactQueuedMatch(gate, message) {
1923
+ const stamp = message.queued_message_id;
1924
+ if (message.role !== "user" || typeof stamp !== "number") return void 0;
1925
+ if (gate.queuedId === void 0) return void 0;
1926
+ return stamp === gate.queuedId;
1927
+ }
1913
1928
  /** Whether a user-message snapshot's content matches the queued entry's. */
1914
1929
  #matchesQueuedContent(gate, message) {
1915
1930
  if (gate.expected === void 0) return true;
@@ -1921,6 +1936,11 @@ var TurnTranslator = class {
1921
1936
  * reuse the turn's ORIGINAL cursor) stay filtered by the advanced cursor.
1922
1937
  */
1923
1938
  #anchorQueuedTurn(anchorId) {
1939
+ if (anchorId <= this.#turnCursor) {
1940
+ throw new Error(
1941
+ `TurnTranslator invariant: queued anchor ${anchorId} is not past cursor ${this.#turnCursor}`
1942
+ );
1943
+ }
1924
1944
  this.#turnCursor = anchorId;
1925
1945
  this.#queuedGate = void 0;
1926
1946
  return [];