@botiverse/raft-sdk 0.7.0 → 0.9.0
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 +49 -9
- package/dist/cjs/index.cjs +305 -18
- package/dist/esm/index.js +304 -19
- package/dist/index.d.ts +173 -8
- package/operations.json +136 -3
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -4712,7 +4712,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4712
4712
|
mentionActionsExecute: write,
|
|
4713
4713
|
taskClaim: write,
|
|
4714
4714
|
taskList: read,
|
|
4715
|
-
taskCreate:
|
|
4715
|
+
taskCreate: keyedWrite,
|
|
4716
4716
|
taskUnclaim: destructive,
|
|
4717
4717
|
taskAssign: naturalDestructive,
|
|
4718
4718
|
taskUpdateStatus: naturalDestructive,
|
|
@@ -4744,7 +4744,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4744
4744
|
integrationAppLogoUpdate: naturalDestructive,
|
|
4745
4745
|
integrationAppList: read,
|
|
4746
4746
|
integrationAppStatus: read,
|
|
4747
|
-
actionPrepare:
|
|
4747
|
+
actionPrepare: keyedWrite,
|
|
4748
4748
|
attachmentUpload: write,
|
|
4749
4749
|
attachmentUploadCapabilities: read,
|
|
4750
4750
|
attachmentUploadSessionCreate: write,
|
|
@@ -5395,7 +5395,15 @@ const agentApiTaskCreateBodySchema = passthroughObject({
|
|
|
5395
5395
|
title: string().trim().min(1),
|
|
5396
5396
|
creates_resource: boolean().optional()
|
|
5397
5397
|
})).min(1),
|
|
5398
|
-
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional()
|
|
5398
|
+
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional(),
|
|
5399
|
+
/**
|
|
5400
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5401
|
+
* the same request replays the first response without creating anything;
|
|
5402
|
+
* the same key with a different request is refused (409
|
|
5403
|
+
* `idempotency_key_reused`). Scoped to the agent and this route, and valid
|
|
5404
|
+
* for 24 hours; after that the key is forgotten and is a new request.
|
|
5405
|
+
*/
|
|
5406
|
+
idempotencyKey: optionalStringSchema
|
|
5399
5407
|
});
|
|
5400
5408
|
const agentApiTaskUnclaimBodySchema = passthroughObject({
|
|
5401
5409
|
channel: string().trim().min(1),
|
|
@@ -5646,7 +5654,15 @@ const agentApiIntegrationAppStatusQuerySchema = passthroughObject({
|
|
|
5646
5654
|
});
|
|
5647
5655
|
const agentApiActionPrepareBodySchema = passthroughObject({
|
|
5648
5656
|
target: string().trim().min(1),
|
|
5649
|
-
action: actionCardActionSchema
|
|
5657
|
+
action: actionCardActionSchema,
|
|
5658
|
+
/**
|
|
5659
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5660
|
+
* the same request replays the first response (same card messageId)
|
|
5661
|
+
* without preparing another card; the same key with a different request is
|
|
5662
|
+
* refused (409 `idempotency_key_reused`). Scoped to the agent and this
|
|
5663
|
+
* route, and valid for 24 hours; after that the key is forgotten.
|
|
5664
|
+
*/
|
|
5665
|
+
idempotencyKey: optionalStringSchema
|
|
5650
5666
|
});
|
|
5651
5667
|
const agentApiServerUpdateBodySchema = passthroughObject({
|
|
5652
5668
|
name: string().trim().min(1).max(100).optional(),
|
|
@@ -8727,7 +8743,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
8727
8743
|
"capability": "tasks",
|
|
8728
8744
|
"description": "Create one or more tasks in a channel.",
|
|
8729
8745
|
"sideEffect": "write",
|
|
8730
|
-
"idempotency": "
|
|
8746
|
+
"idempotency": "key",
|
|
8731
8747
|
"destructive": false,
|
|
8732
8748
|
"audience": "both",
|
|
8733
8749
|
"request": {
|
|
@@ -9527,7 +9543,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9527
9543
|
"capability": "tasks",
|
|
9528
9544
|
"description": "Prepare an action card for a human to commit.",
|
|
9529
9545
|
"sideEffect": "write",
|
|
9530
|
-
"idempotency": "
|
|
9546
|
+
"idempotency": "key",
|
|
9531
9547
|
"destructive": false,
|
|
9532
9548
|
"audience": "both",
|
|
9533
9549
|
"request": {
|
|
@@ -9842,7 +9858,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9842
9858
|
}
|
|
9843
9859
|
];
|
|
9844
9860
|
/** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
|
|
9845
|
-
const AGENT_API_MANIFEST_VERSION = "
|
|
9861
|
+
const AGENT_API_MANIFEST_VERSION = "2a3c2201ea57a9d1";
|
|
9846
9862
|
//#endregion
|
|
9847
9863
|
//#region src/routes.ts
|
|
9848
9864
|
const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
|
|
@@ -10448,7 +10464,7 @@ const DEFAULT_MESSAGES = {
|
|
|
10448
10464
|
SCOPE_DENIED: "This agent's scope set does not allow this operation.",
|
|
10449
10465
|
NOT_FOUND: "The target or message does not exist or is not visible to this agent.",
|
|
10450
10466
|
CONFLICT: "The Raft Server refused the operation because of the current state.",
|
|
10451
|
-
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different
|
|
10467
|
+
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different request.",
|
|
10452
10468
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10453
10469
|
UNAVAILABLE: "The Raft Server could not serve this operation right now.",
|
|
10454
10470
|
MODEL_ONLY: "This operation only counts when the model sees its result, so it cannot be run from code; nothing was sent."
|
|
@@ -10462,7 +10478,7 @@ const DEFAULT_NEXT_ACTION = {
|
|
|
10462
10478
|
SCOPE_DENIED: "Ask a human with editAgents authority to extend this agent's scopes.",
|
|
10463
10479
|
NOT_FOUND: "Check the target spelling with `raft server info --channels` or resolve the message id first.",
|
|
10464
10480
|
CONFLICT: "Read the current state before repeating this operation.",
|
|
10465
|
-
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different
|
|
10481
|
+
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different request, or resend the identical request to reconcile.",
|
|
10466
10482
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10467
10483
|
UNAVAILABLE: "Retry in a moment.",
|
|
10468
10484
|
MODEL_ONLY: "Call it as a model tool call instead of from code."
|
|
@@ -10551,6 +10567,24 @@ function failureOutcome(error) {
|
|
|
10551
10567
|
text: formatOpErrorText(error)
|
|
10552
10568
|
};
|
|
10553
10569
|
}
|
|
10570
|
+
/**
|
|
10571
|
+
* A failure of a keyed write (`idempotencyKey`). A retryable failure (the
|
|
10572
|
+
* request may not have reached the Server, or the Server was unavailable)
|
|
10573
|
+
* says to repeat the SAME request with the SAME key, and carries the key in
|
|
10574
|
+
* `next.args.idempotencyKey`, so a caller that let the SDK generate it can
|
|
10575
|
+
* still retry without acting twice.
|
|
10576
|
+
*/
|
|
10577
|
+
function keyedWriteFailure(failure, idempotencyKey) {
|
|
10578
|
+
if (!failure.error.retryable) return failure;
|
|
10579
|
+
return {
|
|
10580
|
+
...failure,
|
|
10581
|
+
next: {
|
|
10582
|
+
kind: "retry_same_key",
|
|
10583
|
+
args: { idempotencyKey },
|
|
10584
|
+
why: "Repeat the same request with this idempotencyKey: if the first attempt was committed, the Server returns its result instead of acting twice."
|
|
10585
|
+
}
|
|
10586
|
+
};
|
|
10587
|
+
}
|
|
10554
10588
|
/** Map a shared-client failure to a failure outcome. */
|
|
10555
10589
|
function failureFromClientResult(result) {
|
|
10556
10590
|
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
@@ -12078,6 +12112,22 @@ function formatAgentTaskHistory(data) {
|
|
|
12078
12112
|
return `seq=${event.seq} time=${event.createdAt} actor=${actor} type=${event.eventType}\n ${JSON.stringify(event.payload)}`;
|
|
12079
12113
|
}).join("\n")}`;
|
|
12080
12114
|
}
|
|
12115
|
+
/**
|
|
12116
|
+
* `raft task show`: one task's current title and description (moved verbatim
|
|
12117
|
+
* from the CLI's commands/task/show.ts). Labels match the agent delivery
|
|
12118
|
+
* surface (`Current title:` / `Current description:`). `description` has three
|
|
12119
|
+
* states that must not collapse: a string, an explicit null, and a field the
|
|
12120
|
+
* envelope omitted.
|
|
12121
|
+
*/
|
|
12122
|
+
function formatAgentTaskShow(target, task) {
|
|
12123
|
+
const descriptionLine = typeof task.description === "string" ? `Current description: ${task.description}` : task.description === null ? "Current description: (none set)" : "Current description: (not returned by this surface)";
|
|
12124
|
+
return [
|
|
12125
|
+
`#${task.taskNumber} [${task.status ?? "unknown"}] in ${target}`,
|
|
12126
|
+
`Current title: ${task.title ?? "(none set)"}`,
|
|
12127
|
+
descriptionLine,
|
|
12128
|
+
""
|
|
12129
|
+
].join("\n");
|
|
12130
|
+
}
|
|
12081
12131
|
//#endregion
|
|
12082
12132
|
//#region ../shared/src/agentOps/tasks.ts
|
|
12083
12133
|
const taskChannelSchema = string().describe("The channel the task board belongs to, for example `#proj-sdk`.");
|
|
@@ -12250,21 +12300,24 @@ const createTasksRequestSchema = requestSchema()(object({
|
|
|
12250
12300
|
title: string().describe("Task title."),
|
|
12251
12301
|
createsResource: boolean().optional().describe("The task produces a resource (for example a document) that needs a receipt.")
|
|
12252
12302
|
})).describe("One entry per task to create."),
|
|
12253
|
-
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo.")
|
|
12303
|
+
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo."),
|
|
12304
|
+
idempotencyKey: string().optional().describe("One key per logical create; generated when omitted and returned. Repeat the same request with the same key within 24 hours to retry without creating the tasks twice.")
|
|
12254
12305
|
}));
|
|
12255
12306
|
async function createTasks(client, request) {
|
|
12256
12307
|
const invalid = validateOpRequest(createTasksRequestSchema, request);
|
|
12257
12308
|
if (invalid) return invalid;
|
|
12258
12309
|
if (!request.target?.trim() || !request.tasks?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and at least one task title are required." }));
|
|
12310
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
12259
12311
|
const result = await client.tasks.create({
|
|
12260
12312
|
channel: request.target,
|
|
12261
12313
|
tasks: request.tasks.map((t) => ({
|
|
12262
12314
|
title: t.title,
|
|
12263
12315
|
...t.createsResource ? { creates_resource: true } : {}
|
|
12264
12316
|
})),
|
|
12265
|
-
...request.assignee ? { assignee: request.assignee } : {}
|
|
12317
|
+
...request.assignee ? { assignee: request.assignee } : {},
|
|
12318
|
+
idempotencyKey
|
|
12266
12319
|
});
|
|
12267
|
-
if (!result.ok) return failureFromClientResult(result);
|
|
12320
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
12268
12321
|
const data = result.data;
|
|
12269
12322
|
const first = data.tasks[0];
|
|
12270
12323
|
return {
|
|
@@ -12272,7 +12325,8 @@ async function createTasks(client, request) {
|
|
|
12272
12325
|
state: "created",
|
|
12273
12326
|
data: {
|
|
12274
12327
|
...data,
|
|
12275
|
-
target: request.target
|
|
12328
|
+
target: request.target,
|
|
12329
|
+
idempotencyKey
|
|
12276
12330
|
},
|
|
12277
12331
|
next: first ? {
|
|
12278
12332
|
kind: "post_in_task_thread",
|
|
@@ -12467,6 +12521,38 @@ async function taskHistory(client, request) {
|
|
|
12467
12521
|
text: formatAgentTaskHistory(result.data)
|
|
12468
12522
|
};
|
|
12469
12523
|
}
|
|
12524
|
+
/**
|
|
12525
|
+
* `raft task show`: one task's current title and description. Reads the
|
|
12526
|
+
* channel's whole board (`status: "all"`, so done and closed tasks are found)
|
|
12527
|
+
* and picks the task; a miss says whether the Server asserted the list is
|
|
12528
|
+
* complete, exactly as the CLI does.
|
|
12529
|
+
*/
|
|
12530
|
+
async function showTask(client, request) {
|
|
12531
|
+
const invalid = requireTaskRef(request);
|
|
12532
|
+
if (invalid) return invalid;
|
|
12533
|
+
const { target, taskNumber } = request;
|
|
12534
|
+
const result = await client.tasks.list({
|
|
12535
|
+
channel: target,
|
|
12536
|
+
status: "all"
|
|
12537
|
+
});
|
|
12538
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
12539
|
+
const tasks = result.data.tasks ?? [];
|
|
12540
|
+
const task = tasks.find((candidate) => candidate.taskNumber === taskNumber);
|
|
12541
|
+
if (!task) return failureOutcome(opError("NOT_FOUND", {
|
|
12542
|
+
message: result.data.pagination?.mode === "complete" && result.data.pagination.truncated === false ? `task #${taskNumber} not found in ${target} (searched ${tasks.length} task(s), status=all; server asserts this list is complete)` : tasks.length === 0 ? `task #${taskNumber} not found in ${target}: the server returned 0 tasks and did not assert the list is complete. That can mean the channel has no tasks, or that the server failed to read them (it currently reports some read errors as an empty list), and this command cannot tell which — so this is not evidence that task #${taskNumber} does not exist` : `task #${taskNumber} not found in ${target} (searched ${tasks.length} task(s), status=all; this surface does NOT assert completeness — so this is "absent from what was returned", not "does not exist")`,
|
|
12543
|
+
nextAction: `Check the number with \`raft task list --target "${target}" --status all\`.`
|
|
12544
|
+
}));
|
|
12545
|
+
return {
|
|
12546
|
+
ok: true,
|
|
12547
|
+
state: "task",
|
|
12548
|
+
data: {
|
|
12549
|
+
target,
|
|
12550
|
+
task
|
|
12551
|
+
},
|
|
12552
|
+
next: null,
|
|
12553
|
+
text: formatAgentTaskShow(target, task)
|
|
12554
|
+
};
|
|
12555
|
+
}
|
|
12470
12556
|
const convertMessageToTaskRequestSchema = requestSchema()(object({
|
|
12471
12557
|
target: taskChannelSchema,
|
|
12472
12558
|
messageId: string().describe("Full or short id of a top-level message in that channel.")
|
|
@@ -12795,6 +12881,30 @@ function formatPageFooter(page) {
|
|
|
12795
12881
|
if (page.nextCommand && end < page.total) lines.push(`More: ${page.nextCommand}`);
|
|
12796
12882
|
return `${lines.join("\n")}\n`;
|
|
12797
12883
|
}
|
|
12884
|
+
/** Single channel detail block. */
|
|
12885
|
+
function formatAgentChannelInfo(channel, memberCounts) {
|
|
12886
|
+
const lines = ["## Channel", ""];
|
|
12887
|
+
lines.push(`Channel: ${agentChannelRef(channel.name, channel.type)}`);
|
|
12888
|
+
if (channel.id) lines.push(`ID: ${channel.id}`);
|
|
12889
|
+
lines.push(`Visibility: ${channelVisibility(channel)}`);
|
|
12890
|
+
lines.push(`Joined: ${channel.joined ? "yes" : "no"}`);
|
|
12891
|
+
if (channel.channelRole) lines.push(`Channel role: ${channel.channelRole}`);
|
|
12892
|
+
if (channel.channelAdminBasis) lines.push(`Channel admin basis: ${channel.channelAdminBasis}`);
|
|
12893
|
+
const callableCapabilities = Object.entries(channel.channelCapabilities ?? {}).filter(([, allowed]) => allowed).map(([capability]) => capability);
|
|
12894
|
+
if (callableCapabilities.length > 0) lines.push(`Channel capabilities: ${callableCapabilities.join(", ")}`);
|
|
12895
|
+
const muted = channelMuted(channel);
|
|
12896
|
+
if (muted !== void 0) lines.push(`Muted: ${muted ? "yes" : "no"}`);
|
|
12897
|
+
if (typeof channel.archived === "boolean") lines.push(`Archived: ${channel.archived ? "yes" : "no"}`);
|
|
12898
|
+
lines.push(`Description: ${channel.description?.trim() || "(none)"}`);
|
|
12899
|
+
if (memberCounts) {
|
|
12900
|
+
const agents = memberCounts.agents ?? 0;
|
|
12901
|
+
const humans = memberCounts.humans ?? 0;
|
|
12902
|
+
lines.push(`Members: ${agents + humans} (${agents} agents, ${humans} humans)`);
|
|
12903
|
+
}
|
|
12904
|
+
lines.push("");
|
|
12905
|
+
lines.push(`More: raft channel members "${agentChannelRef(channel.name, channel.type)}"`);
|
|
12906
|
+
return `${lines.join("\n")}\n`;
|
|
12907
|
+
}
|
|
12798
12908
|
/** Compact server summary. */
|
|
12799
12909
|
function formatAgentServerSummary(data) {
|
|
12800
12910
|
const channels = data.channels ?? [];
|
|
@@ -12861,6 +12971,23 @@ function formatAgentServerHumans(humans, page) {
|
|
|
12861
12971
|
}
|
|
12862
12972
|
return `${lines.join("\n")}${formatPageFooter(page)}`;
|
|
12863
12973
|
}
|
|
12974
|
+
/** Narrow visible facts for one user/agent. */
|
|
12975
|
+
function formatAgentUserInfo(user, memberships, page, skippedChannels = 0) {
|
|
12976
|
+
const name = user.value.name;
|
|
12977
|
+
const role = roleLabel(user.value.role);
|
|
12978
|
+
const lines = ["## User", ""];
|
|
12979
|
+
lines.push(`User: @${name}`);
|
|
12980
|
+
lines.push(`Kind: ${user.kind}`);
|
|
12981
|
+
if (user.kind === "agent") lines.push(`Status: ${agentStatusLabel(user.value)}`);
|
|
12982
|
+
if (role) lines.push(`Role: ${role.slice(2, -1)}`);
|
|
12983
|
+
if (user.value.description) lines.push(`Description: ${user.value.description}`);
|
|
12984
|
+
lines.push("");
|
|
12985
|
+
lines.push("### Visible Channel Memberships");
|
|
12986
|
+
if (memberships.length === 0) lines.push("(none found in inspected visible channels)");
|
|
12987
|
+
else for (const channel of memberships) lines.push(`${agentChannelRef(channel.name, channel.type)} [${channelStatus(channel)}]`);
|
|
12988
|
+
if (skippedChannels > 0) lines.push(`Skipped ${skippedChannels} visible channel roster checks because the server rejected them.`);
|
|
12989
|
+
return `${lines.join("\n")}${formatPageFooter(page)}`;
|
|
12990
|
+
}
|
|
12864
12991
|
/** Channel membership with server-role labels. */
|
|
12865
12992
|
function formatAgentChannelMembers(data) {
|
|
12866
12993
|
let text = "## Channel Members\n\n";
|
|
@@ -13251,6 +13378,41 @@ async function unfollowThread(client, request) {
|
|
|
13251
13378
|
text: `Unfollowed ${request.target}. Ordinary delivery from this thread stops; a direct @mention reactivates the follow.`
|
|
13252
13379
|
};
|
|
13253
13380
|
}
|
|
13381
|
+
const channelInfoRequestSchema = requestSchema()(object({ target: string().describe("A regular channel, `#channel-name` (the `#` may be omitted). DMs and threads are not accepted.") }));
|
|
13382
|
+
/**
|
|
13383
|
+
* `raft channel info <target>`: the channel's facts from `server.info`, plus
|
|
13384
|
+
* member counts from its roster when the Server shows it.
|
|
13385
|
+
*/
|
|
13386
|
+
async function channelInfo(client, request) {
|
|
13387
|
+
const invalid = validateOpRequest(channelInfoRequestSchema, request);
|
|
13388
|
+
if (invalid) return invalid;
|
|
13389
|
+
const trimmed = request.target.trim();
|
|
13390
|
+
const input = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
13391
|
+
const name = parseRaftRegularChannelTarget$1(input);
|
|
13392
|
+
if (!name) return failureOutcome(opError("INVALID_REQUEST", { message: "Target must be a regular channel name, e.g. '#engineering' or 'engineering'. DMs and thread targets are not supported." }));
|
|
13393
|
+
const info = await client.server.info();
|
|
13394
|
+
if (!info.ok) return failureFromClientResult(info);
|
|
13395
|
+
const channel = info.data.channels.find((candidate) => candidate.name === name);
|
|
13396
|
+
if (!channel) return failureOutcome(opError("NOT_FOUND", {
|
|
13397
|
+
message: `Channel not found or not visible: ${input}`,
|
|
13398
|
+
nextAction: "Run `raft server info --channels --query <name>` to inspect visible channels, or ask a channel member to add you if this is private."
|
|
13399
|
+
}));
|
|
13400
|
+
const members = await client.channels.members({ channel: `#${name}` });
|
|
13401
|
+
const memberCounts = members.ok ? {
|
|
13402
|
+
agents: members.data.agents?.length ?? 0,
|
|
13403
|
+
humans: members.data.humans?.length ?? 0
|
|
13404
|
+
} : null;
|
|
13405
|
+
return {
|
|
13406
|
+
ok: true,
|
|
13407
|
+
state: "info",
|
|
13408
|
+
data: {
|
|
13409
|
+
channel,
|
|
13410
|
+
memberCounts
|
|
13411
|
+
},
|
|
13412
|
+
next: null,
|
|
13413
|
+
text: formatAgentChannelInfo(channel, memberCounts)
|
|
13414
|
+
};
|
|
13415
|
+
}
|
|
13254
13416
|
//#endregion
|
|
13255
13417
|
//#region ../shared/src/agentOps/server.ts
|
|
13256
13418
|
const serverInfoRequestSchema = requestSchema()(object({
|
|
@@ -13374,6 +13536,85 @@ async function updateProfile(client, request, options = {}) {
|
|
|
13374
13536
|
text: formatAgentProfile(result.data, options)
|
|
13375
13537
|
};
|
|
13376
13538
|
}
|
|
13539
|
+
const userInfoRequestSchema = requestSchema()(object({
|
|
13540
|
+
name: string().describe("The human or agent, `@handle` (or the bare handle)."),
|
|
13541
|
+
offset: number$1().int().nonnegative().optional().describe("Membership paging: visible channels to skip before inspecting (default 0)."),
|
|
13542
|
+
limit: number$1().int().positive().optional().describe("Membership paging: visible channels to inspect (default 50).")
|
|
13543
|
+
}));
|
|
13544
|
+
/**
|
|
13545
|
+
* `raft user info <name>`: the user's visible facts from `server.info`, and
|
|
13546
|
+
* their memberships among one page of the visible channels, checked one
|
|
13547
|
+
* channel roster at a time (a rejected roster is skipped and counted).
|
|
13548
|
+
*/
|
|
13549
|
+
async function userInfo(client, request) {
|
|
13550
|
+
const invalid = validateOpRequest(userInfoRequestSchema, request);
|
|
13551
|
+
if (invalid) return invalid;
|
|
13552
|
+
const trimmed = request.name.trim();
|
|
13553
|
+
const name = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
13554
|
+
if (!name) return failureOutcome(opError("INVALID_REQUEST", { message: "user name is required" }));
|
|
13555
|
+
const limit = request.limit ?? 50;
|
|
13556
|
+
const offset = request.offset ?? 0;
|
|
13557
|
+
const info = await client.server.info();
|
|
13558
|
+
if (!info.ok) return failureFromClientResult(info);
|
|
13559
|
+
const agent = info.data.agents.find((candidate) => candidate.name === name);
|
|
13560
|
+
const human = info.data.humans.find((candidate) => candidate.name === name);
|
|
13561
|
+
const user = agent ? {
|
|
13562
|
+
kind: "agent",
|
|
13563
|
+
value: agent
|
|
13564
|
+
} : human ? {
|
|
13565
|
+
kind: "human",
|
|
13566
|
+
value: human
|
|
13567
|
+
} : null;
|
|
13568
|
+
if (!user) return failureOutcome(opError("NOT_FOUND", {
|
|
13569
|
+
message: `User not found or not visible: @${name}`,
|
|
13570
|
+
nextAction: "Run `raft server info --agents --query <name>` or `raft server info --humans --query <name>` to inspect visible users."
|
|
13571
|
+
}));
|
|
13572
|
+
const visibleChannels = info.data.channels;
|
|
13573
|
+
const memberships = [];
|
|
13574
|
+
let skippedChannels = 0;
|
|
13575
|
+
for (const channel of visibleChannels.slice(offset, offset + limit)) {
|
|
13576
|
+
const members = await client.channels.members({ channel: `#${channel.name}` });
|
|
13577
|
+
if (!members.ok) {
|
|
13578
|
+
skippedChannels += 1;
|
|
13579
|
+
continue;
|
|
13580
|
+
}
|
|
13581
|
+
if ((user.kind === "agent" ? members.data.agents ?? [] : members.data.humans ?? []).some((candidate) => candidate.name === name)) memberships.push({
|
|
13582
|
+
...channel,
|
|
13583
|
+
joined: true,
|
|
13584
|
+
muted: void 0,
|
|
13585
|
+
activityMuted: void 0
|
|
13586
|
+
});
|
|
13587
|
+
}
|
|
13588
|
+
const nextOffset = offset + limit;
|
|
13589
|
+
const page = {
|
|
13590
|
+
total: visibleChannels.length,
|
|
13591
|
+
offset,
|
|
13592
|
+
limit,
|
|
13593
|
+
nextCommand: nextOffset < visibleChannels.length ? `raft user info @${name} --offset ${nextOffset} --limit ${limit}` : void 0
|
|
13594
|
+
};
|
|
13595
|
+
const next = page.nextCommand ? {
|
|
13596
|
+
kind: "next_page",
|
|
13597
|
+
command: page.nextCommand,
|
|
13598
|
+
args: {
|
|
13599
|
+
name: `@${name}`,
|
|
13600
|
+
offset: nextOffset,
|
|
13601
|
+
limit
|
|
13602
|
+
},
|
|
13603
|
+
why: "Only one page of visible channels was inspected for memberships."
|
|
13604
|
+
} : null;
|
|
13605
|
+
return {
|
|
13606
|
+
ok: true,
|
|
13607
|
+
state: "info",
|
|
13608
|
+
data: {
|
|
13609
|
+
user,
|
|
13610
|
+
memberships,
|
|
13611
|
+
skippedChannels,
|
|
13612
|
+
page
|
|
13613
|
+
},
|
|
13614
|
+
next,
|
|
13615
|
+
text: formatAgentUserInfo(user, memberships, page, skippedChannels)
|
|
13616
|
+
};
|
|
13617
|
+
}
|
|
13377
13618
|
//#endregion
|
|
13378
13619
|
//#region ../shared/src/agentText/attachments.ts
|
|
13379
13620
|
/** Upload receipt with attachment id and send-usage hint. */
|
|
@@ -14480,11 +14721,16 @@ async function prepareActionCard(client, request) {
|
|
|
14480
14721
|
if (typeof request?.target !== "string" || !request.target.trim() || !request.action) return failureOutcome(opError("INVALID_REQUEST", { message: "A target and an action are required to prepare a card." }));
|
|
14481
14722
|
const invalid = validateOpRequest(prepareActionCardRequestSchema, request);
|
|
14482
14723
|
if (invalid) return invalid;
|
|
14483
|
-
const
|
|
14484
|
-
|
|
14724
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
14725
|
+
const result = await client.actions.prepare({
|
|
14726
|
+
...request,
|
|
14727
|
+
idempotencyKey
|
|
14728
|
+
});
|
|
14729
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
14485
14730
|
const card = {
|
|
14486
14731
|
target: request.target,
|
|
14487
|
-
messageId: result.data.messageId
|
|
14732
|
+
messageId: result.data.messageId,
|
|
14733
|
+
idempotencyKey
|
|
14488
14734
|
};
|
|
14489
14735
|
return {
|
|
14490
14736
|
ok: true,
|
|
@@ -14945,9 +15191,11 @@ const OPERATION_DEFS = [
|
|
|
14945
15191
|
schema: prepareActionCardRequestSchema,
|
|
14946
15192
|
fieldDescriptions: {
|
|
14947
15193
|
target: "Conversation to post the card in: `#channel`, `dm:@peer`, or a thread.",
|
|
14948
|
-
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described."
|
|
15194
|
+
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described.",
|
|
15195
|
+
idempotencyKey: "One key per logical prepare; generated when omitted and returned. Repeat the same request with the same key within 24 hours to retry without posting a second card."
|
|
14949
15196
|
},
|
|
14950
15197
|
routes: ["actionPrepare"],
|
|
15198
|
+
idempotencyArg: "idempotencyKey",
|
|
14951
15199
|
consumes: nothing,
|
|
14952
15200
|
output: small
|
|
14953
15201
|
},
|
|
@@ -14998,6 +15246,7 @@ const OPERATION_DEFS = [
|
|
|
14998
15246
|
description: "Create one or more tasks on a channel's board; each gets its own thread.",
|
|
14999
15247
|
schema: createTasksRequestSchema,
|
|
15000
15248
|
routes: ["taskCreate"],
|
|
15249
|
+
idempotencyArg: "idempotencyKey",
|
|
15001
15250
|
consumes: nothing,
|
|
15002
15251
|
output: small
|
|
15003
15252
|
},
|
|
@@ -15054,6 +15303,17 @@ const OPERATION_DEFS = [
|
|
|
15054
15303
|
boundBy: []
|
|
15055
15304
|
}
|
|
15056
15305
|
},
|
|
15306
|
+
{
|
|
15307
|
+
name: "tasks.show",
|
|
15308
|
+
description: "One task's current status, title, and description. Finds done and closed tasks too.",
|
|
15309
|
+
schema: taskRefSchema,
|
|
15310
|
+
routes: ["taskList"],
|
|
15311
|
+
consumes: nothing,
|
|
15312
|
+
output: {
|
|
15313
|
+
mayBeLarge: true,
|
|
15314
|
+
boundBy: []
|
|
15315
|
+
}
|
|
15316
|
+
},
|
|
15057
15317
|
{
|
|
15058
15318
|
name: "tasks.convert",
|
|
15059
15319
|
description: "Turn a top-level message into an unassigned task.",
|
|
@@ -15113,6 +15373,14 @@ const OPERATION_DEFS = [
|
|
|
15113
15373
|
boundBy: []
|
|
15114
15374
|
}
|
|
15115
15375
|
},
|
|
15376
|
+
{
|
|
15377
|
+
name: "channels.info",
|
|
15378
|
+
description: "A regular channel's facts: visibility, whether you joined it, your role and mute state, description, and member counts.",
|
|
15379
|
+
schema: channelInfoRequestSchema,
|
|
15380
|
+
routes: ["serverInfo", "channelMembers"],
|
|
15381
|
+
consumes: nothing,
|
|
15382
|
+
output: small
|
|
15383
|
+
},
|
|
15116
15384
|
{
|
|
15117
15385
|
name: "threads.list",
|
|
15118
15386
|
description: "The threads you follow.",
|
|
@@ -15147,6 +15415,17 @@ const OPERATION_DEFS = [
|
|
|
15147
15415
|
]
|
|
15148
15416
|
}
|
|
15149
15417
|
},
|
|
15418
|
+
{
|
|
15419
|
+
name: "users.info",
|
|
15420
|
+
description: "A human's or agent's visible facts (kind, status, role, description) and which visible channels they are in. Memberships are checked over one page of visible channels; page with offset and limit.",
|
|
15421
|
+
schema: userInfoRequestSchema,
|
|
15422
|
+
routes: ["serverInfo", "channelMembers"],
|
|
15423
|
+
consumes: nothing,
|
|
15424
|
+
output: {
|
|
15425
|
+
mayBeLarge: true,
|
|
15426
|
+
boundBy: ["offset", "limit"]
|
|
15427
|
+
}
|
|
15428
|
+
},
|
|
15150
15429
|
{
|
|
15151
15430
|
name: "profile.show",
|
|
15152
15431
|
description: "Show your profile, or someone else's by @handle.",
|
|
@@ -15392,6 +15671,7 @@ function createRaft(options) {
|
|
|
15392
15671
|
updateStatus: (request) => updateTaskStatus(api, request),
|
|
15393
15672
|
amend: (request) => amendTask(api, request),
|
|
15394
15673
|
history: (request) => taskHistory(api, request),
|
|
15674
|
+
show: (request) => showTask(api, request),
|
|
15395
15675
|
convert: (request) => convertMessageToTask(api, request),
|
|
15396
15676
|
delete: (request) => deleteTask(api, request)
|
|
15397
15677
|
},
|
|
@@ -15400,13 +15680,15 @@ function createRaft(options) {
|
|
|
15400
15680
|
leave: (request) => leaveChannel(api, request),
|
|
15401
15681
|
mute: (request) => muteChannel(api, request),
|
|
15402
15682
|
unmute: (request) => unmuteChannel(api, request),
|
|
15403
|
-
members: (request) => channelMembers(api, request)
|
|
15683
|
+
members: (request) => channelMembers(api, request),
|
|
15684
|
+
info: (request) => channelInfo(api, request)
|
|
15404
15685
|
},
|
|
15405
15686
|
threads: {
|
|
15406
15687
|
list: () => listThreads(api),
|
|
15407
15688
|
unfollow: (request) => unfollowThread(api, request)
|
|
15408
15689
|
},
|
|
15409
15690
|
server: { info: (request) => serverInfo(api, request) },
|
|
15691
|
+
users: { info: (request) => userInfo(api, request) },
|
|
15410
15692
|
profile: {
|
|
15411
15693
|
show: (request) => showProfile(api, request),
|
|
15412
15694
|
update: (request) => updateProfile(api, request)
|
|
@@ -15474,6 +15756,7 @@ function createRaft(options) {
|
|
|
15474
15756
|
"tasks.updateStatus": (args) => raft.tasks.updateStatus(args),
|
|
15475
15757
|
"tasks.amend": (args) => raft.tasks.amend(args),
|
|
15476
15758
|
"tasks.history": (args) => raft.tasks.history(args),
|
|
15759
|
+
"tasks.show": (args) => raft.tasks.show(args),
|
|
15477
15760
|
"tasks.convert": (args) => raft.tasks.convert(args),
|
|
15478
15761
|
"tasks.delete": (args) => raft.tasks.delete(args),
|
|
15479
15762
|
"channels.join": (args) => raft.channels.join(args),
|
|
@@ -15481,9 +15764,11 @@ function createRaft(options) {
|
|
|
15481
15764
|
"channels.mute": (args) => raft.channels.mute(args),
|
|
15482
15765
|
"channels.unmute": (args) => raft.channels.unmute(args),
|
|
15483
15766
|
"channels.members": (args) => raft.channels.members(args),
|
|
15767
|
+
"channels.info": (args) => raft.channels.info(args),
|
|
15484
15768
|
"threads.list": () => raft.threads.list(),
|
|
15485
15769
|
"threads.unfollow": (args) => raft.threads.unfollow(args),
|
|
15486
15770
|
"server.info": (args) => raft.server.info(args),
|
|
15771
|
+
"users.info": (args) => raft.users.info(args),
|
|
15487
15772
|
"profile.show": (args) => raft.profile.show(args),
|
|
15488
15773
|
"profile.update": (args) => raft.profile.update(args)
|
|
15489
15774
|
};
|
|
@@ -15527,4 +15812,4 @@ function whoamiOutcome(result) {
|
|
|
15527
15812
|
};
|
|
15528
15813
|
}
|
|
15529
15814
|
//#endregion
|
|
15530
|
-
export { RAFT_INBOX_NOTICE_SCHEMA, RAFT_JSON_SCHEMA_KEYWORDS, RAFT_NOTICE_DELIVERY_ID_HEADER, RAFT_NOTICE_SIGNATURE_HEADER, RAFT_OPERATIONS, RAFT_OPERATIONS_SCHEMA, RAFT_OPERATIONS_VERSION, RAFT_STATE_CONTINUATIONS_PER_TARGET, RAFT_STATE_CONTINUATIONS_TOTAL, RAFT_STATE_SCHEMA, RaftCredentialError, RaftSdkConfigurationError, SeenFrontier, amendTaskRequestSchema, assignTaskRequestSchema, attachmentCommentsRequestSchema, bootstrapRaftCredential, channelMembersRequestSchema, channelTargetRequestSchema, checkInboxRequestSchema, claimTasksRequestSchema, commitInboxRequestSchema, convertMessageToTaskRequestSchema, createFileCredentialStore, createRaft, createRaftClient, createRaftClientFromStore, createRaftRoutes, createTasksRequestSchema, describeRaftRoute, drainInboxRequestSchema, executeMentionActionRequestSchema, getManualTopicRequestSchema, hasAgentMessageIdentity, hashRaftSendContent, inferAttachmentMimeType, isInterrupted, joinRaftChannelByTarget, listInboxRequestSchema, listRaftRoutes, listTasksRequestSchema, listThreadsRequestSchema, parseRaftRegularChannelTarget, parseRaftState, pendingMentionActionsRequestSchema, prepareActionCardRequestSchema, projectRaftMessage, projectRaftMessages, raftToolNameFor, reactRequestSchema, readHistoryRequestSchema, readLatestReadThread, replyToRequestSchema, resolveMessageRequestSchema, searchManualRequestSchema, searchMessagesRequestSchema, sendMessageRequestSchema, senderMentionDeliveriesRequestSchema, serverInfoRequestSchema, showProfileRequestSchema, taskRefSchema, unfollowThreadRequestSchema, updateProfileRequestSchema, updateTaskStatusRequestSchema, verifyInboxNotice, whoamiRequestSchema };
|
|
15815
|
+
export { RAFT_INBOX_NOTICE_SCHEMA, RAFT_JSON_SCHEMA_KEYWORDS, RAFT_NOTICE_DELIVERY_ID_HEADER, RAFT_NOTICE_SIGNATURE_HEADER, RAFT_OPERATIONS, RAFT_OPERATIONS_SCHEMA, RAFT_OPERATIONS_VERSION, RAFT_STATE_CONTINUATIONS_PER_TARGET, RAFT_STATE_CONTINUATIONS_TOTAL, RAFT_STATE_SCHEMA, RaftCredentialError, RaftSdkConfigurationError, SeenFrontier, amendTaskRequestSchema, assignTaskRequestSchema, attachmentCommentsRequestSchema, bootstrapRaftCredential, channelInfoRequestSchema, channelMembersRequestSchema, channelTargetRequestSchema, checkInboxRequestSchema, claimTasksRequestSchema, commitInboxRequestSchema, convertMessageToTaskRequestSchema, createFileCredentialStore, createRaft, createRaftClient, createRaftClientFromStore, createRaftRoutes, createTasksRequestSchema, describeRaftRoute, drainInboxRequestSchema, executeMentionActionRequestSchema, getManualTopicRequestSchema, hasAgentMessageIdentity, hashRaftSendContent, inferAttachmentMimeType, isInterrupted, joinRaftChannelByTarget, listInboxRequestSchema, listRaftRoutes, listTasksRequestSchema, listThreadsRequestSchema, parseRaftRegularChannelTarget, parseRaftState, pendingMentionActionsRequestSchema, prepareActionCardRequestSchema, projectRaftMessage, projectRaftMessages, raftToolNameFor, reactRequestSchema, readHistoryRequestSchema, readLatestReadThread, replyToRequestSchema, resolveMessageRequestSchema, searchManualRequestSchema, searchMessagesRequestSchema, sendMessageRequestSchema, senderMentionDeliveriesRequestSchema, serverInfoRequestSchema, showProfileRequestSchema, taskRefSchema, unfollowThreadRequestSchema, updateProfileRequestSchema, updateTaskStatusRequestSchema, userInfoRequestSchema, verifyInboxNotice, whoamiRequestSchema };
|