@botiverse/raft-sdk 0.3.0 → 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.
package/README.md CHANGED
@@ -363,6 +363,56 @@ record through `options.store`. It returns only non-secret identity metadata.
363
363
  Creates the explicit Node.js file store described above. Relative paths and
364
364
  unsafe stored-file permissions fail closed.
365
365
 
366
+ ### `readLatestReadThread(options?)`
367
+
368
+ Node.js only. Returns the thread this agent read most recently with
369
+ `raft message read` on this machine. It reads the record the Raft CLI keeps
370
+ locally: no credential, no login, and no request to the Server.
371
+
372
+ ```ts
373
+ import { readLatestReadThread } from "@botiverse/raft-sdk";
374
+
375
+ const latest = await readLatestReadThread();
376
+ if (latest.state === "thread") {
377
+ console.log(latest.target); // "#general:1a2b3c4d"
378
+ console.log(latest.parentTarget); // "#general"
379
+ } else {
380
+ console.log(latest.reason);
381
+ }
382
+ ```
383
+
384
+ Options, all optional:
385
+
386
+ - `agentId`: whose reads to look at. Defaults to `SLOCK_AGENT_ID`, which the
387
+ Raft daemon sets for the agents it runs.
388
+ - `home`: the Raft home directory. Defaults to what the CLI uses: `RAFT_HOME`,
389
+ then `SLOCK_HOME`, then `~/.slock`.
390
+ - `env`: the environment to take those defaults from. Defaults to
391
+ `process.env`.
392
+
393
+ When there is no thread to report, `state` is `"none"` and `reason` is one of:
394
+
395
+ | `reason` | Meaning |
396
+ | --- | --- |
397
+ | `no_agent_id` | No `agentId` was given and `SLOCK_AGENT_ID` is not set. |
398
+ | `no_record` | The CLI has kept no read record for this agent on this machine. |
399
+ | `unreadable` | A record exists but is not a private file of this user, or is not valid. |
400
+ | `no_reads` | The record holds no read. |
401
+ | `latest_read_is_not_a_thread` | The latest read was a channel or a DM, not a thread. |
402
+
403
+ What to keep in mind:
404
+
405
+ - It reports what was read last, not what the work belongs to. An agent that
406
+ read an unrelated thread afterwards gets that thread. Use the answer as a
407
+ default to confirm.
408
+ - An older thread is never substituted when the latest read was a channel or
409
+ a DM.
410
+ - Only `raft message read` counts. `raft message check` and reads made
411
+ through this SDK's `messages.read()` are not in the CLI's record.
412
+ - A thread target contains the channel name. Do not publish the target of a
413
+ private channel's thread.
414
+ - The function only reads. It never creates or changes the CLI's record.
415
+
366
416
  ### `createRaftClientFromStore(options)`
367
417
 
368
418
  Loads and validates one `RaftCredentialStore` record, then creates the same
@@ -3,6 +3,7 @@ let node_crypto = require("node:crypto");
3
3
  let node_fs = require("node:fs");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let node_path = require("node:path");
6
+ let node_os = require("node:os");
6
7
  Object.freeze({ status: "aborted" });
7
8
  function $constructor(name, initializer, params) {
8
9
  function init(inst, def) {
@@ -5443,6 +5444,7 @@ const agentApiIntegrationAppUpdateBodySchema = passthroughObject({
5443
5444
  clientKey: string().trim().min(1),
5444
5445
  name: optionalStringSchema,
5445
5446
  description: optionalStringSchema,
5447
+ whenToUse: optionalStringSchema,
5446
5448
  category: string().trim().min(1).optional(),
5447
5449
  homepageUrl: optionalStringSchema,
5448
5450
  returnUrl: optionalStringSchema,
@@ -5623,6 +5625,7 @@ const agentApiIntegrationServiceSchema = passthroughObject({
5623
5625
  ]).optional(),
5624
5626
  name: string(),
5625
5627
  description: nullableStringSchema,
5628
+ whenToUse: nullableStringSchema.optional(),
5626
5629
  homepageUrl: nullableStringSchema,
5627
5630
  returnUrl: nullableStringSchema,
5628
5631
  agentManifestUrl: nullableStringSchema,
@@ -11099,6 +11102,43 @@ function sortBySeq(messages) {
11099
11102
  function projectRaftMessages(envelopes) {
11100
11103
  return envelopes.map(projectRaftMessage).filter((m) => m !== null);
11101
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
+ }
11102
11142
  //#endregion
11103
11143
  //#region ../shared/src/agentOps/frontier.ts
11104
11144
  const MAX_EXACT_SEQS = 2500;
@@ -11154,10 +11194,11 @@ var SeenFrontier = class SeenFrontier {
11154
11194
  * Attest that the model saw the context a held send/claim returned. Call it
11155
11195
  * only after the held messages actually reached the model (the SDK cannot
11156
11196
  * know that, so it never records this implicitly). Returns false and records
11157
- * 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.
11158
11199
  */
11159
11200
  recordHeld(held) {
11160
- if (held.withheld || held.seenUpToSeq === null) return false;
11201
+ if (held.withheld || held.contextComplete === false || held.seenUpToSeq === null) return false;
11161
11202
  this.recordUpTo(held.target, held.seenUpToSeq);
11162
11203
  return true;
11163
11204
  }
@@ -11192,6 +11233,73 @@ var SeenFrontier = class SeenFrontier {
11192
11233
  }
11193
11234
  };
11194
11235
  //#endregion
11236
+ //#region ../shared/src/agentOps/latestReadThread.ts
11237
+ const CLI_READ_STATE_NAMESPACE = "slock-cli-consumed-seq";
11238
+ const CLI_READ_STATE_FILENAME = "consumed-seqs.json";
11239
+ const RAFT_HOME_ENVS = ["RAFT_HOME", "SLOCK_HOME"];
11240
+ /** The Raft home the environment configures, or undefined when the default (`~/.slock`) applies. */
11241
+ function configuredRaftHome(env) {
11242
+ for (const name of RAFT_HOME_ENVS) {
11243
+ const value = env[name]?.trim();
11244
+ if (value) return value;
11245
+ }
11246
+ }
11247
+ /** The base directory the environment configures for the read record, or undefined when the default applies. */
11248
+ function configuredCliReadStateBase(env) {
11249
+ return env["SLOCK_CLI_CONSUMED_SEQ_STATE_DIR"]?.trim() || configuredRaftHome(env);
11250
+ }
11251
+ /** An agent id is a path segment of the record's location, so it must be a plain one. */
11252
+ function isLocalStateAgentId(agentId) {
11253
+ return /^[A-Za-z0-9_-]+$/.test(agentId);
11254
+ }
11255
+ /** The record's location under the base directory, as path segments. */
11256
+ function cliReadStatePathSegments(agentId) {
11257
+ if (!isLocalStateAgentId(agentId)) throw new Error("Invalid local state agent identity");
11258
+ return [
11259
+ CLI_READ_STATE_NAMESPACE,
11260
+ agentId,
11261
+ CLI_READ_STATE_FILENAME
11262
+ ];
11263
+ }
11264
+ function positiveFinite(value) {
11265
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
11266
+ }
11267
+ /** `#channel:thread` → `#channel`, `dm:@peer:thread` → `dm:@peer`; null for a channel or DM root. */
11268
+ function parentTargetOfThread(target) {
11269
+ const from = target.startsWith("dm:@") ? 4 : target.startsWith("#") ? 1 : -1;
11270
+ if (from < 0) return null;
11271
+ const separator = target.indexOf(":", from);
11272
+ return separator > 0 && separator < target.length - 1 ? target.slice(0, separator) : null;
11273
+ }
11274
+ function latestReadThreadFromCliReadState(raw) {
11275
+ const targets = raw && typeof raw === "object" ? raw.targets : void 0;
11276
+ let latestTarget;
11277
+ let latestOrder = 0;
11278
+ if (targets && typeof targets === "object" && !Array.isArray(targets)) for (const [target, value] of Object.entries(targets)) {
11279
+ if (target.length === 0) continue;
11280
+ const record = typeof value === "number" ? { seq: value } : value && typeof value === "object" ? value : {};
11281
+ const order = positiveFinite(record.readOrder) ?? positiveFinite(record.seq);
11282
+ if (order !== void 0 && order > latestOrder) {
11283
+ latestOrder = order;
11284
+ latestTarget = target;
11285
+ }
11286
+ }
11287
+ if (latestTarget === void 0) return {
11288
+ state: "none",
11289
+ reason: "no_reads"
11290
+ };
11291
+ const parentTarget = parentTargetOfThread(latestTarget);
11292
+ if (parentTarget === null) return {
11293
+ state: "none",
11294
+ reason: "latest_read_is_not_a_thread"
11295
+ };
11296
+ return {
11297
+ state: "thread",
11298
+ target: latestTarget,
11299
+ parentTarget
11300
+ };
11301
+ }
11302
+ //#endregion
11195
11303
  //#region ../shared/src/agentOps/inbox.ts
11196
11304
  const MAX_DRAIN_ROUNDS = 50;
11197
11305
  function batchNext(batch) {
@@ -11487,6 +11595,7 @@ function heldText(held, action) {
11487
11595
  if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
11488
11596
  if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
11489
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.");
11490
11599
  lines.push(`Full text: raft message read --target "${held.target}"`);
11491
11600
  return lines.join("\n");
11492
11601
  }
@@ -11498,26 +11607,30 @@ function heldNext(held) {
11498
11607
  target: held.target,
11499
11608
  ...held.continuation
11500
11609
  },
11501
- 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)."
11502
11611
  };
11503
11612
  }
11504
11613
  function projectHeld(target, data, idempotencyKey) {
11505
11614
  const withheld = data.freshnessContextMode === "withheld";
11506
- const heldMessages = projectRaftMessages(data.heldMessages ?? []);
11615
+ const heldMessages = projectRaftMessagesInTarget(data.heldMessages ?? [], target);
11507
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;
11508
11620
  return {
11509
11621
  target,
11510
11622
  idempotencyKey,
11511
11623
  continuation: {
11512
11624
  idempotencyKey,
11513
- ...!withheld && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11625
+ ...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11514
11626
  },
11515
- newMessageCount: withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length,
11627
+ newMessageCount,
11516
11628
  heldMessages,
11517
- omittedMessageCount: data.omittedMessageCount ?? 0,
11629
+ omittedMessageCount,
11518
11630
  formalMentionCount: data.mentionAnnotation?.formalMentionCount ?? 0,
11519
11631
  seenUpToSeq,
11520
- withheld
11632
+ withheld,
11633
+ contextComplete
11521
11634
  };
11522
11635
  }
11523
11636
  function isHeldResponse(data) {
@@ -11574,9 +11687,10 @@ async function sendMessage(client, request, frontier, internal = {}) {
11574
11687
  ...request,
11575
11688
  idempotencyKey
11576
11689
  };
11690
+ const base = projectHeld(request.target, data, idempotencyKey);
11577
11691
  const held = {
11578
- ...projectHeld(request.target, data, idempotencyKey),
11579
- 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" ? {
11580
11694
  ...keyed,
11581
11695
  seen: {
11582
11696
  upToSeq: data.seenUpToSeq,
@@ -12308,7 +12422,7 @@ function formatAgentServerInfo(data) {
12308
12422
  text += "Server-profile changes still use raft server update and remain server-role gated.\n";
12309
12423
  text += "Mute state is shown when the server provides it; otherwise it is omitted.\n";
12310
12424
  if (channels.length > 0) for (const t of channels) {
12311
- const statusParts = [t.type === "private" || t.type === "dm" ? "private" : "public", t.joined ? "joined" : "not joined"];
12425
+ const statusParts = [channelVisibility(t), t.joined ? "joined" : "not joined"];
12312
12426
  if (t.channelRole) statusParts.push(`channel role=${t.channelRole}`);
12313
12427
  if (t.channelAdminBasis) statusParts.push(`admin via=${t.channelAdminBasis}`);
12314
12428
  const muted = channelMuted(t);
@@ -12343,6 +12457,7 @@ function agentChannelRef(name, type) {
12343
12457
  }
12344
12458
  function channelVisibility(channel) {
12345
12459
  const type = channel.type?.trim();
12460
+ if (type === "joint") return "joint";
12346
12461
  return type === "private" || type === "dm" ? "private" : "public";
12347
12462
  }
12348
12463
  function channelMuted(channel) {
@@ -13923,12 +14038,67 @@ async function prepareActionCard(client, request) {
13923
14038
  target: request.target,
13924
14039
  messageId: card.messageId
13925
14040
  },
13926
- why: "A human must click the card to commit it; its outcome arrives in your inbox."
14041
+ why: "A human must click the card to commit it. You are not notified of the result yet; read the card message to see its state instead of waiting."
13927
14042
  },
13928
14043
  text: formatAgentActionCardPosted(request.target, card.messageId)
13929
14044
  };
13930
14045
  }
13931
14046
  //#endregion
14047
+ //#region src/latestReadThread.ts
14048
+ const MAX_READ_STATE_BYTES = 33554432;
14049
+ function firstNonEmpty(...values) {
14050
+ for (const value of values) {
14051
+ const trimmed = value?.trim();
14052
+ if (trimmed) return trimmed;
14053
+ }
14054
+ }
14055
+ /**
14056
+ * The thread this agent read most recently with `raft message read`, when
14057
+ * that read is still the latest read of any conversation.
14058
+ *
14059
+ * It is a fact about what was read, not about what the work belongs to: an
14060
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
14061
+ * answer as a default to confirm, and keep private channel names out of
14062
+ * public places.
14063
+ */
14064
+ async function readLatestReadThread(options = {}) {
14065
+ const env = options.env ?? process.env;
14066
+ const agentId = firstNonEmpty(options.agentId, env.SLOCK_AGENT_ID);
14067
+ if (!agentId || !isLocalStateAgentId(agentId)) return {
14068
+ state: "none",
14069
+ reason: "no_agent_id"
14070
+ };
14071
+ const base = firstNonEmpty(options.home, configuredCliReadStateBase(env)) ?? (0, node_path.join)((0, node_os.homedir)(), ".slock");
14072
+ const filePath = (0, node_path.join)(base, ...cliReadStatePathSegments(agentId));
14073
+ let handle;
14074
+ try {
14075
+ handle = await (0, node_fs_promises.open)(filePath, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW);
14076
+ } catch (error) {
14077
+ return {
14078
+ state: "none",
14079
+ reason: error.code === "ENOENT" ? "no_record" : "unreadable"
14080
+ };
14081
+ }
14082
+ let raw;
14083
+ try {
14084
+ const stat = await handle.stat();
14085
+ const ownedByUser = typeof process.getuid !== "function" || stat.uid === process.getuid() && (stat.mode & 63) === 0;
14086
+ if (!stat.isFile() || !ownedByUser || stat.size > MAX_READ_STATE_BYTES) return {
14087
+ state: "none",
14088
+ reason: "unreadable"
14089
+ };
14090
+ raw = JSON.parse(await handle.readFile("utf8"));
14091
+ } catch {
14092
+ return {
14093
+ state: "none",
14094
+ reason: "unreadable"
14095
+ };
14096
+ } finally {
14097
+ await handle.close();
14098
+ }
14099
+ return latestReadThreadFromCliReadState(raw);
14100
+ }
14101
+ //#endregion
13932
14102
  //#region src/raft.ts
13933
14103
  function createRaft(options) {
13934
14104
  const serverUrl = requireServerUrl(options.serverUrl);
@@ -14134,4 +14304,5 @@ exports.parseRaftRegularChannelTarget = parseRaftRegularChannelTarget;
14134
14304
  exports.parseRaftState = parseRaftState;
14135
14305
  exports.projectRaftMessage = projectRaftMessage;
14136
14306
  exports.projectRaftMessages = projectRaftMessages;
14307
+ exports.readLatestReadThread = readLatestReadThread;
14137
14308
  exports.verifyInboxNotice = verifyInboxNotice;
package/dist/esm/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { constants } from "node:fs";
3
3
  import { chmod, link, lstat, mkdir, open, unlink } from "node:fs/promises";
4
- import { dirname, isAbsolute } from "node:path";
4
+ import { dirname, isAbsolute, join } from "node:path";
5
+ import { homedir } from "node:os";
5
6
  Object.freeze({ status: "aborted" });
6
7
  function $constructor(name, initializer, params) {
7
8
  function init(inst, def) {
@@ -5442,6 +5443,7 @@ const agentApiIntegrationAppUpdateBodySchema = passthroughObject({
5442
5443
  clientKey: string().trim().min(1),
5443
5444
  name: optionalStringSchema,
5444
5445
  description: optionalStringSchema,
5446
+ whenToUse: optionalStringSchema,
5445
5447
  category: string().trim().min(1).optional(),
5446
5448
  homepageUrl: optionalStringSchema,
5447
5449
  returnUrl: optionalStringSchema,
@@ -5622,6 +5624,7 @@ const agentApiIntegrationServiceSchema = passthroughObject({
5622
5624
  ]).optional(),
5623
5625
  name: string(),
5624
5626
  description: nullableStringSchema,
5627
+ whenToUse: nullableStringSchema.optional(),
5625
5628
  homepageUrl: nullableStringSchema,
5626
5629
  returnUrl: nullableStringSchema,
5627
5630
  agentManifestUrl: nullableStringSchema,
@@ -11098,6 +11101,43 @@ function sortBySeq(messages) {
11098
11101
  function projectRaftMessages(envelopes) {
11099
11102
  return envelopes.map(projectRaftMessage).filter((m) => m !== null);
11100
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
+ }
11101
11141
  //#endregion
11102
11142
  //#region ../shared/src/agentOps/frontier.ts
11103
11143
  const MAX_EXACT_SEQS = 2500;
@@ -11153,10 +11193,11 @@ var SeenFrontier = class SeenFrontier {
11153
11193
  * Attest that the model saw the context a held send/claim returned. Call it
11154
11194
  * only after the held messages actually reached the model (the SDK cannot
11155
11195
  * know that, so it never records this implicitly). Returns false and records
11156
- * 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.
11157
11198
  */
11158
11199
  recordHeld(held) {
11159
- if (held.withheld || held.seenUpToSeq === null) return false;
11200
+ if (held.withheld || held.contextComplete === false || held.seenUpToSeq === null) return false;
11160
11201
  this.recordUpTo(held.target, held.seenUpToSeq);
11161
11202
  return true;
11162
11203
  }
@@ -11191,6 +11232,73 @@ var SeenFrontier = class SeenFrontier {
11191
11232
  }
11192
11233
  };
11193
11234
  //#endregion
11235
+ //#region ../shared/src/agentOps/latestReadThread.ts
11236
+ const CLI_READ_STATE_NAMESPACE = "slock-cli-consumed-seq";
11237
+ const CLI_READ_STATE_FILENAME = "consumed-seqs.json";
11238
+ const RAFT_HOME_ENVS = ["RAFT_HOME", "SLOCK_HOME"];
11239
+ /** The Raft home the environment configures, or undefined when the default (`~/.slock`) applies. */
11240
+ function configuredRaftHome(env) {
11241
+ for (const name of RAFT_HOME_ENVS) {
11242
+ const value = env[name]?.trim();
11243
+ if (value) return value;
11244
+ }
11245
+ }
11246
+ /** The base directory the environment configures for the read record, or undefined when the default applies. */
11247
+ function configuredCliReadStateBase(env) {
11248
+ return env["SLOCK_CLI_CONSUMED_SEQ_STATE_DIR"]?.trim() || configuredRaftHome(env);
11249
+ }
11250
+ /** An agent id is a path segment of the record's location, so it must be a plain one. */
11251
+ function isLocalStateAgentId(agentId) {
11252
+ return /^[A-Za-z0-9_-]+$/.test(agentId);
11253
+ }
11254
+ /** The record's location under the base directory, as path segments. */
11255
+ function cliReadStatePathSegments(agentId) {
11256
+ if (!isLocalStateAgentId(agentId)) throw new Error("Invalid local state agent identity");
11257
+ return [
11258
+ CLI_READ_STATE_NAMESPACE,
11259
+ agentId,
11260
+ CLI_READ_STATE_FILENAME
11261
+ ];
11262
+ }
11263
+ function positiveFinite(value) {
11264
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
11265
+ }
11266
+ /** `#channel:thread` → `#channel`, `dm:@peer:thread` → `dm:@peer`; null for a channel or DM root. */
11267
+ function parentTargetOfThread(target) {
11268
+ const from = target.startsWith("dm:@") ? 4 : target.startsWith("#") ? 1 : -1;
11269
+ if (from < 0) return null;
11270
+ const separator = target.indexOf(":", from);
11271
+ return separator > 0 && separator < target.length - 1 ? target.slice(0, separator) : null;
11272
+ }
11273
+ function latestReadThreadFromCliReadState(raw) {
11274
+ const targets = raw && typeof raw === "object" ? raw.targets : void 0;
11275
+ let latestTarget;
11276
+ let latestOrder = 0;
11277
+ if (targets && typeof targets === "object" && !Array.isArray(targets)) for (const [target, value] of Object.entries(targets)) {
11278
+ if (target.length === 0) continue;
11279
+ const record = typeof value === "number" ? { seq: value } : value && typeof value === "object" ? value : {};
11280
+ const order = positiveFinite(record.readOrder) ?? positiveFinite(record.seq);
11281
+ if (order !== void 0 && order > latestOrder) {
11282
+ latestOrder = order;
11283
+ latestTarget = target;
11284
+ }
11285
+ }
11286
+ if (latestTarget === void 0) return {
11287
+ state: "none",
11288
+ reason: "no_reads"
11289
+ };
11290
+ const parentTarget = parentTargetOfThread(latestTarget);
11291
+ if (parentTarget === null) return {
11292
+ state: "none",
11293
+ reason: "latest_read_is_not_a_thread"
11294
+ };
11295
+ return {
11296
+ state: "thread",
11297
+ target: latestTarget,
11298
+ parentTarget
11299
+ };
11300
+ }
11301
+ //#endregion
11194
11302
  //#region ../shared/src/agentOps/inbox.ts
11195
11303
  const MAX_DRAIN_ROUNDS = 50;
11196
11304
  function batchNext(batch) {
@@ -11486,6 +11594,7 @@ function heldText(held, action) {
11486
11594
  if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
11487
11595
  if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
11488
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.");
11489
11598
  lines.push(`Full text: raft message read --target "${held.target}"`);
11490
11599
  return lines.join("\n");
11491
11600
  }
@@ -11497,26 +11606,30 @@ function heldNext(held) {
11497
11606
  target: held.target,
11498
11607
  ...held.continuation
11499
11608
  },
11500
- 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)."
11501
11610
  };
11502
11611
  }
11503
11612
  function projectHeld(target, data, idempotencyKey) {
11504
11613
  const withheld = data.freshnessContextMode === "withheld";
11505
- const heldMessages = projectRaftMessages(data.heldMessages ?? []);
11614
+ const heldMessages = projectRaftMessagesInTarget(data.heldMessages ?? [], target);
11506
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;
11507
11619
  return {
11508
11620
  target,
11509
11621
  idempotencyKey,
11510
11622
  continuation: {
11511
11623
  idempotencyKey,
11512
- ...!withheld && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11624
+ ...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
11513
11625
  },
11514
- newMessageCount: withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length,
11626
+ newMessageCount,
11515
11627
  heldMessages,
11516
- omittedMessageCount: data.omittedMessageCount ?? 0,
11628
+ omittedMessageCount,
11517
11629
  formalMentionCount: data.mentionAnnotation?.formalMentionCount ?? 0,
11518
11630
  seenUpToSeq,
11519
- withheld
11631
+ withheld,
11632
+ contextComplete
11520
11633
  };
11521
11634
  }
11522
11635
  function isHeldResponse(data) {
@@ -11573,9 +11686,10 @@ async function sendMessage(client, request, frontier, internal = {}) {
11573
11686
  ...request,
11574
11687
  idempotencyKey
11575
11688
  };
11689
+ const base = projectHeld(request.target, data, idempotencyKey);
11576
11690
  const held = {
11577
- ...projectHeld(request.target, data, idempotencyKey),
11578
- 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" ? {
11579
11693
  ...keyed,
11580
11694
  seen: {
11581
11695
  upToSeq: data.seenUpToSeq,
@@ -12307,7 +12421,7 @@ function formatAgentServerInfo(data) {
12307
12421
  text += "Server-profile changes still use raft server update and remain server-role gated.\n";
12308
12422
  text += "Mute state is shown when the server provides it; otherwise it is omitted.\n";
12309
12423
  if (channels.length > 0) for (const t of channels) {
12310
- const statusParts = [t.type === "private" || t.type === "dm" ? "private" : "public", t.joined ? "joined" : "not joined"];
12424
+ const statusParts = [channelVisibility(t), t.joined ? "joined" : "not joined"];
12311
12425
  if (t.channelRole) statusParts.push(`channel role=${t.channelRole}`);
12312
12426
  if (t.channelAdminBasis) statusParts.push(`admin via=${t.channelAdminBasis}`);
12313
12427
  const muted = channelMuted(t);
@@ -12342,6 +12456,7 @@ function agentChannelRef(name, type) {
12342
12456
  }
12343
12457
  function channelVisibility(channel) {
12344
12458
  const type = channel.type?.trim();
12459
+ if (type === "joint") return "joint";
12345
12460
  return type === "private" || type === "dm" ? "private" : "public";
12346
12461
  }
12347
12462
  function channelMuted(channel) {
@@ -13922,12 +14037,67 @@ async function prepareActionCard(client, request) {
13922
14037
  target: request.target,
13923
14038
  messageId: card.messageId
13924
14039
  },
13925
- why: "A human must click the card to commit it; its outcome arrives in your inbox."
14040
+ why: "A human must click the card to commit it. You are not notified of the result yet; read the card message to see its state instead of waiting."
13926
14041
  },
13927
14042
  text: formatAgentActionCardPosted(request.target, card.messageId)
13928
14043
  };
13929
14044
  }
13930
14045
  //#endregion
14046
+ //#region src/latestReadThread.ts
14047
+ const MAX_READ_STATE_BYTES = 33554432;
14048
+ function firstNonEmpty(...values) {
14049
+ for (const value of values) {
14050
+ const trimmed = value?.trim();
14051
+ if (trimmed) return trimmed;
14052
+ }
14053
+ }
14054
+ /**
14055
+ * The thread this agent read most recently with `raft message read`, when
14056
+ * that read is still the latest read of any conversation.
14057
+ *
14058
+ * It is a fact about what was read, not about what the work belongs to: an
14059
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
14060
+ * answer as a default to confirm, and keep private channel names out of
14061
+ * public places.
14062
+ */
14063
+ async function readLatestReadThread(options = {}) {
14064
+ const env = options.env ?? process.env;
14065
+ const agentId = firstNonEmpty(options.agentId, env.SLOCK_AGENT_ID);
14066
+ if (!agentId || !isLocalStateAgentId(agentId)) return {
14067
+ state: "none",
14068
+ reason: "no_agent_id"
14069
+ };
14070
+ const base = firstNonEmpty(options.home, configuredCliReadStateBase(env)) ?? join(homedir(), ".slock");
14071
+ const filePath = join(base, ...cliReadStatePathSegments(agentId));
14072
+ let handle;
14073
+ try {
14074
+ handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
14075
+ } catch (error) {
14076
+ return {
14077
+ state: "none",
14078
+ reason: error.code === "ENOENT" ? "no_record" : "unreadable"
14079
+ };
14080
+ }
14081
+ let raw;
14082
+ try {
14083
+ const stat = await handle.stat();
14084
+ const ownedByUser = typeof process.getuid !== "function" || stat.uid === process.getuid() && (stat.mode & 63) === 0;
14085
+ if (!stat.isFile() || !ownedByUser || stat.size > MAX_READ_STATE_BYTES) return {
14086
+ state: "none",
14087
+ reason: "unreadable"
14088
+ };
14089
+ raw = JSON.parse(await handle.readFile("utf8"));
14090
+ } catch {
14091
+ return {
14092
+ state: "none",
14093
+ reason: "unreadable"
14094
+ };
14095
+ } finally {
14096
+ await handle.close();
14097
+ }
14098
+ return latestReadThreadFromCliReadState(raw);
14099
+ }
14100
+ //#endregion
13931
14101
  //#region src/raft.ts
13932
14102
  function createRaft(options) {
13933
14103
  const serverUrl = requireServerUrl(options.serverUrl);
@@ -14108,4 +14278,4 @@ function createRaft(options) {
14108
14278
  };
14109
14279
  }
14110
14280
  //#endregion
14111
- 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 };
14281
+ 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, readLatestReadThread, verifyInboxNotice };
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
  /**
@@ -1857,6 +1866,7 @@ declare const agentApiIntegrationAppUpdateBodySchema: z.ZodObject<{
1857
1866
  clientKey: z.ZodString;
1858
1867
  name: z.ZodOptional<z.ZodString>;
1859
1868
  description: z.ZodOptional<z.ZodString>;
1869
+ whenToUse: z.ZodOptional<z.ZodString>;
1860
1870
  category: z.ZodOptional<z.ZodString>;
1861
1871
  homepageUrl: z.ZodOptional<z.ZodString>;
1862
1872
  returnUrl: z.ZodOptional<z.ZodString>;
@@ -2120,6 +2130,7 @@ declare const agentApiIntegrationListResponseSchema: z.ZodObject<{
2120
2130
  }>>;
2121
2131
  name: z.ZodString;
2122
2132
  description: z.ZodNullable<z.ZodString>;
2133
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2123
2134
  homepageUrl: z.ZodNullable<z.ZodString>;
2124
2135
  returnUrl: z.ZodNullable<z.ZodString>;
2125
2136
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -2198,6 +2209,7 @@ declare const agentApiIntegrationLoginResponseSchema: z.ZodObject<{
2198
2209
  }>>;
2199
2210
  name: z.ZodString;
2200
2211
  description: z.ZodNullable<z.ZodString>;
2212
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2201
2213
  homepageUrl: z.ZodNullable<z.ZodString>;
2202
2214
  returnUrl: z.ZodNullable<z.ZodString>;
2203
2215
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7291,6 +7303,7 @@ declare const agentApiContract: {
7291
7303
  }>>;
7292
7304
  name: z.ZodString;
7293
7305
  description: z.ZodNullable<z.ZodString>;
7306
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7294
7307
  homepageUrl: z.ZodNullable<z.ZodString>;
7295
7308
  returnUrl: z.ZodNullable<z.ZodString>;
7296
7309
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7412,6 +7425,7 @@ declare const agentApiContract: {
7412
7425
  }>>;
7413
7426
  name: z.ZodString;
7414
7427
  description: z.ZodNullable<z.ZodString>;
7428
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7415
7429
  homepageUrl: z.ZodNullable<z.ZodString>;
7416
7430
  returnUrl: z.ZodNullable<z.ZodString>;
7417
7431
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7603,6 +7617,7 @@ declare const agentApiContract: {
7603
7617
  clientKey: z.ZodString;
7604
7618
  name: z.ZodOptional<z.ZodString>;
7605
7619
  description: z.ZodOptional<z.ZodString>;
7620
+ whenToUse: z.ZodOptional<z.ZodString>;
7606
7621
  category: z.ZodOptional<z.ZodString>;
7607
7622
  homepageUrl: z.ZodOptional<z.ZodString>;
7608
7623
  returnUrl: z.ZodOptional<z.ZodString>;
@@ -9305,6 +9320,46 @@ export declare function createRaftClientFromStore(options: CreateRaftClientFromS
9305
9320
  */
9306
9321
  export declare function createFileCredentialStore(filePath: string): RaftCredentialStore;
9307
9322
  //#endregion
9323
+ //#region src/latestReadThread.d.ts
9324
+ interface ReadLatestReadThreadOptions {
9325
+ /** The agent whose reads to look at. Defaults to `SLOCK_AGENT_ID` from `env`. */
9326
+ agentId?: string;
9327
+ /**
9328
+ * The Raft home directory the CLI uses. Defaults to what the CLI resolves
9329
+ * from `env`: `RAFT_HOME`, then `SLOCK_HOME`, then `~/.slock`.
9330
+ */
9331
+ home?: string;
9332
+ /** Environment to resolve the defaults from. Defaults to `process.env`. */
9333
+ env?: Record<string, string | undefined>;
9334
+ }
9335
+ type RaftLatestReadThread = {
9336
+ state: "thread";
9337
+ /** The thread target, as `raft message read --target` takes it, for example `#general:1a2b3c4d`. */
9338
+ target: string;
9339
+ /** The channel or DM the thread belongs to, for example `#general`. */
9340
+ parentTarget: string;
9341
+ } | {
9342
+ state: "none";
9343
+ /**
9344
+ * - `no_agent_id`: no `agentId` was given and `SLOCK_AGENT_ID` is not set (or is not a valid id).
9345
+ * - `no_record`: the CLI has kept no read record for this agent here.
9346
+ * - `unreadable`: a record exists but is not a private regular file of this user, is too large, or is not JSON.
9347
+ * - `no_reads`: the record holds no read.
9348
+ * - `latest_read_is_not_a_thread`: the latest read was a channel or DM root. An older thread is never substituted.
9349
+ */
9350
+ reason: "no_agent_id" | "no_record" | "unreadable" | "no_reads" | "latest_read_is_not_a_thread";
9351
+ };
9352
+ /**
9353
+ * The thread this agent read most recently with `raft message read`, when
9354
+ * that read is still the latest read of any conversation.
9355
+ *
9356
+ * It is a fact about what was read, not about what the work belongs to: an
9357
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
9358
+ * answer as a default to confirm, and keep private channel names out of
9359
+ * public places.
9360
+ */
9361
+ export declare function readLatestReadThread(options?: ReadLatestReadThreadOptions): Promise<RaftLatestReadThread>;
9362
+ //#endregion
9308
9363
  //#region src/raft.d.ts
9309
9364
  interface CreateRaftOptions {
9310
9365
  /** Raft Server origin, for example `https://api.raft.build`. */
@@ -9534,4 +9589,4 @@ interface Raft {
9534
9589
  }
9535
9590
  export declare function createRaft(options: CreateRaftOptions): Raft;
9536
9591
  //#endregion
9537
- export type { AmendTaskOutcome, AmendTaskRequest, AssignTaskRequest, BootstrapRaftCredentialOptions, CheckInboxOutcome, CheckInboxRequest, ClaimTasksOutcome, ClaimTasksRequest, CreateRaftClientFromStoreOptions, CreateRaftClientOptions, CreateRaftOptions, CreateRaftRoutesOptions, CreateTasksRequest, DrainInboxRequest, ExecuteMentionActionRequest, ListInboxRequest, ListTasksRequest, ManualContext, PrepareActionCardRequest, 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, RaftPreparedCard, 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 };
9592
+ export type { AmendTaskOutcome, AmendTaskRequest, AssignTaskRequest, BootstrapRaftCredentialOptions, CheckInboxOutcome, CheckInboxRequest, ClaimTasksOutcome, ClaimTasksRequest, CreateRaftClientFromStoreOptions, CreateRaftClientOptions, CreateRaftOptions, CreateRaftRoutesOptions, CreateTasksRequest, DrainInboxRequest, ExecuteMentionActionRequest, ListInboxRequest, ListTasksRequest, ManualContext, PrepareActionCardRequest, 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, RaftLatestReadThread, RaftManageClient, RaftMessage, RaftMessageAttachment, RaftMessageTask, RaftNextStep, RaftNoticeFlag, RaftNoticeTarget, RaftOpError, RaftOpErrorCode, RaftOutcome, RaftPendingMentions, RaftPreparedCard, 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, ReadLatestReadThreadOptions, 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": "0.3.0",
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",