@botiverse/raft-sdk 0.3.0 → 0.3.1

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,
@@ -11192,6 +11195,73 @@ var SeenFrontier = class SeenFrontier {
11192
11195
  }
11193
11196
  };
11194
11197
  //#endregion
11198
+ //#region ../shared/src/agentOps/latestReadThread.ts
11199
+ const CLI_READ_STATE_NAMESPACE = "slock-cli-consumed-seq";
11200
+ const CLI_READ_STATE_FILENAME = "consumed-seqs.json";
11201
+ const RAFT_HOME_ENVS = ["RAFT_HOME", "SLOCK_HOME"];
11202
+ /** The Raft home the environment configures, or undefined when the default (`~/.slock`) applies. */
11203
+ function configuredRaftHome(env) {
11204
+ for (const name of RAFT_HOME_ENVS) {
11205
+ const value = env[name]?.trim();
11206
+ if (value) return value;
11207
+ }
11208
+ }
11209
+ /** The base directory the environment configures for the read record, or undefined when the default applies. */
11210
+ function configuredCliReadStateBase(env) {
11211
+ return env["SLOCK_CLI_CONSUMED_SEQ_STATE_DIR"]?.trim() || configuredRaftHome(env);
11212
+ }
11213
+ /** An agent id is a path segment of the record's location, so it must be a plain one. */
11214
+ function isLocalStateAgentId(agentId) {
11215
+ return /^[A-Za-z0-9_-]+$/.test(agentId);
11216
+ }
11217
+ /** The record's location under the base directory, as path segments. */
11218
+ function cliReadStatePathSegments(agentId) {
11219
+ if (!isLocalStateAgentId(agentId)) throw new Error("Invalid local state agent identity");
11220
+ return [
11221
+ CLI_READ_STATE_NAMESPACE,
11222
+ agentId,
11223
+ CLI_READ_STATE_FILENAME
11224
+ ];
11225
+ }
11226
+ function positiveFinite(value) {
11227
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
11228
+ }
11229
+ /** `#channel:thread` → `#channel`, `dm:@peer:thread` → `dm:@peer`; null for a channel or DM root. */
11230
+ function parentTargetOfThread(target) {
11231
+ const from = target.startsWith("dm:@") ? 4 : target.startsWith("#") ? 1 : -1;
11232
+ if (from < 0) return null;
11233
+ const separator = target.indexOf(":", from);
11234
+ return separator > 0 && separator < target.length - 1 ? target.slice(0, separator) : null;
11235
+ }
11236
+ function latestReadThreadFromCliReadState(raw) {
11237
+ const targets = raw && typeof raw === "object" ? raw.targets : void 0;
11238
+ let latestTarget;
11239
+ let latestOrder = 0;
11240
+ if (targets && typeof targets === "object" && !Array.isArray(targets)) for (const [target, value] of Object.entries(targets)) {
11241
+ if (target.length === 0) continue;
11242
+ const record = typeof value === "number" ? { seq: value } : value && typeof value === "object" ? value : {};
11243
+ const order = positiveFinite(record.readOrder) ?? positiveFinite(record.seq);
11244
+ if (order !== void 0 && order > latestOrder) {
11245
+ latestOrder = order;
11246
+ latestTarget = target;
11247
+ }
11248
+ }
11249
+ if (latestTarget === void 0) return {
11250
+ state: "none",
11251
+ reason: "no_reads"
11252
+ };
11253
+ const parentTarget = parentTargetOfThread(latestTarget);
11254
+ if (parentTarget === null) return {
11255
+ state: "none",
11256
+ reason: "latest_read_is_not_a_thread"
11257
+ };
11258
+ return {
11259
+ state: "thread",
11260
+ target: latestTarget,
11261
+ parentTarget
11262
+ };
11263
+ }
11264
+ //#endregion
11195
11265
  //#region ../shared/src/agentOps/inbox.ts
11196
11266
  const MAX_DRAIN_ROUNDS = 50;
11197
11267
  function batchNext(batch) {
@@ -12308,7 +12378,7 @@ function formatAgentServerInfo(data) {
12308
12378
  text += "Server-profile changes still use raft server update and remain server-role gated.\n";
12309
12379
  text += "Mute state is shown when the server provides it; otherwise it is omitted.\n";
12310
12380
  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"];
12381
+ const statusParts = [channelVisibility(t), t.joined ? "joined" : "not joined"];
12312
12382
  if (t.channelRole) statusParts.push(`channel role=${t.channelRole}`);
12313
12383
  if (t.channelAdminBasis) statusParts.push(`admin via=${t.channelAdminBasis}`);
12314
12384
  const muted = channelMuted(t);
@@ -12343,6 +12413,7 @@ function agentChannelRef(name, type) {
12343
12413
  }
12344
12414
  function channelVisibility(channel) {
12345
12415
  const type = channel.type?.trim();
12416
+ if (type === "joint") return "joint";
12346
12417
  return type === "private" || type === "dm" ? "private" : "public";
12347
12418
  }
12348
12419
  function channelMuted(channel) {
@@ -13923,12 +13994,67 @@ async function prepareActionCard(client, request) {
13923
13994
  target: request.target,
13924
13995
  messageId: card.messageId
13925
13996
  },
13926
- why: "A human must click the card to commit it; its outcome arrives in your inbox."
13997
+ 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
13998
  },
13928
13999
  text: formatAgentActionCardPosted(request.target, card.messageId)
13929
14000
  };
13930
14001
  }
13931
14002
  //#endregion
14003
+ //#region src/latestReadThread.ts
14004
+ const MAX_READ_STATE_BYTES = 33554432;
14005
+ function firstNonEmpty(...values) {
14006
+ for (const value of values) {
14007
+ const trimmed = value?.trim();
14008
+ if (trimmed) return trimmed;
14009
+ }
14010
+ }
14011
+ /**
14012
+ * The thread this agent read most recently with `raft message read`, when
14013
+ * that read is still the latest read of any conversation.
14014
+ *
14015
+ * It is a fact about what was read, not about what the work belongs to: an
14016
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
14017
+ * answer as a default to confirm, and keep private channel names out of
14018
+ * public places.
14019
+ */
14020
+ async function readLatestReadThread(options = {}) {
14021
+ const env = options.env ?? process.env;
14022
+ const agentId = firstNonEmpty(options.agentId, env.SLOCK_AGENT_ID);
14023
+ if (!agentId || !isLocalStateAgentId(agentId)) return {
14024
+ state: "none",
14025
+ reason: "no_agent_id"
14026
+ };
14027
+ const base = firstNonEmpty(options.home, configuredCliReadStateBase(env)) ?? (0, node_path.join)((0, node_os.homedir)(), ".slock");
14028
+ const filePath = (0, node_path.join)(base, ...cliReadStatePathSegments(agentId));
14029
+ let handle;
14030
+ try {
14031
+ handle = await (0, node_fs_promises.open)(filePath, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW);
14032
+ } catch (error) {
14033
+ return {
14034
+ state: "none",
14035
+ reason: error.code === "ENOENT" ? "no_record" : "unreadable"
14036
+ };
14037
+ }
14038
+ let raw;
14039
+ try {
14040
+ const stat = await handle.stat();
14041
+ const ownedByUser = typeof process.getuid !== "function" || stat.uid === process.getuid() && (stat.mode & 63) === 0;
14042
+ if (!stat.isFile() || !ownedByUser || stat.size > MAX_READ_STATE_BYTES) return {
14043
+ state: "none",
14044
+ reason: "unreadable"
14045
+ };
14046
+ raw = JSON.parse(await handle.readFile("utf8"));
14047
+ } catch {
14048
+ return {
14049
+ state: "none",
14050
+ reason: "unreadable"
14051
+ };
14052
+ } finally {
14053
+ await handle.close();
14054
+ }
14055
+ return latestReadThreadFromCliReadState(raw);
14056
+ }
14057
+ //#endregion
13932
14058
  //#region src/raft.ts
13933
14059
  function createRaft(options) {
13934
14060
  const serverUrl = requireServerUrl(options.serverUrl);
@@ -14134,4 +14260,5 @@ exports.parseRaftRegularChannelTarget = parseRaftRegularChannelTarget;
14134
14260
  exports.parseRaftState = parseRaftState;
14135
14261
  exports.projectRaftMessage = projectRaftMessage;
14136
14262
  exports.projectRaftMessages = projectRaftMessages;
14263
+ exports.readLatestReadThread = readLatestReadThread;
14137
14264
  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,
@@ -11191,6 +11194,73 @@ var SeenFrontier = class SeenFrontier {
11191
11194
  }
11192
11195
  };
11193
11196
  //#endregion
11197
+ //#region ../shared/src/agentOps/latestReadThread.ts
11198
+ const CLI_READ_STATE_NAMESPACE = "slock-cli-consumed-seq";
11199
+ const CLI_READ_STATE_FILENAME = "consumed-seqs.json";
11200
+ const RAFT_HOME_ENVS = ["RAFT_HOME", "SLOCK_HOME"];
11201
+ /** The Raft home the environment configures, or undefined when the default (`~/.slock`) applies. */
11202
+ function configuredRaftHome(env) {
11203
+ for (const name of RAFT_HOME_ENVS) {
11204
+ const value = env[name]?.trim();
11205
+ if (value) return value;
11206
+ }
11207
+ }
11208
+ /** The base directory the environment configures for the read record, or undefined when the default applies. */
11209
+ function configuredCliReadStateBase(env) {
11210
+ return env["SLOCK_CLI_CONSUMED_SEQ_STATE_DIR"]?.trim() || configuredRaftHome(env);
11211
+ }
11212
+ /** An agent id is a path segment of the record's location, so it must be a plain one. */
11213
+ function isLocalStateAgentId(agentId) {
11214
+ return /^[A-Za-z0-9_-]+$/.test(agentId);
11215
+ }
11216
+ /** The record's location under the base directory, as path segments. */
11217
+ function cliReadStatePathSegments(agentId) {
11218
+ if (!isLocalStateAgentId(agentId)) throw new Error("Invalid local state agent identity");
11219
+ return [
11220
+ CLI_READ_STATE_NAMESPACE,
11221
+ agentId,
11222
+ CLI_READ_STATE_FILENAME
11223
+ ];
11224
+ }
11225
+ function positiveFinite(value) {
11226
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
11227
+ }
11228
+ /** `#channel:thread` → `#channel`, `dm:@peer:thread` → `dm:@peer`; null for a channel or DM root. */
11229
+ function parentTargetOfThread(target) {
11230
+ const from = target.startsWith("dm:@") ? 4 : target.startsWith("#") ? 1 : -1;
11231
+ if (from < 0) return null;
11232
+ const separator = target.indexOf(":", from);
11233
+ return separator > 0 && separator < target.length - 1 ? target.slice(0, separator) : null;
11234
+ }
11235
+ function latestReadThreadFromCliReadState(raw) {
11236
+ const targets = raw && typeof raw === "object" ? raw.targets : void 0;
11237
+ let latestTarget;
11238
+ let latestOrder = 0;
11239
+ if (targets && typeof targets === "object" && !Array.isArray(targets)) for (const [target, value] of Object.entries(targets)) {
11240
+ if (target.length === 0) continue;
11241
+ const record = typeof value === "number" ? { seq: value } : value && typeof value === "object" ? value : {};
11242
+ const order = positiveFinite(record.readOrder) ?? positiveFinite(record.seq);
11243
+ if (order !== void 0 && order > latestOrder) {
11244
+ latestOrder = order;
11245
+ latestTarget = target;
11246
+ }
11247
+ }
11248
+ if (latestTarget === void 0) return {
11249
+ state: "none",
11250
+ reason: "no_reads"
11251
+ };
11252
+ const parentTarget = parentTargetOfThread(latestTarget);
11253
+ if (parentTarget === null) return {
11254
+ state: "none",
11255
+ reason: "latest_read_is_not_a_thread"
11256
+ };
11257
+ return {
11258
+ state: "thread",
11259
+ target: latestTarget,
11260
+ parentTarget
11261
+ };
11262
+ }
11263
+ //#endregion
11194
11264
  //#region ../shared/src/agentOps/inbox.ts
11195
11265
  const MAX_DRAIN_ROUNDS = 50;
11196
11266
  function batchNext(batch) {
@@ -12307,7 +12377,7 @@ function formatAgentServerInfo(data) {
12307
12377
  text += "Server-profile changes still use raft server update and remain server-role gated.\n";
12308
12378
  text += "Mute state is shown when the server provides it; otherwise it is omitted.\n";
12309
12379
  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"];
12380
+ const statusParts = [channelVisibility(t), t.joined ? "joined" : "not joined"];
12311
12381
  if (t.channelRole) statusParts.push(`channel role=${t.channelRole}`);
12312
12382
  if (t.channelAdminBasis) statusParts.push(`admin via=${t.channelAdminBasis}`);
12313
12383
  const muted = channelMuted(t);
@@ -12342,6 +12412,7 @@ function agentChannelRef(name, type) {
12342
12412
  }
12343
12413
  function channelVisibility(channel) {
12344
12414
  const type = channel.type?.trim();
12415
+ if (type === "joint") return "joint";
12345
12416
  return type === "private" || type === "dm" ? "private" : "public";
12346
12417
  }
12347
12418
  function channelMuted(channel) {
@@ -13922,12 +13993,67 @@ async function prepareActionCard(client, request) {
13922
13993
  target: request.target,
13923
13994
  messageId: card.messageId
13924
13995
  },
13925
- why: "A human must click the card to commit it; its outcome arrives in your inbox."
13996
+ 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
13997
  },
13927
13998
  text: formatAgentActionCardPosted(request.target, card.messageId)
13928
13999
  };
13929
14000
  }
13930
14001
  //#endregion
14002
+ //#region src/latestReadThread.ts
14003
+ const MAX_READ_STATE_BYTES = 33554432;
14004
+ function firstNonEmpty(...values) {
14005
+ for (const value of values) {
14006
+ const trimmed = value?.trim();
14007
+ if (trimmed) return trimmed;
14008
+ }
14009
+ }
14010
+ /**
14011
+ * The thread this agent read most recently with `raft message read`, when
14012
+ * that read is still the latest read of any conversation.
14013
+ *
14014
+ * It is a fact about what was read, not about what the work belongs to: an
14015
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
14016
+ * answer as a default to confirm, and keep private channel names out of
14017
+ * public places.
14018
+ */
14019
+ async function readLatestReadThread(options = {}) {
14020
+ const env = options.env ?? process.env;
14021
+ const agentId = firstNonEmpty(options.agentId, env.SLOCK_AGENT_ID);
14022
+ if (!agentId || !isLocalStateAgentId(agentId)) return {
14023
+ state: "none",
14024
+ reason: "no_agent_id"
14025
+ };
14026
+ const base = firstNonEmpty(options.home, configuredCliReadStateBase(env)) ?? join(homedir(), ".slock");
14027
+ const filePath = join(base, ...cliReadStatePathSegments(agentId));
14028
+ let handle;
14029
+ try {
14030
+ handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
14031
+ } catch (error) {
14032
+ return {
14033
+ state: "none",
14034
+ reason: error.code === "ENOENT" ? "no_record" : "unreadable"
14035
+ };
14036
+ }
14037
+ let raw;
14038
+ try {
14039
+ const stat = await handle.stat();
14040
+ const ownedByUser = typeof process.getuid !== "function" || stat.uid === process.getuid() && (stat.mode & 63) === 0;
14041
+ if (!stat.isFile() || !ownedByUser || stat.size > MAX_READ_STATE_BYTES) return {
14042
+ state: "none",
14043
+ reason: "unreadable"
14044
+ };
14045
+ raw = JSON.parse(await handle.readFile("utf8"));
14046
+ } catch {
14047
+ return {
14048
+ state: "none",
14049
+ reason: "unreadable"
14050
+ };
14051
+ } finally {
14052
+ await handle.close();
14053
+ }
14054
+ return latestReadThreadFromCliReadState(raw);
14055
+ }
14056
+ //#endregion
13931
14057
  //#region src/raft.ts
13932
14058
  function createRaft(options) {
13933
14059
  const serverUrl = requireServerUrl(options.serverUrl);
@@ -14108,4 +14234,4 @@ function createRaft(options) {
14108
14234
  };
14109
14235
  }
14110
14236
  //#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 };
14237
+ 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
@@ -1857,6 +1857,7 @@ declare const agentApiIntegrationAppUpdateBodySchema: z.ZodObject<{
1857
1857
  clientKey: z.ZodString;
1858
1858
  name: z.ZodOptional<z.ZodString>;
1859
1859
  description: z.ZodOptional<z.ZodString>;
1860
+ whenToUse: z.ZodOptional<z.ZodString>;
1860
1861
  category: z.ZodOptional<z.ZodString>;
1861
1862
  homepageUrl: z.ZodOptional<z.ZodString>;
1862
1863
  returnUrl: z.ZodOptional<z.ZodString>;
@@ -2120,6 +2121,7 @@ declare const agentApiIntegrationListResponseSchema: z.ZodObject<{
2120
2121
  }>>;
2121
2122
  name: z.ZodString;
2122
2123
  description: z.ZodNullable<z.ZodString>;
2124
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2123
2125
  homepageUrl: z.ZodNullable<z.ZodString>;
2124
2126
  returnUrl: z.ZodNullable<z.ZodString>;
2125
2127
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -2198,6 +2200,7 @@ declare const agentApiIntegrationLoginResponseSchema: z.ZodObject<{
2198
2200
  }>>;
2199
2201
  name: z.ZodString;
2200
2202
  description: z.ZodNullable<z.ZodString>;
2203
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2201
2204
  homepageUrl: z.ZodNullable<z.ZodString>;
2202
2205
  returnUrl: z.ZodNullable<z.ZodString>;
2203
2206
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7291,6 +7294,7 @@ declare const agentApiContract: {
7291
7294
  }>>;
7292
7295
  name: z.ZodString;
7293
7296
  description: z.ZodNullable<z.ZodString>;
7297
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7294
7298
  homepageUrl: z.ZodNullable<z.ZodString>;
7295
7299
  returnUrl: z.ZodNullable<z.ZodString>;
7296
7300
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7412,6 +7416,7 @@ declare const agentApiContract: {
7412
7416
  }>>;
7413
7417
  name: z.ZodString;
7414
7418
  description: z.ZodNullable<z.ZodString>;
7419
+ whenToUse: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7415
7420
  homepageUrl: z.ZodNullable<z.ZodString>;
7416
7421
  returnUrl: z.ZodNullable<z.ZodString>;
7417
7422
  agentManifestUrl: z.ZodNullable<z.ZodString>;
@@ -7603,6 +7608,7 @@ declare const agentApiContract: {
7603
7608
  clientKey: z.ZodString;
7604
7609
  name: z.ZodOptional<z.ZodString>;
7605
7610
  description: z.ZodOptional<z.ZodString>;
7611
+ whenToUse: z.ZodOptional<z.ZodString>;
7606
7612
  category: z.ZodOptional<z.ZodString>;
7607
7613
  homepageUrl: z.ZodOptional<z.ZodString>;
7608
7614
  returnUrl: z.ZodOptional<z.ZodString>;
@@ -9305,6 +9311,46 @@ export declare function createRaftClientFromStore(options: CreateRaftClientFromS
9305
9311
  */
9306
9312
  export declare function createFileCredentialStore(filePath: string): RaftCredentialStore;
9307
9313
  //#endregion
9314
+ //#region src/latestReadThread.d.ts
9315
+ interface ReadLatestReadThreadOptions {
9316
+ /** The agent whose reads to look at. Defaults to `SLOCK_AGENT_ID` from `env`. */
9317
+ agentId?: string;
9318
+ /**
9319
+ * The Raft home directory the CLI uses. Defaults to what the CLI resolves
9320
+ * from `env`: `RAFT_HOME`, then `SLOCK_HOME`, then `~/.slock`.
9321
+ */
9322
+ home?: string;
9323
+ /** Environment to resolve the defaults from. Defaults to `process.env`. */
9324
+ env?: Record<string, string | undefined>;
9325
+ }
9326
+ type RaftLatestReadThread = {
9327
+ state: "thread";
9328
+ /** The thread target, as `raft message read --target` takes it, for example `#general:1a2b3c4d`. */
9329
+ target: string;
9330
+ /** The channel or DM the thread belongs to, for example `#general`. */
9331
+ parentTarget: string;
9332
+ } | {
9333
+ state: "none";
9334
+ /**
9335
+ * - `no_agent_id`: no `agentId` was given and `SLOCK_AGENT_ID` is not set (or is not a valid id).
9336
+ * - `no_record`: the CLI has kept no read record for this agent here.
9337
+ * - `unreadable`: a record exists but is not a private regular file of this user, is too large, or is not JSON.
9338
+ * - `no_reads`: the record holds no read.
9339
+ * - `latest_read_is_not_a_thread`: the latest read was a channel or DM root. An older thread is never substituted.
9340
+ */
9341
+ reason: "no_agent_id" | "no_record" | "unreadable" | "no_reads" | "latest_read_is_not_a_thread";
9342
+ };
9343
+ /**
9344
+ * The thread this agent read most recently with `raft message read`, when
9345
+ * that read is still the latest read of any conversation.
9346
+ *
9347
+ * It is a fact about what was read, not about what the work belongs to: an
9348
+ * agent that read an unrelated thread afterwards gets that thread. Treat the
9349
+ * answer as a default to confirm, and keep private channel names out of
9350
+ * public places.
9351
+ */
9352
+ export declare function readLatestReadThread(options?: ReadLatestReadThreadOptions): Promise<RaftLatestReadThread>;
9353
+ //#endregion
9308
9354
  //#region src/raft.d.ts
9309
9355
  interface CreateRaftOptions {
9310
9356
  /** Raft Server origin, for example `https://api.raft.build`. */
@@ -9534,4 +9580,4 @@ interface Raft {
9534
9580
  }
9535
9581
  export declare function createRaft(options: CreateRaftOptions): Raft;
9536
9582
  //#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 };
9583
+ 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.1",
4
4
  "license": "FSL-1.1-ALv2",
5
5
  "description": "Typed Raft Agent API client for external agents and bots.",
6
6
  "type": "module",