@botiverse/raft-sdk 1.0.0-alpha.3 → 1.0.0-alpha.4

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/README.md CHANGED
@@ -58,6 +58,52 @@ export async function onStep(state: Stored) {
58
58
  await raft.messages.send(pending); // { target, content, idempotencyKey, seen: { upToSeq } }
59
59
  ```
60
60
 
61
+ ### Persisting state between tool calls (`state`)
62
+
63
+ If your runtime keeps nothing in memory between model steps, give the client a
64
+ store. The SDK loads it before the first operation and saves after each
65
+ successful operation that changed it; you implement two async methods.
66
+
67
+ ```ts
68
+ const raft = createRaft({ serverUrl, credential, state: store });
69
+
70
+ await raft.inbox.commit(); // the batch the previous call pulled is now processed
71
+ const batch = await raft.inbox.check(); // acknowledges it on the Server, returns the next batch
72
+ // … hand batch.data.messages to the model …
73
+ ```
74
+
75
+ - `inbox.check()` records the returned batch's cursor as **pending**; pulling
76
+ acknowledges nothing.
77
+ - `inbox.commit()` promotes the pending cursor to **committed** (also accepts
78
+ `{ cursor }`). The next `check()` sends it as `since`, which is what
79
+ acknowledges that batch. The SDK never commits on its own, so a call that
80
+ dies before `commit()` gets the same batch again.
81
+ - The seen frontier and held-send keys are saved too: resending the same
82
+ content to the same target after a hold reuses its idempotency key. After a
83
+ hold, `raft.frontier.recordHeld(held.data)` then `await raft.state.save()`.
84
+ - Saving is one attempt and never fails the operation; failures and stale
85
+ writes go to `onStateSaveError`. Losing the state is safe: at worst a batch
86
+ is delivered once more or a send is held once.
87
+
88
+ The state is one small versioned JSON value:
89
+ `{ schema: "raft-sdk-state.v1", version, cursor, pendingCursor, frontier, continuations }`.
90
+ `save(state, { expectedVersion })` receives the `version` this client loaded;
91
+ throw to reject a stale write, or ignore it if your store cannot compare.
92
+ An IndexedDB-style store with a synchronous transaction:
93
+
94
+ ```ts
95
+ const store: RaftStateStore = {
96
+ load: async () => (await db.get("inbox", "state")) ?? null,
97
+ save: async (state, { expectedVersion }) => {
98
+ await db.transaction("inbox", "readwrite", (tx) => {
99
+ const cur = tx.get("inbox", "state") as { version?: number } | undefined;
100
+ if (cur?.version !== expectedVersion) throw new Error("stale");
101
+ tx.put("inbox", state, "state");
102
+ });
103
+ },
104
+ };
105
+ ```
106
+
61
107
  A push notice is a content-free wake-up: verify it, then pull.
62
108
 
63
109
  ```ts
@@ -11044,13 +11044,17 @@ var SeenFrontier = class SeenFrontier {
11044
11044
  aliases = /* @__PURE__ */ new Map();
11045
11045
  static fromSnapshot(snapshot) {
11046
11046
  const frontier = new SeenFrontier();
11047
- if (!snapshot || snapshot.version !== 1) return frontier;
11047
+ frontier.absorb(snapshot);
11048
+ return frontier;
11049
+ }
11050
+ /** Merge a snapshot into this frontier by the same monotonic rules (never lowers a mark). */
11051
+ absorb(snapshot) {
11052
+ if (!snapshot || snapshot.version !== 1) return;
11053
+ for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) this.recordAlias(alias, canonical);
11048
11054
  for (const [target, state] of Object.entries(snapshot.targets ?? {})) {
11049
- if (positiveInt(state.upTo)) frontier.recordUpTo(target, state.upTo);
11050
- if (Array.isArray(state.exact)) frontier.recordExact(target, state.exact);
11055
+ if (positiveInt(state.upTo)) this.recordUpTo(target, state.upTo);
11056
+ if (Array.isArray(state.exact)) this.recordExact(target, state.exact);
11051
11057
  }
11052
- for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) frontier.recordAlias(alias, canonical);
11053
- return frontier;
11054
11058
  }
11055
11059
  /** Remember that the Server resolved `requested` to `canonical` (a thread spelling, a `~agent` suffix, …). */
11056
11060
  recordAlias(requested, canonical) {
@@ -11197,7 +11201,7 @@ async function checkInbox(client, request = {}, frontier) {
11197
11201
  * return value summarises the drain; the batch that ends the iteration is
11198
11202
  * acknowledged only by the caller's next pull (`summary.cursor`).
11199
11203
  */
11200
- async function* drainInbox(client, request = {}, frontier) {
11204
+ async function* drainInbox(client, request = {}, frontier, onPull) {
11201
11205
  const ack = request.ack ?? "cursor";
11202
11206
  let cursor = request.since ?? null;
11203
11207
  let ackMode = ack;
@@ -11220,6 +11224,7 @@ async function* drainInbox(client, request = {}, frontier) {
11220
11224
  error: round
11221
11225
  };
11222
11226
  const batch = round.data;
11227
+ await onPull?.(sent, batch);
11223
11228
  ackMode = batch.ackMode;
11224
11229
  stillUnread = batch.stillUnreadConversations;
11225
11230
  if (batch.messages.length > 0) {
@@ -11528,13 +11533,6 @@ async function sendMessage(client, request, frontier, internal = {}) {
11528
11533
  if (data.state === "sent") return sentOutcome(request.target, data);
11529
11534
  return failureOutcome(opError("INVALID_RESPONSE", { message: `Unexpected send state "${data.state}".` }));
11530
11535
  }
11531
- /** Reply where a received message came from. */
11532
- function replyTo(client, message, request, frontier) {
11533
- return sendMessage(client, {
11534
- ...request,
11535
- target: message.target
11536
- }, frontier);
11537
- }
11538
11536
  function taskTimestamps(t) {
11539
11537
  return `${t.createdAt ? ` created=${formatUtcTimestamp(t.createdAt)}` : ""}${t.updatedAt ? ` updated=${formatUtcTimestamp(t.updatedAt)}` : ""}`;
11540
11538
  }
@@ -13614,11 +13612,179 @@ async function searchManual(client, request) {
13614
13612
  };
13615
13613
  }
13616
13614
  //#endregion
13615
+ //#region ../shared/src/agentOps/state.ts
13616
+ const RAFT_STATE_SCHEMA = "raft-sdk-state.v1";
13617
+ const RAFT_STATE_CONTINUATIONS_PER_TARGET = 3;
13618
+ const RAFT_STATE_CONTINUATIONS_TOTAL = 20;
13619
+ function isNullableCursor(value) {
13620
+ return value === null || typeof value === "number" && Number.isInteger(value) && value >= 0;
13621
+ }
13622
+ /** Validate a loaded value; anything unrecognised is treated as an empty store (safe: see file header). */
13623
+ function parseRaftState(value) {
13624
+ if (!value || typeof value !== "object") return null;
13625
+ const v = value;
13626
+ if (v.schema !== "raft-sdk-state.v1") return null;
13627
+ if (typeof v.version !== "number" || !Number.isInteger(v.version) || v.version < 0) return null;
13628
+ if (!isNullableCursor(v.cursor ?? null) || !isNullableCursor(v.pendingCursor ?? null)) return null;
13629
+ const frontier = v.frontier;
13630
+ const continuations = Array.isArray(v.continuations) ? v.continuations.filter((c) => {
13631
+ const r = c;
13632
+ return Boolean(r) && typeof r.target === "string" && typeof r.idempotencyKey === "string" && typeof r.contentHash === "string" && typeof r.heldAt === "string";
13633
+ }) : [];
13634
+ return {
13635
+ schema: RAFT_STATE_SCHEMA,
13636
+ version: v.version,
13637
+ cursor: v.cursor ?? null,
13638
+ pendingCursor: v.pendingCursor ?? null,
13639
+ frontier: frontier && frontier.version === 1 ? frontier : {
13640
+ version: 1,
13641
+ targets: {},
13642
+ aliases: {}
13643
+ },
13644
+ continuations
13645
+ };
13646
+ }
13647
+ /** Hex SHA-256 identifying one logical message (WebCrypto; Workers-safe). */
13648
+ async function hashRaftSendContent(target, content, attachmentIds = []) {
13649
+ const bytes = new TextEncoder().encode(`${target}\n${[...attachmentIds].sort().join(",")}\n${content}`);
13650
+ return [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((b) => b.toString(16).padStart(2, "0")).join("");
13651
+ }
13652
+ /**
13653
+ * One client's view of the persisted state: loads once, holds the working
13654
+ * copy, saves on request. Also used without a store (memory only), so the
13655
+ * pending/committed cursor flow behaves the same with or without persistence.
13656
+ */
13657
+ var RaftStateSession = class {
13658
+ store;
13659
+ onError;
13660
+ frontier;
13661
+ cursor = null;
13662
+ pendingCursor = null;
13663
+ continuations = [];
13664
+ loadedVersion = void 0;
13665
+ loading = null;
13666
+ dirty = false;
13667
+ constructor(store, frontier, onError) {
13668
+ this.store = store;
13669
+ this.onError = onError;
13670
+ this.frontier = frontier;
13671
+ }
13672
+ get persistent() {
13673
+ return this.store !== void 0;
13674
+ }
13675
+ /** The version loaded from (or last saved to) the store; `undefined` before the first save of an empty store. */
13676
+ get version() {
13677
+ return this.loadedVersion;
13678
+ }
13679
+ /** Load once. A failed or unreadable load starts from an empty state (reported, not thrown). */
13680
+ ensureLoaded() {
13681
+ if (!this.store) return Promise.resolve();
13682
+ this.loading ??= (async () => {
13683
+ let raw = null;
13684
+ try {
13685
+ raw = await this.store.load();
13686
+ } catch (error) {
13687
+ this.onError?.(error, {
13688
+ phase: "load",
13689
+ version: void 0
13690
+ });
13691
+ return;
13692
+ }
13693
+ const state = parseRaftState(raw);
13694
+ if (!state) return;
13695
+ this.loadedVersion = state.version;
13696
+ this.cursor = state.cursor;
13697
+ this.pendingCursor = state.pendingCursor;
13698
+ this.frontier.absorb(state.frontier);
13699
+ this.continuations = state.continuations ?? [];
13700
+ })();
13701
+ return this.loading;
13702
+ }
13703
+ markDirty() {
13704
+ this.dirty = true;
13705
+ }
13706
+ /** Record the cursor of a batch just returned. Acknowledges nothing. */
13707
+ setPending(cursor) {
13708
+ if (cursor === null || cursor === this.pendingCursor) return;
13709
+ this.pendingCursor = cursor;
13710
+ this.dirty = true;
13711
+ }
13712
+ /**
13713
+ * Promote a cursor to committed. With no argument, commits `pendingCursor`.
13714
+ * Returns the committed cursor, or null when there was nothing to commit.
13715
+ * Never moves the committed cursor backwards.
13716
+ */
13717
+ commit(cursor) {
13718
+ const next = cursor === void 0 ? this.pendingCursor : cursor;
13719
+ if (next === null || next === void 0) return null;
13720
+ if (this.cursor === null || next > this.cursor) this.cursor = next;
13721
+ if (this.pendingCursor !== null && this.pendingCursor <= this.cursor) this.pendingCursor = null;
13722
+ this.dirty = true;
13723
+ return this.cursor;
13724
+ }
13725
+ findContinuation(target, contentHash) {
13726
+ return this.continuations.find((c) => c.target === target && c.contentHash === contentHash);
13727
+ }
13728
+ rememberContinuation(entry) {
13729
+ this.continuations = this.continuations.filter((c) => !(c.target === entry.target && c.contentHash === entry.contentHash));
13730
+ this.continuations.push(entry);
13731
+ const perTarget = this.continuations.filter((c) => c.target === entry.target);
13732
+ for (const drop of perTarget.slice(0, Math.max(0, perTarget.length - 3))) this.continuations.splice(this.continuations.indexOf(drop), 1);
13733
+ if (this.continuations.length > 20) this.continuations.splice(0, this.continuations.length - 20);
13734
+ this.dirty = true;
13735
+ }
13736
+ forgetContinuation(target, contentHash) {
13737
+ const before = this.continuations.length;
13738
+ this.continuations = this.continuations.filter((c) => !(c.target === target && c.contentHash === contentHash));
13739
+ if (this.continuations.length !== before) this.dirty = true;
13740
+ }
13741
+ snapshot(nextVersion = (this.loadedVersion ?? 0) + 1) {
13742
+ return {
13743
+ schema: RAFT_STATE_SCHEMA,
13744
+ version: nextVersion,
13745
+ cursor: this.cursor,
13746
+ pendingCursor: this.pendingCursor,
13747
+ frontier: this.frontier.snapshot(),
13748
+ continuations: [...this.continuations]
13749
+ };
13750
+ }
13751
+ /**
13752
+ * Save if anything changed (or `force`). One attempt; a failure is reported
13753
+ * and swallowed. Returns whether the save succeeded (true when nothing to save).
13754
+ */
13755
+ async save(options = {}) {
13756
+ if (!this.store) return true;
13757
+ if (!this.dirty && !options.force) return true;
13758
+ const expectedVersion = this.loadedVersion;
13759
+ const state = this.snapshot();
13760
+ try {
13761
+ await this.store.save(state, { expectedVersion });
13762
+ } catch (error) {
13763
+ this.onError?.(error, {
13764
+ phase: "save",
13765
+ version: expectedVersion
13766
+ });
13767
+ return false;
13768
+ }
13769
+ this.loadedVersion = state.version;
13770
+ this.dirty = false;
13771
+ return true;
13772
+ }
13773
+ };
13774
+ //#endregion
13617
13775
  //#region src/raft.ts
13618
13776
  function createRaft(options) {
13619
13777
  const serverUrl = requireServerUrl(options.serverUrl);
13620
13778
  const authorization = `Bearer ${requireAgentCredential(options.credential)}`;
13621
13779
  const frontier = SeenFrontier.fromSnapshot(options.frontier);
13780
+ const session = new RaftStateSession(options.state, frontier, options.onStateSaveError);
13781
+ /** Load before, run, then save if the operation succeeded and changed the state. */
13782
+ const withState = async (run) => {
13783
+ await session.ensureLoaded();
13784
+ const outcome = await run();
13785
+ if (outcome.ok) await session.save();
13786
+ return outcome;
13787
+ };
13622
13788
  const routes = createRaftRoutes({
13623
13789
  serverUrl,
13624
13790
  fetch: options.fetch,
@@ -13639,6 +13805,27 @@ function createRaft(options) {
13639
13805
  throttle: options.throttle,
13640
13806
  auth: { authorization }
13641
13807
  } });
13808
+ /**
13809
+ * Send through the state session: a resend of the same logical message
13810
+ * (same target, content, attachments) reuses the held send's idempotency key;
13811
+ * a hold is remembered; a successful send clears it.
13812
+ */
13813
+ const sendWithState = (request) => withState(async () => {
13814
+ const contentHash = await hashRaftSendContent(request.target, request.content ?? "", request.attachmentIds ?? []);
13815
+ const pending = request.idempotencyKey ? void 0 : session.findContinuation(request.target, contentHash);
13816
+ const outcome = await sendMessage(routes, pending ? {
13817
+ ...request,
13818
+ idempotencyKey: pending.idempotencyKey
13819
+ } : request, frontier);
13820
+ if (outcome.ok && outcome.state === "held") session.rememberContinuation({
13821
+ target: request.target,
13822
+ idempotencyKey: outcome.data.idempotencyKey,
13823
+ contentHash,
13824
+ heldAt: (/* @__PURE__ */ new Date()).toISOString()
13825
+ });
13826
+ else if (outcome.ok && outcome.state === "sent") session.forgetContinuation(request.target, contentHash);
13827
+ return outcome;
13828
+ });
13642
13829
  return {
13643
13830
  identity: { whoami: () => getRaftContext(routes) },
13644
13831
  wake: {
@@ -13650,14 +13837,53 @@ function createRaft(options) {
13650
13837
  }
13651
13838
  },
13652
13839
  inbox: {
13653
- check: (request) => checkInbox(inboxApi, request, frontier),
13654
- drain: (request) => drainInbox(inboxApi, request, frontier),
13840
+ check: (request = {}) => withState(async () => {
13841
+ const since = request.since ?? session.cursor ?? void 0;
13842
+ const outcome = await checkInbox(inboxApi, {
13843
+ ...request,
13844
+ ...since === void 0 ? {} : { since }
13845
+ }, frontier);
13846
+ if (outcome.ok) {
13847
+ session.markDirty();
13848
+ if (since !== void 0 && (request.ack ?? "cursor") === "cursor") session.commit(since);
13849
+ if (outcome.data.ackMode === "cursor" && outcome.data.messages.length > 0) session.setPending(outcome.data.cursor);
13850
+ }
13851
+ return outcome;
13852
+ }),
13853
+ commit: async (target) => {
13854
+ await session.ensureLoaded();
13855
+ const cursor = session.commit(target === void 0 ? void 0 : target.cursor);
13856
+ return {
13857
+ cursor,
13858
+ saved: cursor === null ? true : await session.save()
13859
+ };
13860
+ },
13861
+ drain: (request = {}) => (async function* () {
13862
+ await session.ensureLoaded();
13863
+ const since = request.since ?? session.cursor ?? void 0;
13864
+ return yield* drainInbox(inboxApi, {
13865
+ ...request,
13866
+ ...since === void 0 ? {} : { since }
13867
+ }, frontier, async (sent, batch) => {
13868
+ if (sent !== null) session.commit(sent);
13869
+ if (batch.ackMode === "cursor" && batch.messages.length > 0) session.setPending(batch.cursor);
13870
+ session.markDirty();
13871
+ await session.save();
13872
+ });
13873
+ })(),
13655
13874
  list: (request) => listInbox(routes, request)
13656
13875
  },
13657
13876
  messages: {
13658
- read: (request) => readHistory(routes, request, frontier),
13659
- send: (request) => sendMessage(routes, request, frontier),
13660
- reply: (message, request) => replyTo(routes, message, request, frontier),
13877
+ read: (request) => withState(async () => {
13878
+ const outcome = await readHistory(routes, request, frontier);
13879
+ if (outcome.ok) session.markDirty();
13880
+ return outcome;
13881
+ }),
13882
+ send: (request) => sendWithState(request),
13883
+ reply: (message, request) => sendWithState({
13884
+ ...request,
13885
+ target: message.target
13886
+ }),
13661
13887
  search: (request) => searchMessages(routes, request),
13662
13888
  resolve: (request) => resolveMessage(routes, request),
13663
13889
  react: (request) => reactToMessage(routes, request, "add"),
@@ -13711,6 +13937,15 @@ function createRaft(options) {
13711
13937
  update: (request) => updateProfile(routes, request)
13712
13938
  },
13713
13939
  frontier,
13940
+ state: {
13941
+ save: async () => {
13942
+ await session.ensureLoaded();
13943
+ session.markDirty();
13944
+ return session.save();
13945
+ },
13946
+ load: () => session.ensureLoaded(),
13947
+ snapshot: () => session.snapshot(session.version ?? 0)
13948
+ },
13714
13949
  routes
13715
13950
  };
13716
13951
  }
@@ -13718,6 +13953,9 @@ function createRaft(options) {
13718
13953
  exports.RAFT_INBOX_NOTICE_SCHEMA = RAFT_INBOX_NOTICE_SCHEMA;
13719
13954
  exports.RAFT_NOTICE_DELIVERY_ID_HEADER = RAFT_NOTICE_DELIVERY_ID_HEADER;
13720
13955
  exports.RAFT_NOTICE_SIGNATURE_HEADER = RAFT_NOTICE_SIGNATURE_HEADER;
13956
+ exports.RAFT_STATE_CONTINUATIONS_PER_TARGET = RAFT_STATE_CONTINUATIONS_PER_TARGET;
13957
+ exports.RAFT_STATE_CONTINUATIONS_TOTAL = RAFT_STATE_CONTINUATIONS_TOTAL;
13958
+ exports.RAFT_STATE_SCHEMA = RAFT_STATE_SCHEMA;
13721
13959
  exports.RaftCredentialError = RaftCredentialError;
13722
13960
  exports.RaftSdkConfigurationError = RaftSdkConfigurationError;
13723
13961
  exports.SeenFrontier = SeenFrontier;
@@ -13729,10 +13967,12 @@ exports.createRaftClientFromStore = createRaftClientFromStore;
13729
13967
  exports.createRaftRoutes = createRaftRoutes;
13730
13968
  exports.describeRaftRoute = describeRaftRoute;
13731
13969
  exports.hasAgentMessageIdentity = hasAgentMessageIdentity;
13970
+ exports.hashRaftSendContent = hashRaftSendContent;
13732
13971
  exports.inferAttachmentMimeType = inferAttachmentMimeType;
13733
13972
  exports.joinRaftChannelByTarget = joinRaftChannelByTarget;
13734
13973
  exports.listRaftRoutes = listRaftRoutes;
13735
13974
  exports.parseRaftRegularChannelTarget = parseRaftRegularChannelTarget;
13975
+ exports.parseRaftState = parseRaftState;
13736
13976
  exports.projectRaftMessage = projectRaftMessage;
13737
13977
  exports.projectRaftMessages = projectRaftMessages;
13738
13978
  exports.verifyInboxNotice = verifyInboxNotice;
package/dist/esm/index.js CHANGED
@@ -11043,13 +11043,17 @@ var SeenFrontier = class SeenFrontier {
11043
11043
  aliases = /* @__PURE__ */ new Map();
11044
11044
  static fromSnapshot(snapshot) {
11045
11045
  const frontier = new SeenFrontier();
11046
- if (!snapshot || snapshot.version !== 1) return frontier;
11046
+ frontier.absorb(snapshot);
11047
+ return frontier;
11048
+ }
11049
+ /** Merge a snapshot into this frontier by the same monotonic rules (never lowers a mark). */
11050
+ absorb(snapshot) {
11051
+ if (!snapshot || snapshot.version !== 1) return;
11052
+ for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) this.recordAlias(alias, canonical);
11047
11053
  for (const [target, state] of Object.entries(snapshot.targets ?? {})) {
11048
- if (positiveInt(state.upTo)) frontier.recordUpTo(target, state.upTo);
11049
- if (Array.isArray(state.exact)) frontier.recordExact(target, state.exact);
11054
+ if (positiveInt(state.upTo)) this.recordUpTo(target, state.upTo);
11055
+ if (Array.isArray(state.exact)) this.recordExact(target, state.exact);
11050
11056
  }
11051
- for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) frontier.recordAlias(alias, canonical);
11052
- return frontier;
11053
11057
  }
11054
11058
  /** Remember that the Server resolved `requested` to `canonical` (a thread spelling, a `~agent` suffix, …). */
11055
11059
  recordAlias(requested, canonical) {
@@ -11196,7 +11200,7 @@ async function checkInbox(client, request = {}, frontier) {
11196
11200
  * return value summarises the drain; the batch that ends the iteration is
11197
11201
  * acknowledged only by the caller's next pull (`summary.cursor`).
11198
11202
  */
11199
- async function* drainInbox(client, request = {}, frontier) {
11203
+ async function* drainInbox(client, request = {}, frontier, onPull) {
11200
11204
  const ack = request.ack ?? "cursor";
11201
11205
  let cursor = request.since ?? null;
11202
11206
  let ackMode = ack;
@@ -11219,6 +11223,7 @@ async function* drainInbox(client, request = {}, frontier) {
11219
11223
  error: round
11220
11224
  };
11221
11225
  const batch = round.data;
11226
+ await onPull?.(sent, batch);
11222
11227
  ackMode = batch.ackMode;
11223
11228
  stillUnread = batch.stillUnreadConversations;
11224
11229
  if (batch.messages.length > 0) {
@@ -11527,13 +11532,6 @@ async function sendMessage(client, request, frontier, internal = {}) {
11527
11532
  if (data.state === "sent") return sentOutcome(request.target, data);
11528
11533
  return failureOutcome(opError("INVALID_RESPONSE", { message: `Unexpected send state "${data.state}".` }));
11529
11534
  }
11530
- /** Reply where a received message came from. */
11531
- function replyTo(client, message, request, frontier) {
11532
- return sendMessage(client, {
11533
- ...request,
11534
- target: message.target
11535
- }, frontier);
11536
- }
11537
11535
  function taskTimestamps(t) {
11538
11536
  return `${t.createdAt ? ` created=${formatUtcTimestamp(t.createdAt)}` : ""}${t.updatedAt ? ` updated=${formatUtcTimestamp(t.updatedAt)}` : ""}`;
11539
11537
  }
@@ -13613,11 +13611,179 @@ async function searchManual(client, request) {
13613
13611
  };
13614
13612
  }
13615
13613
  //#endregion
13614
+ //#region ../shared/src/agentOps/state.ts
13615
+ const RAFT_STATE_SCHEMA = "raft-sdk-state.v1";
13616
+ const RAFT_STATE_CONTINUATIONS_PER_TARGET = 3;
13617
+ const RAFT_STATE_CONTINUATIONS_TOTAL = 20;
13618
+ function isNullableCursor(value) {
13619
+ return value === null || typeof value === "number" && Number.isInteger(value) && value >= 0;
13620
+ }
13621
+ /** Validate a loaded value; anything unrecognised is treated as an empty store (safe: see file header). */
13622
+ function parseRaftState(value) {
13623
+ if (!value || typeof value !== "object") return null;
13624
+ const v = value;
13625
+ if (v.schema !== "raft-sdk-state.v1") return null;
13626
+ if (typeof v.version !== "number" || !Number.isInteger(v.version) || v.version < 0) return null;
13627
+ if (!isNullableCursor(v.cursor ?? null) || !isNullableCursor(v.pendingCursor ?? null)) return null;
13628
+ const frontier = v.frontier;
13629
+ const continuations = Array.isArray(v.continuations) ? v.continuations.filter((c) => {
13630
+ const r = c;
13631
+ return Boolean(r) && typeof r.target === "string" && typeof r.idempotencyKey === "string" && typeof r.contentHash === "string" && typeof r.heldAt === "string";
13632
+ }) : [];
13633
+ return {
13634
+ schema: RAFT_STATE_SCHEMA,
13635
+ version: v.version,
13636
+ cursor: v.cursor ?? null,
13637
+ pendingCursor: v.pendingCursor ?? null,
13638
+ frontier: frontier && frontier.version === 1 ? frontier : {
13639
+ version: 1,
13640
+ targets: {},
13641
+ aliases: {}
13642
+ },
13643
+ continuations
13644
+ };
13645
+ }
13646
+ /** Hex SHA-256 identifying one logical message (WebCrypto; Workers-safe). */
13647
+ async function hashRaftSendContent(target, content, attachmentIds = []) {
13648
+ const bytes = new TextEncoder().encode(`${target}\n${[...attachmentIds].sort().join(",")}\n${content}`);
13649
+ return [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((b) => b.toString(16).padStart(2, "0")).join("");
13650
+ }
13651
+ /**
13652
+ * One client's view of the persisted state: loads once, holds the working
13653
+ * copy, saves on request. Also used without a store (memory only), so the
13654
+ * pending/committed cursor flow behaves the same with or without persistence.
13655
+ */
13656
+ var RaftStateSession = class {
13657
+ store;
13658
+ onError;
13659
+ frontier;
13660
+ cursor = null;
13661
+ pendingCursor = null;
13662
+ continuations = [];
13663
+ loadedVersion = void 0;
13664
+ loading = null;
13665
+ dirty = false;
13666
+ constructor(store, frontier, onError) {
13667
+ this.store = store;
13668
+ this.onError = onError;
13669
+ this.frontier = frontier;
13670
+ }
13671
+ get persistent() {
13672
+ return this.store !== void 0;
13673
+ }
13674
+ /** The version loaded from (or last saved to) the store; `undefined` before the first save of an empty store. */
13675
+ get version() {
13676
+ return this.loadedVersion;
13677
+ }
13678
+ /** Load once. A failed or unreadable load starts from an empty state (reported, not thrown). */
13679
+ ensureLoaded() {
13680
+ if (!this.store) return Promise.resolve();
13681
+ this.loading ??= (async () => {
13682
+ let raw = null;
13683
+ try {
13684
+ raw = await this.store.load();
13685
+ } catch (error) {
13686
+ this.onError?.(error, {
13687
+ phase: "load",
13688
+ version: void 0
13689
+ });
13690
+ return;
13691
+ }
13692
+ const state = parseRaftState(raw);
13693
+ if (!state) return;
13694
+ this.loadedVersion = state.version;
13695
+ this.cursor = state.cursor;
13696
+ this.pendingCursor = state.pendingCursor;
13697
+ this.frontier.absorb(state.frontier);
13698
+ this.continuations = state.continuations ?? [];
13699
+ })();
13700
+ return this.loading;
13701
+ }
13702
+ markDirty() {
13703
+ this.dirty = true;
13704
+ }
13705
+ /** Record the cursor of a batch just returned. Acknowledges nothing. */
13706
+ setPending(cursor) {
13707
+ if (cursor === null || cursor === this.pendingCursor) return;
13708
+ this.pendingCursor = cursor;
13709
+ this.dirty = true;
13710
+ }
13711
+ /**
13712
+ * Promote a cursor to committed. With no argument, commits `pendingCursor`.
13713
+ * Returns the committed cursor, or null when there was nothing to commit.
13714
+ * Never moves the committed cursor backwards.
13715
+ */
13716
+ commit(cursor) {
13717
+ const next = cursor === void 0 ? this.pendingCursor : cursor;
13718
+ if (next === null || next === void 0) return null;
13719
+ if (this.cursor === null || next > this.cursor) this.cursor = next;
13720
+ if (this.pendingCursor !== null && this.pendingCursor <= this.cursor) this.pendingCursor = null;
13721
+ this.dirty = true;
13722
+ return this.cursor;
13723
+ }
13724
+ findContinuation(target, contentHash) {
13725
+ return this.continuations.find((c) => c.target === target && c.contentHash === contentHash);
13726
+ }
13727
+ rememberContinuation(entry) {
13728
+ this.continuations = this.continuations.filter((c) => !(c.target === entry.target && c.contentHash === entry.contentHash));
13729
+ this.continuations.push(entry);
13730
+ const perTarget = this.continuations.filter((c) => c.target === entry.target);
13731
+ for (const drop of perTarget.slice(0, Math.max(0, perTarget.length - 3))) this.continuations.splice(this.continuations.indexOf(drop), 1);
13732
+ if (this.continuations.length > 20) this.continuations.splice(0, this.continuations.length - 20);
13733
+ this.dirty = true;
13734
+ }
13735
+ forgetContinuation(target, contentHash) {
13736
+ const before = this.continuations.length;
13737
+ this.continuations = this.continuations.filter((c) => !(c.target === target && c.contentHash === contentHash));
13738
+ if (this.continuations.length !== before) this.dirty = true;
13739
+ }
13740
+ snapshot(nextVersion = (this.loadedVersion ?? 0) + 1) {
13741
+ return {
13742
+ schema: RAFT_STATE_SCHEMA,
13743
+ version: nextVersion,
13744
+ cursor: this.cursor,
13745
+ pendingCursor: this.pendingCursor,
13746
+ frontier: this.frontier.snapshot(),
13747
+ continuations: [...this.continuations]
13748
+ };
13749
+ }
13750
+ /**
13751
+ * Save if anything changed (or `force`). One attempt; a failure is reported
13752
+ * and swallowed. Returns whether the save succeeded (true when nothing to save).
13753
+ */
13754
+ async save(options = {}) {
13755
+ if (!this.store) return true;
13756
+ if (!this.dirty && !options.force) return true;
13757
+ const expectedVersion = this.loadedVersion;
13758
+ const state = this.snapshot();
13759
+ try {
13760
+ await this.store.save(state, { expectedVersion });
13761
+ } catch (error) {
13762
+ this.onError?.(error, {
13763
+ phase: "save",
13764
+ version: expectedVersion
13765
+ });
13766
+ return false;
13767
+ }
13768
+ this.loadedVersion = state.version;
13769
+ this.dirty = false;
13770
+ return true;
13771
+ }
13772
+ };
13773
+ //#endregion
13616
13774
  //#region src/raft.ts
13617
13775
  function createRaft(options) {
13618
13776
  const serverUrl = requireServerUrl(options.serverUrl);
13619
13777
  const authorization = `Bearer ${requireAgentCredential(options.credential)}`;
13620
13778
  const frontier = SeenFrontier.fromSnapshot(options.frontier);
13779
+ const session = new RaftStateSession(options.state, frontier, options.onStateSaveError);
13780
+ /** Load before, run, then save if the operation succeeded and changed the state. */
13781
+ const withState = async (run) => {
13782
+ await session.ensureLoaded();
13783
+ const outcome = await run();
13784
+ if (outcome.ok) await session.save();
13785
+ return outcome;
13786
+ };
13621
13787
  const routes = createRaftRoutes({
13622
13788
  serverUrl,
13623
13789
  fetch: options.fetch,
@@ -13638,6 +13804,27 @@ function createRaft(options) {
13638
13804
  throttle: options.throttle,
13639
13805
  auth: { authorization }
13640
13806
  } });
13807
+ /**
13808
+ * Send through the state session: a resend of the same logical message
13809
+ * (same target, content, attachments) reuses the held send's idempotency key;
13810
+ * a hold is remembered; a successful send clears it.
13811
+ */
13812
+ const sendWithState = (request) => withState(async () => {
13813
+ const contentHash = await hashRaftSendContent(request.target, request.content ?? "", request.attachmentIds ?? []);
13814
+ const pending = request.idempotencyKey ? void 0 : session.findContinuation(request.target, contentHash);
13815
+ const outcome = await sendMessage(routes, pending ? {
13816
+ ...request,
13817
+ idempotencyKey: pending.idempotencyKey
13818
+ } : request, frontier);
13819
+ if (outcome.ok && outcome.state === "held") session.rememberContinuation({
13820
+ target: request.target,
13821
+ idempotencyKey: outcome.data.idempotencyKey,
13822
+ contentHash,
13823
+ heldAt: (/* @__PURE__ */ new Date()).toISOString()
13824
+ });
13825
+ else if (outcome.ok && outcome.state === "sent") session.forgetContinuation(request.target, contentHash);
13826
+ return outcome;
13827
+ });
13641
13828
  return {
13642
13829
  identity: { whoami: () => getRaftContext(routes) },
13643
13830
  wake: {
@@ -13649,14 +13836,53 @@ function createRaft(options) {
13649
13836
  }
13650
13837
  },
13651
13838
  inbox: {
13652
- check: (request) => checkInbox(inboxApi, request, frontier),
13653
- drain: (request) => drainInbox(inboxApi, request, frontier),
13839
+ check: (request = {}) => withState(async () => {
13840
+ const since = request.since ?? session.cursor ?? void 0;
13841
+ const outcome = await checkInbox(inboxApi, {
13842
+ ...request,
13843
+ ...since === void 0 ? {} : { since }
13844
+ }, frontier);
13845
+ if (outcome.ok) {
13846
+ session.markDirty();
13847
+ if (since !== void 0 && (request.ack ?? "cursor") === "cursor") session.commit(since);
13848
+ if (outcome.data.ackMode === "cursor" && outcome.data.messages.length > 0) session.setPending(outcome.data.cursor);
13849
+ }
13850
+ return outcome;
13851
+ }),
13852
+ commit: async (target) => {
13853
+ await session.ensureLoaded();
13854
+ const cursor = session.commit(target === void 0 ? void 0 : target.cursor);
13855
+ return {
13856
+ cursor,
13857
+ saved: cursor === null ? true : await session.save()
13858
+ };
13859
+ },
13860
+ drain: (request = {}) => (async function* () {
13861
+ await session.ensureLoaded();
13862
+ const since = request.since ?? session.cursor ?? void 0;
13863
+ return yield* drainInbox(inboxApi, {
13864
+ ...request,
13865
+ ...since === void 0 ? {} : { since }
13866
+ }, frontier, async (sent, batch) => {
13867
+ if (sent !== null) session.commit(sent);
13868
+ if (batch.ackMode === "cursor" && batch.messages.length > 0) session.setPending(batch.cursor);
13869
+ session.markDirty();
13870
+ await session.save();
13871
+ });
13872
+ })(),
13654
13873
  list: (request) => listInbox(routes, request)
13655
13874
  },
13656
13875
  messages: {
13657
- read: (request) => readHistory(routes, request, frontier),
13658
- send: (request) => sendMessage(routes, request, frontier),
13659
- reply: (message, request) => replyTo(routes, message, request, frontier),
13876
+ read: (request) => withState(async () => {
13877
+ const outcome = await readHistory(routes, request, frontier);
13878
+ if (outcome.ok) session.markDirty();
13879
+ return outcome;
13880
+ }),
13881
+ send: (request) => sendWithState(request),
13882
+ reply: (message, request) => sendWithState({
13883
+ ...request,
13884
+ target: message.target
13885
+ }),
13660
13886
  search: (request) => searchMessages(routes, request),
13661
13887
  resolve: (request) => resolveMessage(routes, request),
13662
13888
  react: (request) => reactToMessage(routes, request, "add"),
@@ -13710,8 +13936,17 @@ function createRaft(options) {
13710
13936
  update: (request) => updateProfile(routes, request)
13711
13937
  },
13712
13938
  frontier,
13939
+ state: {
13940
+ save: async () => {
13941
+ await session.ensureLoaded();
13942
+ session.markDirty();
13943
+ return session.save();
13944
+ },
13945
+ load: () => session.ensureLoaded(),
13946
+ snapshot: () => session.snapshot(session.version ?? 0)
13947
+ },
13713
13948
  routes
13714
13949
  };
13715
13950
  }
13716
13951
  //#endregion
13717
- export { RAFT_INBOX_NOTICE_SCHEMA, RAFT_NOTICE_DELIVERY_ID_HEADER, RAFT_NOTICE_SIGNATURE_HEADER, RaftCredentialError, RaftSdkConfigurationError, SeenFrontier, bootstrapRaftCredential, createFileCredentialStore, createRaft, createRaftClient, createRaftClientFromStore, createRaftRoutes, describeRaftRoute, hasAgentMessageIdentity, inferAttachmentMimeType, joinRaftChannelByTarget, listRaftRoutes, parseRaftRegularChannelTarget, projectRaftMessage, projectRaftMessages, verifyInboxNotice };
13952
+ export { RAFT_INBOX_NOTICE_SCHEMA, RAFT_NOTICE_DELIVERY_ID_HEADER, RAFT_NOTICE_SIGNATURE_HEADER, RAFT_STATE_CONTINUATIONS_PER_TARGET, RAFT_STATE_CONTINUATIONS_TOTAL, RAFT_STATE_SCHEMA, RaftCredentialError, RaftSdkConfigurationError, SeenFrontier, bootstrapRaftCredential, createFileCredentialStore, createRaft, createRaftClient, createRaftClientFromStore, createRaftRoutes, describeRaftRoute, hasAgentMessageIdentity, hashRaftSendContent, inferAttachmentMimeType, joinRaftChannelByTarget, listRaftRoutes, parseRaftRegularChannelTarget, parseRaftState, projectRaftMessage, projectRaftMessages, verifyInboxNotice };
package/dist/index.d.ts CHANGED
@@ -444,6 +444,8 @@ export declare class SeenFrontier {
444
444
  private readonly targets;
445
445
  private readonly aliases;
446
446
  static fromSnapshot(snapshot: SeenFrontierSnapshot | null | undefined): SeenFrontier;
447
+ /** Merge a snapshot into this frontier by the same monotonic rules (never lowers a mark). */
448
+ absorb(snapshot: SeenFrontierSnapshot | null | undefined): void;
447
449
  /** Remember that the Server resolved `requested` to `canonical` (a thread spelling, a `~agent` suffix, …). */
448
450
  recordAlias(requested: string, canonical: string): void;
449
451
  canonical(target: string): string;
@@ -935,6 +937,49 @@ interface ManualContext {
935
937
  reason: string;
936
938
  }
937
939
  //#endregion
940
+ //#region ../shared/src/agentOps/state.d.ts
941
+ export declare const RAFT_STATE_SCHEMA: "raft-sdk-state.v1";
942
+ interface RaftStateContinuation {
943
+ target: string;
944
+ idempotencyKey: string;
945
+ /** Hex SHA-256 of target, content and attachment ids; a resend with the same hash is the same logical message. */
946
+ contentHash: string;
947
+ /** ISO time the send was held. */
948
+ heldAt: string;
949
+ }
950
+ interface RaftState {
951
+ schema: typeof RAFT_STATE_SCHEMA;
952
+ /** Revision, incremented on every save by this SDK. Compare-and-set stores compare against it. */
953
+ version: number;
954
+ /** Last committed inbox cursor; the next pull passes it as `since`. */
955
+ cursor: number | null;
956
+ /** Cursor of the batch returned but not yet committed. */
957
+ pendingCursor: number | null;
958
+ frontier: SeenFrontierSnapshot;
959
+ continuations?: RaftStateContinuation[];
960
+ }
961
+ interface RaftStateStore {
962
+ load(): Promise<RaftState | null>;
963
+ /**
964
+ * Persist `state`. `expectedVersion` is the version this client loaded
965
+ * (`undefined` when the store was empty). Throw to reject a stale write;
966
+ * stores without compare-and-set may ignore it.
967
+ */
968
+ save(state: RaftState, options: {
969
+ expectedVersion: number | undefined;
970
+ }): Promise<void>;
971
+ }
972
+ type RaftStateSaveErrorHandler = (error: unknown, context: {
973
+ phase: "load" | "save";
974
+ version: number | undefined;
975
+ }) => void;
976
+ export declare const RAFT_STATE_CONTINUATIONS_PER_TARGET = 3;
977
+ export declare const RAFT_STATE_CONTINUATIONS_TOTAL = 20;
978
+ /** Validate a loaded value; anything unrecognised is treated as an empty store (safe: see file header). */
979
+ export declare function parseRaftState(value: unknown): RaftState | null;
980
+ /** Hex SHA-256 identifying one logical message (WebCrypto; Workers-safe). */
981
+ export declare function hashRaftSendContent(target: string, content: string, attachmentIds?: readonly string[]): Promise<string>;
982
+ //#endregion
938
983
  //#region ../shared/src/agentApiRawClient.d.ts
939
984
  type AgentApiRawClientErrorReason = "missing_route" | "missing_path_param" | "request_contract_mismatch" | "transport_error" | "http_error" | "empty_response" | "response_contract_mismatch";
940
985
  interface AgentApiRawTransportRequest<K extends AgentApiRouteKey = AgentApiRouteKey> {
@@ -9299,6 +9344,22 @@ interface CreateRaftOptions {
9299
9344
  * is held once and returns the unread context, which is the safe default.
9300
9345
  */
9301
9346
  frontier?: SeenFrontierSnapshot | null;
9347
+ /**
9348
+ * Persist the client's state (committed and pending inbox cursors, the seen
9349
+ * frontier, held-send keys) across tool calls and processes. The store
9350
+ * implements `load()` and `save(state, { expectedVersion })`; the SDK loads
9351
+ * once before the first operation and saves after each successful operation
9352
+ * that changed the state (one attempt, never fails the operation).
9353
+ */
9354
+ state?: RaftStateStore;
9355
+ /** Called when loading or saving the state fails or is rejected as stale. */
9356
+ onStateSaveError?: RaftStateSaveErrorHandler;
9357
+ }
9358
+ interface RaftInboxCommitResult {
9359
+ /** The committed cursor, or null when there was nothing to commit. */
9360
+ cursor: number | null;
9361
+ /** Whether the state was persisted (true when no store is configured). */
9362
+ saved: boolean;
9302
9363
  }
9303
9364
  interface Raft {
9304
9365
  identity: {
@@ -9320,8 +9381,22 @@ interface Raft {
9320
9381
  };
9321
9382
  };
9322
9383
  inbox: {
9323
- /** One bounded pull. Nothing is acknowledged until the next call passes `data.cursor` as `since` (cursor mode). */
9384
+ /**
9385
+ * One bounded pull. Nothing is acknowledged by it. Without `since`, sends
9386
+ * the last committed cursor (from `state`), which is what acknowledges the
9387
+ * previously committed batch on the Server; the returned batch's cursor is
9388
+ * recorded as pending until you `commit()` it.
9389
+ */
9324
9390
  check(request?: CheckInboxRequest): Promise<CheckInboxOutcome>;
9391
+ /**
9392
+ * Mark a batch as processed. With no argument, commits the pending cursor
9393
+ * from the last `check()` (possibly in an earlier process, via `state`);
9394
+ * also accepts `{ cursor }` or a batch. Records only; the next `check()`
9395
+ * acknowledges it on the Server. The SDK never commits on its own.
9396
+ */
9397
+ commit(target?: {
9398
+ cursor: number | null;
9399
+ }): Promise<RaftInboxCommitResult>;
9325
9400
  /**
9326
9401
  * Pull until the Server reports nothing more, like `raft message check`, as
9327
9402
  * an async iterator: the pull that acknowledges a batch is only sent when
@@ -9455,9 +9530,17 @@ interface Raft {
9455
9530
  };
9456
9531
  /** What this process has shown its model, per target; export it to survive restarts. */
9457
9532
  frontier: SeenFrontier;
9533
+ state: {
9534
+ /** Save now (for example after `frontier.recordHeld`). Best effort; returns whether it succeeded. */
9535
+ save(): Promise<boolean>;
9536
+ /** Load the persisted state now (operations do this automatically). */
9537
+ load(): Promise<void>;
9538
+ /** The current state value, as it would be saved. */
9539
+ snapshot(): RaftState;
9540
+ };
9458
9541
  /** Every Agent API route, typed from the shared contract. */
9459
9542
  routes: RaftRoutes;
9460
9543
  }
9461
9544
  export declare function createRaft(options: CreateRaftOptions): Raft;
9462
9545
  //#endregion
9463
- export type { AmendTaskOutcome, AmendTaskRequest, AssignTaskRequest, BootstrapRaftCredentialOptions, CheckInboxOutcome, CheckInboxRequest, ClaimTasksOutcome, ClaimTasksRequest, CreateRaftClientFromStoreOptions, CreateRaftClientOptions, CreateRaftOptions, CreateRaftRoutesOptions, CreateTasksRequest, DrainInboxRequest, ExecuteMentionActionRequest, ListInboxRequest, ListTasksRequest, ManualContext, Raft, RaftAckMode, RaftActionPrepareRequest, RaftActionPrepared, RaftApiError, RaftApiResult, RaftAppConfig, RaftAppConfigPatch, RaftAttachmentBytes, RaftAttachmentUploaded, RaftAvatarUpload, RaftChannelJoinClient, RaftChannelJoinClientResult, RaftChannelJoinError, RaftChannelJoinFailure, RaftChannelJoinOperation, RaftChannelJoinRequest, RaftChannelJoinResult, RaftChannelJoinSuccess, RaftChannelJoinTransportError, RaftChannelMuteState, RaftChannelRef, RaftClaimHeld, RaftClaimHeldLike, RaftClaimResult, RaftClaimRow, RaftClaimRowState, RaftClient, RaftClientError, RaftClientFailure, RaftClientResult, RaftClientSuccess, RaftClientThrottleOptions, RaftClientTransportRequest, RaftContextAgent, RaftContextData, RaftContextError, RaftContextResult, RaftContextServer, RaftCredentialErrorCode, RaftCredentialIdentity, RaftCredentialStore, RaftEvent, RaftEventAttachment, RaftEventExternalMessage, RaftEventsReceiveData, RaftEventsReceiveError, RaftEventsReceiveRequest, RaftEventsReceiveResult, RaftFailure, RaftHeld, RaftHeldBase, RaftHistoryPage, RaftInboxBatch, RaftInboxConversation, RaftInboxDrainSummary, RaftInboxListing, RaftInboxNotice, RaftManageClient, RaftMessage, RaftMessageAttachment, RaftMessageTask, RaftNextStep, RaftNoticeFlag, RaftNoticeTarget, RaftOpError, RaftOpErrorCode, RaftOutcome, RaftPendingMentions, RaftProfile, RaftProfileUpdate, RaftRouteAnnotations, RaftRouteInfo, RaftRouteKey, RaftRouteMeta, RaftRouteResult, RaftRouteRetryPolicy, RaftRoutes, RaftSdkConfigurationErrorCode, RaftSearchPage, RaftSendContinuation, RaftSenderType, RaftSent, RaftServerInfo, RaftServerProfile, RaftServerUpdate, RaftTaskBoard, RaftTaskStatus, RaftTasksCreated, RaftWebhookStatus, ReactRequest, ReadHistoryRequest, SearchMessagesRequest, SeenAttestation, SeenFrontierSnapshot, SendMessageOutcome, SendMessageRequest, ServerInfoRequest, ServerInfoSection, StoredRaftCredential, TaskRef, UpdateTaskStatusOutcome, UpdateTaskStatusRequest, UploadAttachmentRequest, VerifyNoticeInput, VerifyNoticeRejection, VerifyNoticeResult };
9546
+ export type { AmendTaskOutcome, AmendTaskRequest, AssignTaskRequest, BootstrapRaftCredentialOptions, CheckInboxOutcome, CheckInboxRequest, ClaimTasksOutcome, ClaimTasksRequest, CreateRaftClientFromStoreOptions, CreateRaftClientOptions, CreateRaftOptions, CreateRaftRoutesOptions, CreateTasksRequest, DrainInboxRequest, ExecuteMentionActionRequest, ListInboxRequest, ListTasksRequest, ManualContext, Raft, RaftAckMode, RaftActionPrepareRequest, RaftActionPrepared, RaftApiError, RaftApiResult, RaftAppConfig, RaftAppConfigPatch, RaftAttachmentBytes, RaftAttachmentUploaded, RaftAvatarUpload, RaftChannelJoinClient, RaftChannelJoinClientResult, RaftChannelJoinError, RaftChannelJoinFailure, RaftChannelJoinOperation, RaftChannelJoinRequest, RaftChannelJoinResult, RaftChannelJoinSuccess, RaftChannelJoinTransportError, RaftChannelMuteState, RaftChannelRef, RaftClaimHeld, RaftClaimHeldLike, RaftClaimResult, RaftClaimRow, RaftClaimRowState, RaftClient, RaftClientError, RaftClientFailure, RaftClientResult, RaftClientSuccess, RaftClientThrottleOptions, RaftClientTransportRequest, RaftContextAgent, RaftContextData, RaftContextError, RaftContextResult, RaftContextServer, RaftCredentialErrorCode, RaftCredentialIdentity, RaftCredentialStore, RaftEvent, RaftEventAttachment, RaftEventExternalMessage, RaftEventsReceiveData, RaftEventsReceiveError, RaftEventsReceiveRequest, RaftEventsReceiveResult, RaftFailure, RaftHeld, RaftHeldBase, RaftHistoryPage, RaftInboxBatch, RaftInboxCommitResult, RaftInboxConversation, RaftInboxDrainSummary, RaftInboxListing, RaftInboxNotice, RaftManageClient, RaftMessage, RaftMessageAttachment, RaftMessageTask, RaftNextStep, RaftNoticeFlag, RaftNoticeTarget, RaftOpError, RaftOpErrorCode, RaftOutcome, RaftPendingMentions, RaftProfile, RaftProfileUpdate, RaftRouteAnnotations, RaftRouteInfo, RaftRouteKey, RaftRouteMeta, RaftRouteResult, RaftRouteRetryPolicy, RaftRoutes, RaftSdkConfigurationErrorCode, RaftSearchPage, RaftSendContinuation, RaftSenderType, RaftSent, RaftServerInfo, RaftServerProfile, RaftServerUpdate, RaftState, RaftStateContinuation, RaftStateSaveErrorHandler, RaftStateStore, RaftTaskBoard, RaftTaskStatus, RaftTasksCreated, RaftWebhookStatus, ReactRequest, ReadHistoryRequest, SearchMessagesRequest, SeenAttestation, SeenFrontierSnapshot, SendMessageOutcome, SendMessageRequest, ServerInfoRequest, ServerInfoSection, StoredRaftCredential, TaskRef, UpdateTaskStatusOutcome, UpdateTaskStatusRequest, UploadAttachmentRequest, VerifyNoticeInput, VerifyNoticeRejection, VerifyNoticeResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-sdk",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.4",
4
4
  "license": "FSL-1.1-ALv2",
5
5
  "description": "Typed Raft Agent API client for external agents and bots.",
6
6
  "type": "module",