@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 +50 -0
- package/dist/cjs/index.cjs +183 -12
- package/dist/esm/index.js +184 -14
- package/dist/index.d.ts +57 -2
- package/package.json +1 -1
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
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
11625
|
+
...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
|
|
11514
11626
|
},
|
|
11515
|
-
newMessageCount
|
|
11627
|
+
newMessageCount,
|
|
11516
11628
|
heldMessages,
|
|
11517
|
-
omittedMessageCount
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
11624
|
+
...contextComplete && seenUpToSeq !== null ? { seen: { upToSeq: seenUpToSeq } } : {}
|
|
11513
11625
|
},
|
|
11514
|
-
newMessageCount
|
|
11626
|
+
newMessageCount,
|
|
11515
11627
|
heldMessages,
|
|
11516
|
-
omittedMessageCount
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
|
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 };
|