@botiverse/raft-sdk 0.3.1 → 0.3.2

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.
@@ -11102,6 +11102,43 @@ function sortBySeq(messages) {
11102
11102
  function projectRaftMessages(envelopes) {
11103
11103
  return envelopes.map(projectRaftMessage).filter((m) => m !== null);
11104
11104
  }
11105
+ /** The conversation fields a target string names; `null` for a shape this cannot read. */
11106
+ function identityFromTarget(target) {
11107
+ const dm = /^dm:@([^:\s]+)(?::([^:\s]+))?$/.exec(target);
11108
+ if (dm) return dm[2] ? {
11109
+ channel_type: "thread",
11110
+ channel_name: dm[2],
11111
+ parent_channel_name: dm[1],
11112
+ parent_channel_type: "dm"
11113
+ } : {
11114
+ channel_type: "dm",
11115
+ channel_name: dm[1]
11116
+ };
11117
+ const channel = /^#([^:\s]+)(?::([^:\s]+))?$/.exec(target);
11118
+ if (channel) return channel[2] ? {
11119
+ channel_type: "thread",
11120
+ channel_name: channel[2],
11121
+ parent_channel_name: channel[1],
11122
+ parent_channel_type: "channel"
11123
+ } : {
11124
+ channel_type: "channel",
11125
+ channel_name: channel[1]
11126
+ };
11127
+ return null;
11128
+ }
11129
+ /**
11130
+ * Project envelopes that all belong to one known conversation. Some Server
11131
+ * responses (held previews) send envelopes without conversation fields
11132
+ * because the request already named the target; fill them from `target`
11133
+ * instead of dropping the message.
11134
+ */
11135
+ function projectRaftMessagesInTarget(envelopes, target) {
11136
+ const identity = identityFromTarget(target);
11137
+ return envelopes.map((envelope) => projectRaftMessage(identity && !hasAgentMessageIdentity(envelope) ? {
11138
+ ...envelope,
11139
+ ...identity
11140
+ } : envelope)).filter((m) => m !== null);
11141
+ }
11105
11142
  //#endregion
11106
11143
  //#region ../shared/src/agentOps/frontier.ts
11107
11144
  const MAX_EXACT_SEQS = 2500;
@@ -11157,10 +11194,11 @@ var SeenFrontier = class SeenFrontier {
11157
11194
  * Attest that the model saw the context a held send/claim returned. Call it
11158
11195
  * only after the held messages actually reached the model (the SDK cannot
11159
11196
  * know that, so it never records this implicitly). Returns false and records
11160
- * nothing when the context was withheld or the Server sent no boundary.
11197
+ * nothing when the context was withheld, not fully shown
11198
+ * (`contextComplete: false`), or the Server sent no boundary.
11161
11199
  */
11162
11200
  recordHeld(held) {
11163
- if (held.withheld || held.seenUpToSeq === null) return false;
11201
+ if (held.withheld || held.contextComplete === false || held.seenUpToSeq === null) return false;
11164
11202
  this.recordUpTo(held.target, held.seenUpToSeq);
11165
11203
  return true;
11166
11204
  }
@@ -11557,6 +11595,7 @@ function heldText(held, action) {
11557
11595
  if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
11558
11596
  if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
11559
11597
  if (held.heldMessages.length > 0) lines.push(formatAgentMessages(held.heldMessages.map((m) => toAgentMessageLike(m.raw))));
11598
+ if (!held.contextComplete) lines.push("Not all of them are shown here; read the conversation before sending again.");
11560
11599
  lines.push(`Full text: raft message read --target "${held.target}"`);
11561
11600
  return lines.join("\n");
11562
11601
  }
@@ -11568,26 +11607,30 @@ function heldNext(held) {
11568
11607
  target: held.target,
11569
11608
  ...held.continuation
11570
11609
  },
11571
- why: held.withheld ? "Newer messages exist in this conversation but were withheld; read them, then send again with the same idempotencyKey." : "Newer messages arrived in this conversation. Show the held messages to the model, call frontier.recordHeld(held) to attest that, then send again with these args (same idempotencyKey, seen.upToSeq)."
11610
+ why: held.withheld ? "Newer messages exist in this conversation but were withheld; read them, then send again with the same idempotencyKey." : !held.contextComplete ? "Newer messages arrived in this conversation and not all of them are shown here; read the conversation, then send again with the same idempotencyKey." : "Newer messages arrived in this conversation. Show the held messages to the model, call frontier.recordHeld(held) to attest that, then send again with these args (same idempotencyKey, seen.upToSeq)."
11572
11611
  };
11573
11612
  }
11574
11613
  function projectHeld(target, data, idempotencyKey) {
11575
11614
  const withheld = data.freshnessContextMode === "withheld";
11576
- const heldMessages = projectRaftMessages(data.heldMessages ?? []);
11615
+ const heldMessages = projectRaftMessagesInTarget(data.heldMessages ?? [], target);
11577
11616
  const seenUpToSeq = typeof data.seenUpToSeq === "number" ? data.seenUpToSeq : null;
11617
+ const newMessageCount = withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length;
11618
+ const omittedMessageCount = data.omittedMessageCount ?? 0;
11619
+ const contextComplete = !withheld && heldMessages.length + omittedMessageCount >= newMessageCount;
11578
11620
  return {
11579
11621
  target,
11580
11622
  idempotencyKey,
11581
11623
  continuation: {
11582
11624
  idempotencyKey,
11583
- ...!withheld && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11625
+ ...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11584
11626
  },
11585
- newMessageCount: withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length,
11627
+ newMessageCount,
11586
11628
  heldMessages,
11587
- omittedMessageCount: data.omittedMessageCount ?? 0,
11629
+ omittedMessageCount,
11588
11630
  formalMentionCount: data.mentionAnnotation?.formalMentionCount ?? 0,
11589
11631
  seenUpToSeq,
11590
- withheld
11632
+ withheld,
11633
+ contextComplete
11591
11634
  };
11592
11635
  }
11593
11636
  function isHeldResponse(data) {
@@ -11644,9 +11687,10 @@ async function sendMessage(client, request, frontier, internal = {}) {
11644
11687
  ...request,
11645
11688
  idempotencyKey
11646
11689
  };
11690
+ const base = projectHeld(request.target, data, idempotencyKey);
11647
11691
  const held = {
11648
- ...projectHeld(request.target, data, idempotencyKey),
11649
- resend: (options) => sendMessage(client, options.seen === "held" && typeof data.seenUpToSeq === "number" ? {
11692
+ ...base,
11693
+ resend: (options) => sendMessage(client, options.seen === "held" && base.contextComplete && typeof data.seenUpToSeq === "number" ? {
11650
11694
  ...keyed,
11651
11695
  seen: {
11652
11696
  upToSeq: data.seenUpToSeq,
package/dist/esm/index.js CHANGED
@@ -11101,6 +11101,43 @@ function sortBySeq(messages) {
11101
11101
  function projectRaftMessages(envelopes) {
11102
11102
  return envelopes.map(projectRaftMessage).filter((m) => m !== null);
11103
11103
  }
11104
+ /** The conversation fields a target string names; `null` for a shape this cannot read. */
11105
+ function identityFromTarget(target) {
11106
+ const dm = /^dm:@([^:\s]+)(?::([^:\s]+))?$/.exec(target);
11107
+ if (dm) return dm[2] ? {
11108
+ channel_type: "thread",
11109
+ channel_name: dm[2],
11110
+ parent_channel_name: dm[1],
11111
+ parent_channel_type: "dm"
11112
+ } : {
11113
+ channel_type: "dm",
11114
+ channel_name: dm[1]
11115
+ };
11116
+ const channel = /^#([^:\s]+)(?::([^:\s]+))?$/.exec(target);
11117
+ if (channel) return channel[2] ? {
11118
+ channel_type: "thread",
11119
+ channel_name: channel[2],
11120
+ parent_channel_name: channel[1],
11121
+ parent_channel_type: "channel"
11122
+ } : {
11123
+ channel_type: "channel",
11124
+ channel_name: channel[1]
11125
+ };
11126
+ return null;
11127
+ }
11128
+ /**
11129
+ * Project envelopes that all belong to one known conversation. Some Server
11130
+ * responses (held previews) send envelopes without conversation fields
11131
+ * because the request already named the target; fill them from `target`
11132
+ * instead of dropping the message.
11133
+ */
11134
+ function projectRaftMessagesInTarget(envelopes, target) {
11135
+ const identity = identityFromTarget(target);
11136
+ return envelopes.map((envelope) => projectRaftMessage(identity && !hasAgentMessageIdentity(envelope) ? {
11137
+ ...envelope,
11138
+ ...identity
11139
+ } : envelope)).filter((m) => m !== null);
11140
+ }
11104
11141
  //#endregion
11105
11142
  //#region ../shared/src/agentOps/frontier.ts
11106
11143
  const MAX_EXACT_SEQS = 2500;
@@ -11156,10 +11193,11 @@ var SeenFrontier = class SeenFrontier {
11156
11193
  * Attest that the model saw the context a held send/claim returned. Call it
11157
11194
  * only after the held messages actually reached the model (the SDK cannot
11158
11195
  * know that, so it never records this implicitly). Returns false and records
11159
- * nothing when the context was withheld or the Server sent no boundary.
11196
+ * nothing when the context was withheld, not fully shown
11197
+ * (`contextComplete: false`), or the Server sent no boundary.
11160
11198
  */
11161
11199
  recordHeld(held) {
11162
- if (held.withheld || held.seenUpToSeq === null) return false;
11200
+ if (held.withheld || held.contextComplete === false || held.seenUpToSeq === null) return false;
11163
11201
  this.recordUpTo(held.target, held.seenUpToSeq);
11164
11202
  return true;
11165
11203
  }
@@ -11556,6 +11594,7 @@ function heldText(held, action) {
11556
11594
  if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
11557
11595
  if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
11558
11596
  if (held.heldMessages.length > 0) lines.push(formatAgentMessages(held.heldMessages.map((m) => toAgentMessageLike(m.raw))));
11597
+ if (!held.contextComplete) lines.push("Not all of them are shown here; read the conversation before sending again.");
11559
11598
  lines.push(`Full text: raft message read --target "${held.target}"`);
11560
11599
  return lines.join("\n");
11561
11600
  }
@@ -11567,26 +11606,30 @@ function heldNext(held) {
11567
11606
  target: held.target,
11568
11607
  ...held.continuation
11569
11608
  },
11570
- why: held.withheld ? "Newer messages exist in this conversation but were withheld; read them, then send again with the same idempotencyKey." : "Newer messages arrived in this conversation. Show the held messages to the model, call frontier.recordHeld(held) to attest that, then send again with these args (same idempotencyKey, seen.upToSeq)."
11609
+ why: held.withheld ? "Newer messages exist in this conversation but were withheld; read them, then send again with the same idempotencyKey." : !held.contextComplete ? "Newer messages arrived in this conversation and not all of them are shown here; read the conversation, then send again with the same idempotencyKey." : "Newer messages arrived in this conversation. Show the held messages to the model, call frontier.recordHeld(held) to attest that, then send again with these args (same idempotencyKey, seen.upToSeq)."
11571
11610
  };
11572
11611
  }
11573
11612
  function projectHeld(target, data, idempotencyKey) {
11574
11613
  const withheld = data.freshnessContextMode === "withheld";
11575
- const heldMessages = projectRaftMessages(data.heldMessages ?? []);
11614
+ const heldMessages = projectRaftMessagesInTarget(data.heldMessages ?? [], target);
11576
11615
  const seenUpToSeq = typeof data.seenUpToSeq === "number" ? data.seenUpToSeq : null;
11616
+ const newMessageCount = withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length;
11617
+ const omittedMessageCount = data.omittedMessageCount ?? 0;
11618
+ const contextComplete = !withheld && heldMessages.length + omittedMessageCount >= newMessageCount;
11577
11619
  return {
11578
11620
  target,
11579
11621
  idempotencyKey,
11580
11622
  continuation: {
11581
11623
  idempotencyKey,
11582
- ...!withheld && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11624
+ ...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11583
11625
  },
11584
- newMessageCount: withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length,
11626
+ newMessageCount,
11585
11627
  heldMessages,
11586
- omittedMessageCount: data.omittedMessageCount ?? 0,
11628
+ omittedMessageCount,
11587
11629
  formalMentionCount: data.mentionAnnotation?.formalMentionCount ?? 0,
11588
11630
  seenUpToSeq,
11589
- withheld
11631
+ withheld,
11632
+ contextComplete
11590
11633
  };
11591
11634
  }
11592
11635
  function isHeldResponse(data) {
@@ -11643,9 +11686,10 @@ async function sendMessage(client, request, frontier, internal = {}) {
11643
11686
  ...request,
11644
11687
  idempotencyKey
11645
11688
  };
11689
+ const base = projectHeld(request.target, data, idempotencyKey);
11646
11690
  const held = {
11647
- ...projectHeld(request.target, data, idempotencyKey),
11648
- resend: (options) => sendMessage(client, options.seen === "held" && typeof data.seenUpToSeq === "number" ? {
11691
+ ...base,
11692
+ resend: (options) => sendMessage(client, options.seen === "held" && base.contextComplete && typeof data.seenUpToSeq === "number" ? {
11649
11693
  ...keyed,
11650
11694
  seen: {
11651
11695
  upToSeq: data.seenUpToSeq,
package/dist/index.d.ts CHANGED
@@ -457,12 +457,14 @@ export declare class SeenFrontier {
457
457
  * Attest that the model saw the context a held send/claim returned. Call it
458
458
  * only after the held messages actually reached the model (the SDK cannot
459
459
  * know that, so it never records this implicitly). Returns false and records
460
- * nothing when the context was withheld or the Server sent no boundary.
460
+ * nothing when the context was withheld, not fully shown
461
+ * (`contextComplete: false`), or the Server sent no boundary.
461
462
  */
462
463
  recordHeld(held: {
463
464
  target: string;
464
465
  seenUpToSeq: number | null;
465
466
  withheld: boolean;
467
+ contextComplete?: boolean;
466
468
  }): boolean;
467
469
  /** What to attest on a send or claim to `target`; omits `seenUpToSeq` when nothing contiguous is known. */
468
470
  attestation(target: string): SeenAttestation;
@@ -625,6 +627,13 @@ interface RaftHeldBase {
625
627
  seenUpToSeq: number | null;
626
628
  /** Reviewer isolation: bodies withheld, only a count. */
627
629
  withheld: boolean;
630
+ /**
631
+ * Whether `heldMessages` plus `omittedMessageCount` account for every one of
632
+ * `newMessageCount`. When false the model cannot have seen all of them, so
633
+ * no `seen` is offered and `frontier.recordHeld` records nothing; read the
634
+ * conversation instead.
635
+ */
636
+ contextComplete: boolean;
628
637
  }
629
638
  interface RaftHeld extends RaftHeldBase {
630
639
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "license": "FSL-1.1-ALv2",
5
5
  "description": "Typed Raft Agent API client for external agents and bots.",
6
6
  "type": "module",