@botiverse/raft-sdk 0.8.0 → 0.10.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 +71 -6
- package/dist/cjs/index.cjs +862 -433
- package/dist/esm/index.js +862 -434
- package/dist/index.d.ts +150 -7
- package/operations.json +51 -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,
|
|
@@ -4752,6 +4752,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4752
4752
|
attachmentUploadSessionCancel: naturalDestructive,
|
|
4753
4753
|
attachmentUploadSessionStatus: read,
|
|
4754
4754
|
attachmentDownload: read,
|
|
4755
|
+
attachmentDownloadUrl: read,
|
|
4755
4756
|
attachmentCommentsList: read,
|
|
4756
4757
|
pushWebhookStatus: {
|
|
4757
4758
|
sideEffect: "read",
|
|
@@ -5280,6 +5281,18 @@ const agentApiMessageReactionBodySchema = passthroughObject({ emoji: string().tr
|
|
|
5280
5281
|
const agentApiChannelMembershipParamsSchema = passthroughObject({ channelId: string().trim().min(1).transform(asChannelId) });
|
|
5281
5282
|
const agentApiChannelLifecycleBodySchema = passthroughObject({ target: string().trim().min(1) });
|
|
5282
5283
|
const agentApiAttachmentDownloadParamsSchema = passthroughObject({ attachmentId: string().trim().min(1) });
|
|
5284
|
+
/**
|
|
5285
|
+
* Short-lived download URL for an attachment visible to the bound agent
|
|
5286
|
+
* credential (`GET /attachments/:attachmentId/url`). For runtimes that cannot
|
|
5287
|
+
* take binary tool results: the agent fetches the bytes itself from `url`
|
|
5288
|
+
* before `expiresAt`. The URL is a bearer capability; never log it.
|
|
5289
|
+
*/
|
|
5290
|
+
const agentApiAttachmentDownloadUrlResponseSchema = passthroughObject({
|
|
5291
|
+
url: string().min(1),
|
|
5292
|
+
expiresAt: string().datetime(),
|
|
5293
|
+
filename: string(),
|
|
5294
|
+
mimeType: string()
|
|
5295
|
+
});
|
|
5283
5296
|
const agentApiAttachmentCommentsParamsSchema = passthroughObject({ attachmentId: string().trim().min(1) });
|
|
5284
5297
|
const agentApiAttachmentCommentsQuerySchema = passthroughObject({ limit: optionalStringSchema });
|
|
5285
5298
|
const agentApiAttachmentCommentAnchorSchema = passthroughObject({
|
|
@@ -5395,7 +5408,15 @@ const agentApiTaskCreateBodySchema = passthroughObject({
|
|
|
5395
5408
|
title: string().trim().min(1),
|
|
5396
5409
|
creates_resource: boolean().optional()
|
|
5397
5410
|
})).min(1),
|
|
5398
|
-
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional()
|
|
5411
|
+
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional(),
|
|
5412
|
+
/**
|
|
5413
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5414
|
+
* the same request replays the first response without creating anything;
|
|
5415
|
+
* the same key with a different request is refused (409
|
|
5416
|
+
* `idempotency_key_reused`). Scoped to the agent and this route, and valid
|
|
5417
|
+
* for 24 hours; after that the key is forgotten and is a new request.
|
|
5418
|
+
*/
|
|
5419
|
+
idempotencyKey: optionalStringSchema
|
|
5399
5420
|
});
|
|
5400
5421
|
const agentApiTaskUnclaimBodySchema = passthroughObject({
|
|
5401
5422
|
channel: string().trim().min(1),
|
|
@@ -5646,7 +5667,15 @@ const agentApiIntegrationAppStatusQuerySchema = passthroughObject({
|
|
|
5646
5667
|
});
|
|
5647
5668
|
const agentApiActionPrepareBodySchema = passthroughObject({
|
|
5648
5669
|
target: string().trim().min(1),
|
|
5649
|
-
action: actionCardActionSchema
|
|
5670
|
+
action: actionCardActionSchema,
|
|
5671
|
+
/**
|
|
5672
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5673
|
+
* the same request replays the first response (same card messageId)
|
|
5674
|
+
* without preparing another card; the same key with a different request is
|
|
5675
|
+
* refused (409 `idempotency_key_reused`). Scoped to the agent and this
|
|
5676
|
+
* route, and valid for 24 hours; after that the key is forgotten.
|
|
5677
|
+
*/
|
|
5678
|
+
idempotencyKey: optionalStringSchema
|
|
5650
5679
|
});
|
|
5651
5680
|
const agentApiServerUpdateBodySchema = passthroughObject({
|
|
5652
5681
|
name: string().trim().min(1).max(100).optional(),
|
|
@@ -7320,6 +7349,19 @@ const agentApiContract = {
|
|
|
7320
7349
|
request: { params: agentApiAttachmentDownloadParamsSchema },
|
|
7321
7350
|
response: { kind: "binary" }
|
|
7322
7351
|
}),
|
|
7352
|
+
attachmentDownloadUrl: route({
|
|
7353
|
+
key: "attachmentDownloadUrl",
|
|
7354
|
+
method: "GET",
|
|
7355
|
+
path: "/attachments/:attachmentId/url",
|
|
7356
|
+
client: {
|
|
7357
|
+
resource: "attachments",
|
|
7358
|
+
method: "downloadUrl"
|
|
7359
|
+
},
|
|
7360
|
+
capability: "read",
|
|
7361
|
+
description: "Mint a short-lived (5 minute) download URL for an attachment visible to the bound agent credential, with its filename and MIME type. 409 download_url_unavailable when this Server's storage cannot presign; use the binary download instead.",
|
|
7362
|
+
request: { params: agentApiAttachmentDownloadParamsSchema },
|
|
7363
|
+
response: { body: agentApiAttachmentDownloadUrlResponseSchema }
|
|
7364
|
+
}),
|
|
7323
7365
|
attachmentCommentsList: route({
|
|
7324
7366
|
key: "attachmentCommentsList",
|
|
7325
7367
|
method: "GET",
|
|
@@ -8727,7 +8769,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
8727
8769
|
"capability": "tasks",
|
|
8728
8770
|
"description": "Create one or more tasks in a channel.",
|
|
8729
8771
|
"sideEffect": "write",
|
|
8730
|
-
"idempotency": "
|
|
8772
|
+
"idempotency": "key",
|
|
8731
8773
|
"destructive": false,
|
|
8732
8774
|
"audience": "both",
|
|
8733
8775
|
"request": {
|
|
@@ -9527,7 +9569,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9527
9569
|
"capability": "tasks",
|
|
9528
9570
|
"description": "Prepare an action card for a human to commit.",
|
|
9529
9571
|
"sideEffect": "write",
|
|
9530
|
-
"idempotency": "
|
|
9572
|
+
"idempotency": "key",
|
|
9531
9573
|
"destructive": false,
|
|
9532
9574
|
"audience": "both",
|
|
9533
9575
|
"request": {
|
|
@@ -9715,6 +9757,31 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9715
9757
|
"body": false
|
|
9716
9758
|
}
|
|
9717
9759
|
},
|
|
9760
|
+
{
|
|
9761
|
+
"key": "attachmentDownloadUrl",
|
|
9762
|
+
"method": "GET",
|
|
9763
|
+
"path": "/attachments/:attachmentId/url",
|
|
9764
|
+
"fullPath": "/internal/agent-api/attachments/:attachmentId/url",
|
|
9765
|
+
"client": {
|
|
9766
|
+
"resource": "attachments",
|
|
9767
|
+
"method": "downloadUrl"
|
|
9768
|
+
},
|
|
9769
|
+
"capability": "read",
|
|
9770
|
+
"description": "Mint a short-lived (5 minute) download URL for an attachment visible to the bound agent credential, with its filename and MIME type. 409 download_url_unavailable when this Server's storage cannot presign; use the binary download instead.",
|
|
9771
|
+
"sideEffect": "read",
|
|
9772
|
+
"idempotency": "natural",
|
|
9773
|
+
"destructive": false,
|
|
9774
|
+
"audience": "both",
|
|
9775
|
+
"request": {
|
|
9776
|
+
"params": true,
|
|
9777
|
+
"query": false,
|
|
9778
|
+
"body": false
|
|
9779
|
+
},
|
|
9780
|
+
"response": {
|
|
9781
|
+
"kind": "json",
|
|
9782
|
+
"body": true
|
|
9783
|
+
}
|
|
9784
|
+
},
|
|
9718
9785
|
{
|
|
9719
9786
|
"key": "attachmentCommentsList",
|
|
9720
9787
|
"method": "GET",
|
|
@@ -9842,7 +9909,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9842
9909
|
}
|
|
9843
9910
|
];
|
|
9844
9911
|
/** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
|
|
9845
|
-
const AGENT_API_MANIFEST_VERSION = "
|
|
9912
|
+
const AGENT_API_MANIFEST_VERSION = "c762a744e3542f89";
|
|
9846
9913
|
//#endregion
|
|
9847
9914
|
//#region src/routes.ts
|
|
9848
9915
|
const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
|
|
@@ -10437,6 +10504,280 @@ function fileReadError() {
|
|
|
10437
10504
|
return new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The Raft credential file is invalid or unreadable");
|
|
10438
10505
|
}
|
|
10439
10506
|
//#endregion
|
|
10507
|
+
//#region ../shared/src/agentOps/hint.ts
|
|
10508
|
+
/** `tasks.updateStatus` → `tasks_update_status`: the manifest's tool name for an operation name. */
|
|
10509
|
+
function raftToolNameFor(name) {
|
|
10510
|
+
return name.replace(/\./g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
10511
|
+
}
|
|
10512
|
+
/** Tool-form wording for a CLI command that has no operation (channel / server admin writes). */
|
|
10513
|
+
const RAFT_NO_TOOL_WORDING = "ask a human via an action card (`actions_prepare`)";
|
|
10514
|
+
const ELLIPSIS = "…";
|
|
10515
|
+
function q(value) {
|
|
10516
|
+
return `"${value}"`;
|
|
10517
|
+
}
|
|
10518
|
+
function hint(cli, op, fill) {
|
|
10519
|
+
if (!op) return { cli };
|
|
10520
|
+
const args = Object.fromEntries(Object.entries(op.args).filter(([, value]) => value !== void 0));
|
|
10521
|
+
return fill && fill.length > 0 ? {
|
|
10522
|
+
cli,
|
|
10523
|
+
op: {
|
|
10524
|
+
name: op.name,
|
|
10525
|
+
args,
|
|
10526
|
+
partial: true
|
|
10527
|
+
},
|
|
10528
|
+
fill
|
|
10529
|
+
} : {
|
|
10530
|
+
cli,
|
|
10531
|
+
op: {
|
|
10532
|
+
name: op.name,
|
|
10533
|
+
args
|
|
10534
|
+
}
|
|
10535
|
+
};
|
|
10536
|
+
}
|
|
10537
|
+
function toolArgs(hint) {
|
|
10538
|
+
const parts = Object.entries(hint.op?.args ?? {}).map(([key, value]) => `${key}: ${JSON.stringify(value)}`);
|
|
10539
|
+
for (const key of hint.fill ?? []) parts.push(`${key}: ${ELLIPSIS}`);
|
|
10540
|
+
return parts.length > 0 ? `{ ${parts.join(", ")} }` : "{}";
|
|
10541
|
+
}
|
|
10542
|
+
/** Render a hint: `raft message read --target "#ops"` (cli) or `messages_read({ target: "#ops" })` (tool). */
|
|
10543
|
+
function formatHint(hint, style = "cli") {
|
|
10544
|
+
if (style === "cli") return ["raft", ...hint.cli].join(" ");
|
|
10545
|
+
if (!hint.op) return RAFT_NO_TOOL_WORDING;
|
|
10546
|
+
if (hint.codeOnly) return `raft.${hint.op.name}(${toolArgs(hint)})`;
|
|
10547
|
+
return `${raftToolNameFor(hint.op.name)}(${toolArgs(hint)})`;
|
|
10548
|
+
}
|
|
10549
|
+
/** Just the command's name, for prose that lists commands: `raft channel join` (cli) or `channels_join` (tool). */
|
|
10550
|
+
function formatHintName(hint, style = "cli") {
|
|
10551
|
+
if (style === "cli") return ["raft", ...hint.cli].join(" ");
|
|
10552
|
+
if (!hint.op) return RAFT_NO_TOOL_WORDING;
|
|
10553
|
+
return hint.codeOnly ? `raft.${hint.op.name}` : raftToolNameFor(hint.op.name);
|
|
10554
|
+
}
|
|
10555
|
+
/** A flag named in prose: `--limit` (cli) or `limit` (tool, the argument name). */
|
|
10556
|
+
function formatHintFlag(flag, arg, style = "cli") {
|
|
10557
|
+
return style === "cli" ? `--${flag}` : arg;
|
|
10558
|
+
}
|
|
10559
|
+
/**
|
|
10560
|
+
* The hint as a next step: `command` is the rendered hint, `operation` the
|
|
10561
|
+
* structured call (when there is one). `args` keeps the step's own arguments.
|
|
10562
|
+
*/
|
|
10563
|
+
function hintStep(kind, hint, why, style = "cli", args) {
|
|
10564
|
+
return {
|
|
10565
|
+
kind,
|
|
10566
|
+
command: formatHint(hint, style),
|
|
10567
|
+
...args ? { args } : {},
|
|
10568
|
+
...hint.op ? { operation: hint.op } : {},
|
|
10569
|
+
why
|
|
10570
|
+
};
|
|
10571
|
+
}
|
|
10572
|
+
/**
|
|
10573
|
+
* Every hint the shared formatters and operations produce. Placeholder
|
|
10574
|
+
* arguments (`<name>`, `"…"`) are `fill` keys: the CLI form prints the
|
|
10575
|
+
* placeholder, the tool form `name: …`.
|
|
10576
|
+
*/
|
|
10577
|
+
const RAFT_HINTS = {
|
|
10578
|
+
messageRead: ({ target, after, before, around }) => hint([
|
|
10579
|
+
"message",
|
|
10580
|
+
"read",
|
|
10581
|
+
"--target",
|
|
10582
|
+
q(target),
|
|
10583
|
+
...after === void 0 ? [] : ["--after", String(after)],
|
|
10584
|
+
...before === void 0 ? [] : ["--before", String(before)],
|
|
10585
|
+
...around === void 0 ? [] : ["--around", around.shown]
|
|
10586
|
+
], {
|
|
10587
|
+
name: "messages.read",
|
|
10588
|
+
args: {
|
|
10589
|
+
target,
|
|
10590
|
+
after,
|
|
10591
|
+
before,
|
|
10592
|
+
around: around?.id
|
|
10593
|
+
}
|
|
10594
|
+
}),
|
|
10595
|
+
/** Send into a conversation; `content` is always the caller's. Without a target the target is left too. */
|
|
10596
|
+
messageSend: ({ target, attachmentId }) => hint([
|
|
10597
|
+
"message",
|
|
10598
|
+
"send",
|
|
10599
|
+
...target === void 0 ? [] : ["--target", q(target)],
|
|
10600
|
+
...attachmentId === void 0 ? [] : ["--attachment-id", attachmentId]
|
|
10601
|
+
], {
|
|
10602
|
+
name: "messages.send",
|
|
10603
|
+
args: {
|
|
10604
|
+
target,
|
|
10605
|
+
attachmentIds: attachmentId === void 0 ? void 0 : [attachmentId]
|
|
10606
|
+
}
|
|
10607
|
+
}, target === void 0 ? ["target", "content"] : ["content"]),
|
|
10608
|
+
/** The send command's name alone (prose, never a call). */
|
|
10609
|
+
messageSendName: () => hint(["message", "send"], {
|
|
10610
|
+
name: "messages.send",
|
|
10611
|
+
args: {}
|
|
10612
|
+
}, ["target", "content"]),
|
|
10613
|
+
messageCheck: () => hint(["message", "check"], {
|
|
10614
|
+
name: "inbox.check",
|
|
10615
|
+
args: {}
|
|
10616
|
+
}),
|
|
10617
|
+
inboxList: ({ view, before } = {}) => hint([
|
|
10618
|
+
"inbox",
|
|
10619
|
+
"check",
|
|
10620
|
+
...view === void 0 ? [] : ["--view", view],
|
|
10621
|
+
...before === void 0 ? [] : ["--before", String(before)]
|
|
10622
|
+
], {
|
|
10623
|
+
name: "inbox.list",
|
|
10624
|
+
args: {
|
|
10625
|
+
view,
|
|
10626
|
+
before
|
|
10627
|
+
}
|
|
10628
|
+
}),
|
|
10629
|
+
/** `query: true` is the `<name>` placeholder. */
|
|
10630
|
+
serverInfo: ({ view, offset, limit, query, joined } = {}) => hint([
|
|
10631
|
+
"server",
|
|
10632
|
+
"info",
|
|
10633
|
+
...view === void 0 ? [] : [`--${view}`],
|
|
10634
|
+
...offset === void 0 ? [] : ["--offset", String(offset)],
|
|
10635
|
+
...limit === void 0 ? [] : ["--limit", String(limit)],
|
|
10636
|
+
...query === void 0 ? [] : ["--query", query === true ? "<name>" : JSON.stringify(query)],
|
|
10637
|
+
...joined ? ["--joined"] : []
|
|
10638
|
+
], {
|
|
10639
|
+
name: "server.info",
|
|
10640
|
+
args: {
|
|
10641
|
+
view,
|
|
10642
|
+
offset,
|
|
10643
|
+
limit,
|
|
10644
|
+
query: typeof query === "string" ? query : void 0,
|
|
10645
|
+
joined: joined ? true : void 0
|
|
10646
|
+
}
|
|
10647
|
+
}, query === true ? ["query"] : void 0),
|
|
10648
|
+
/** `name` undefined is the `<name>` placeholder. */
|
|
10649
|
+
userInfo: ({ name, offset, limit } = {}) => hint([
|
|
10650
|
+
"user",
|
|
10651
|
+
"info",
|
|
10652
|
+
name === void 0 ? "<name>" : `@${name}`,
|
|
10653
|
+
...offset === void 0 ? [] : ["--offset", String(offset)],
|
|
10654
|
+
...limit === void 0 ? [] : ["--limit", String(limit)]
|
|
10655
|
+
], {
|
|
10656
|
+
name: "users.info",
|
|
10657
|
+
args: {
|
|
10658
|
+
name: name === void 0 ? void 0 : `@${name}`,
|
|
10659
|
+
offset,
|
|
10660
|
+
limit
|
|
10661
|
+
}
|
|
10662
|
+
}, name === void 0 ? ["name"] : void 0),
|
|
10663
|
+
/** The `<name>` placeholder form. */
|
|
10664
|
+
channelInfo: () => hint([
|
|
10665
|
+
"channel",
|
|
10666
|
+
"info",
|
|
10667
|
+
"<name>"
|
|
10668
|
+
], {
|
|
10669
|
+
name: "channels.info",
|
|
10670
|
+
args: {}
|
|
10671
|
+
}, ["target"]),
|
|
10672
|
+
channelMembers: (target) => hint([
|
|
10673
|
+
"channel",
|
|
10674
|
+
"members",
|
|
10675
|
+
q(target)
|
|
10676
|
+
], {
|
|
10677
|
+
name: "channels.members",
|
|
10678
|
+
args: { target }
|
|
10679
|
+
}),
|
|
10680
|
+
/** Channel attention commands by name (prose). */
|
|
10681
|
+
channelJoinName: () => hint(["channel", "join"], {
|
|
10682
|
+
name: "channels.join",
|
|
10683
|
+
args: {}
|
|
10684
|
+
}, ["target"]),
|
|
10685
|
+
channelLeaveName: () => hint(["channel", "leave"], {
|
|
10686
|
+
name: "channels.leave",
|
|
10687
|
+
args: {}
|
|
10688
|
+
}, ["target"]),
|
|
10689
|
+
channelMuteName: () => hint(["channel", "mute"], {
|
|
10690
|
+
name: "channels.mute",
|
|
10691
|
+
args: {}
|
|
10692
|
+
}, ["target"]),
|
|
10693
|
+
channelUnmuteName: () => hint(["channel", "unmute"], {
|
|
10694
|
+
name: "channels.unmute",
|
|
10695
|
+
args: {}
|
|
10696
|
+
}, ["target"]),
|
|
10697
|
+
threadUnfollowName: () => hint(["thread", "unfollow"], {
|
|
10698
|
+
name: "threads.unfollow",
|
|
10699
|
+
args: {}
|
|
10700
|
+
}, ["target"]),
|
|
10701
|
+
/** Channel / server admin writes: no operation by policy (a human acts through an action card). */
|
|
10702
|
+
channelCreateName: () => hint(["channel", "create"]),
|
|
10703
|
+
serverUpdateName: () => hint(["server", "update"]),
|
|
10704
|
+
taskClaim: ({ target, taskNumber }) => hint([
|
|
10705
|
+
"task",
|
|
10706
|
+
"claim",
|
|
10707
|
+
"--target",
|
|
10708
|
+
q(target),
|
|
10709
|
+
"--number",
|
|
10710
|
+
String(taskNumber)
|
|
10711
|
+
], {
|
|
10712
|
+
name: "tasks.claim",
|
|
10713
|
+
args: {
|
|
10714
|
+
target,
|
|
10715
|
+
taskNumbers: [taskNumber]
|
|
10716
|
+
}
|
|
10717
|
+
}),
|
|
10718
|
+
taskListAll: (target) => hint([
|
|
10719
|
+
"task",
|
|
10720
|
+
"list",
|
|
10721
|
+
"--target",
|
|
10722
|
+
q(target),
|
|
10723
|
+
"--status",
|
|
10724
|
+
"all"
|
|
10725
|
+
], {
|
|
10726
|
+
name: "tasks.list",
|
|
10727
|
+
args: {
|
|
10728
|
+
target,
|
|
10729
|
+
status: "all"
|
|
10730
|
+
}
|
|
10731
|
+
}),
|
|
10732
|
+
mentionAction: (action, resolutionId) => hint([
|
|
10733
|
+
"mention",
|
|
10734
|
+
action,
|
|
10735
|
+
resolutionId
|
|
10736
|
+
], {
|
|
10737
|
+
name: "mentions.execute",
|
|
10738
|
+
args: {
|
|
10739
|
+
action,
|
|
10740
|
+
resolutionIds: [resolutionId]
|
|
10741
|
+
}
|
|
10742
|
+
}),
|
|
10743
|
+
mentionPending: () => hint(["mention", "pending"], {
|
|
10744
|
+
name: "mentions.pending",
|
|
10745
|
+
args: {}
|
|
10746
|
+
}),
|
|
10747
|
+
/** `intent` and `reason` are always the caller's. */
|
|
10748
|
+
manualGet: (topic) => hint([
|
|
10749
|
+
"manual",
|
|
10750
|
+
"get",
|
|
10751
|
+
topic,
|
|
10752
|
+
"--intent",
|
|
10753
|
+
q(ELLIPSIS),
|
|
10754
|
+
"--reason",
|
|
10755
|
+
q(ELLIPSIS)
|
|
10756
|
+
], {
|
|
10757
|
+
name: "manual.get",
|
|
10758
|
+
args: { topic }
|
|
10759
|
+
}, ["intent", "reason"]),
|
|
10760
|
+
/** The download pointer on message lines; the tool form mints a URL (one attachment: its id; several: left to fill). */
|
|
10761
|
+
attachmentView: (attachmentId) => hint(["attachment", "view"], {
|
|
10762
|
+
name: "attachments.downloadUrl",
|
|
10763
|
+
args: { attachmentId }
|
|
10764
|
+
}, attachmentId === void 0 ? ["attachmentId"] : void 0),
|
|
10765
|
+
/** The binary download (typed method only): where an attachment whose URL cannot be minted is fetched. */
|
|
10766
|
+
attachmentDownload: (attachmentId) => ({
|
|
10767
|
+
...hint([
|
|
10768
|
+
"attachment",
|
|
10769
|
+
"view",
|
|
10770
|
+
attachmentId,
|
|
10771
|
+
"--output",
|
|
10772
|
+
"<path>"
|
|
10773
|
+
], {
|
|
10774
|
+
name: "attachments.download",
|
|
10775
|
+
args: { attachmentId }
|
|
10776
|
+
}),
|
|
10777
|
+
codeOnly: true
|
|
10778
|
+
})
|
|
10779
|
+
};
|
|
10780
|
+
//#endregion
|
|
10440
10781
|
//#region ../shared/src/agentOps/outcome.ts
|
|
10441
10782
|
const SERVER_ERROR_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
10442
10783
|
const DEFAULT_MESSAGES = {
|
|
@@ -10448,11 +10789,14 @@ const DEFAULT_MESSAGES = {
|
|
|
10448
10789
|
SCOPE_DENIED: "This agent's scope set does not allow this operation.",
|
|
10449
10790
|
NOT_FOUND: "The target or message does not exist or is not visible to this agent.",
|
|
10450
10791
|
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
|
|
10792
|
+
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different request.",
|
|
10452
10793
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10453
10794
|
UNAVAILABLE: "The Raft Server could not serve this operation right now.",
|
|
10454
10795
|
MODEL_ONLY: "This operation only counts when the model sees its result, so it cannot be run from code; nothing was sent."
|
|
10455
10796
|
};
|
|
10797
|
+
function notFoundNextAction(style) {
|
|
10798
|
+
return `Check the target spelling with \`${formatHint(RAFT_HINTS.serverInfo({ view: "channels" }), style)}\` or resolve the message id first.`;
|
|
10799
|
+
}
|
|
10456
10800
|
const DEFAULT_NEXT_ACTION = {
|
|
10457
10801
|
INVALID_REQUEST: "Fix the request arguments; nothing was sent.",
|
|
10458
10802
|
TRANSPORT_ERROR: "Check connectivity to the Raft Server and retry if the operation is safe to repeat.",
|
|
@@ -10460,9 +10804,9 @@ const DEFAULT_NEXT_ACTION = {
|
|
|
10460
10804
|
INVALID_RESPONSE: "Upgrade the SDK or report the Server version; the response shape is not the one this SDK knows.",
|
|
10461
10805
|
CAPABILITY_NOT_AUTHORIZED: "Ask a human who can mint credentials to include the missing capability.",
|
|
10462
10806
|
SCOPE_DENIED: "Ask a human with editAgents authority to extend this agent's scopes.",
|
|
10463
|
-
NOT_FOUND: "
|
|
10807
|
+
NOT_FOUND: notFoundNextAction("cli"),
|
|
10464
10808
|
CONFLICT: "Read the current state before repeating this operation.",
|
|
10465
|
-
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different
|
|
10809
|
+
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different request, or resend the identical request to reconcile.",
|
|
10466
10810
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10467
10811
|
UNAVAILABLE: "Retry in a moment.",
|
|
10468
10812
|
MODEL_ONLY: "Call it as a model tool call instead of from code."
|
|
@@ -10551,6 +10895,49 @@ function failureOutcome(error) {
|
|
|
10551
10895
|
text: formatOpErrorText(error)
|
|
10552
10896
|
};
|
|
10553
10897
|
}
|
|
10898
|
+
/**
|
|
10899
|
+
* Render the SDK's own default next action in a hint style. Only NOT_FOUND's
|
|
10900
|
+
* default names a command; it is set deep in the shared client-error mapping,
|
|
10901
|
+
* so `createRaft({ hints: "tool" })` re-renders it here instead of threading
|
|
10902
|
+
* the style through every failure. A Server-sent next action is left as is.
|
|
10903
|
+
*/
|
|
10904
|
+
function restyleDefaultNextAction(outcome, style) {
|
|
10905
|
+
if (style === "cli" || !outcome || typeof outcome !== "object") return outcome;
|
|
10906
|
+
const failure = outcome;
|
|
10907
|
+
if (failure.ok !== false || !failure.error || failure.error.nextAction !== DEFAULT_NEXT_ACTION.NOT_FOUND) return outcome;
|
|
10908
|
+
const error = {
|
|
10909
|
+
...failure.error,
|
|
10910
|
+
nextAction: notFoundNextAction(style)
|
|
10911
|
+
};
|
|
10912
|
+
const next = failure.next && failure.next.why === DEFAULT_NEXT_ACTION.NOT_FOUND ? {
|
|
10913
|
+
...failure.next,
|
|
10914
|
+
why: error.nextAction
|
|
10915
|
+
} : failure.next ?? null;
|
|
10916
|
+
return {
|
|
10917
|
+
...failure,
|
|
10918
|
+
error,
|
|
10919
|
+
next,
|
|
10920
|
+
text: formatOpErrorText(error)
|
|
10921
|
+
};
|
|
10922
|
+
}
|
|
10923
|
+
/**
|
|
10924
|
+
* A failure of a keyed write (`idempotencyKey`). A retryable failure (the
|
|
10925
|
+
* request may not have reached the Server, or the Server was unavailable)
|
|
10926
|
+
* says to repeat the SAME request with the SAME key, and carries the key in
|
|
10927
|
+
* `next.args.idempotencyKey`, so a caller that let the SDK generate it can
|
|
10928
|
+
* still retry without acting twice.
|
|
10929
|
+
*/
|
|
10930
|
+
function keyedWriteFailure(failure, idempotencyKey) {
|
|
10931
|
+
if (!failure.error.retryable) return failure;
|
|
10932
|
+
return {
|
|
10933
|
+
...failure,
|
|
10934
|
+
next: {
|
|
10935
|
+
kind: "retry_same_key",
|
|
10936
|
+
args: { idempotencyKey },
|
|
10937
|
+
why: "Repeat the same request with this idempotencyKey: if the first attempt was committed, the Server returns its result instead of acting twice."
|
|
10938
|
+
}
|
|
10939
|
+
};
|
|
10940
|
+
}
|
|
10554
10941
|
/** Map a shared-client failure to a failure outcome. */
|
|
10555
10942
|
function failureFromClientResult(result) {
|
|
10556
10943
|
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
@@ -10956,9 +11343,10 @@ function formatAgentSenderHandle(m) {
|
|
|
10956
11343
|
const desc = m.sender_description ?? null;
|
|
10957
11344
|
return desc ? `@${name} — ${desc}` : `@${name}`;
|
|
10958
11345
|
}
|
|
10959
|
-
function formatAgentAttachmentSuffix(attachments) {
|
|
11346
|
+
function formatAgentAttachmentSuffix(attachments, style = "cli") {
|
|
10960
11347
|
if (!attachments?.length) return "";
|
|
10961
|
-
|
|
11348
|
+
const download = formatHint(RAFT_HINTS.attachmentView(style === "tool" && attachments.length === 1 ? attachments[0].id : void 0), style);
|
|
11349
|
+
return ` [${attachments.length} attachment${attachments.length > 1 ? "s" : ""}: ${attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} — use ${download} to download]`;
|
|
10962
11350
|
}
|
|
10963
11351
|
function formatAgentTaskAssigneeSuffix(assigneeId, assigneeName) {
|
|
10964
11352
|
if (!assigneeId) return "";
|
|
@@ -10984,7 +11372,7 @@ function formatAgentTaskCurrentProjection(projection, taskNumber, neutralize) {
|
|
|
10984
11372
|
return `\n${lines.join("\n")}`;
|
|
10985
11373
|
}
|
|
10986
11374
|
/** One received-message line: header bracket + sender + content + suffixes. */
|
|
10987
|
-
function formatAgentMessageLine(m) {
|
|
11375
|
+
function formatAgentMessageLine(m, style = "cli") {
|
|
10988
11376
|
if (m.third_party_event) {
|
|
10989
11377
|
const msgId = m.message_id ? m.message_id.slice(0, 8) : m.third_party_event.id.slice(0, 8);
|
|
10990
11378
|
const time = m.timestamp ? formatUtcTimestamp(m.timestamp) : "-";
|
|
@@ -11000,22 +11388,22 @@ function formatAgentMessageLine(m) {
|
|
|
11000
11388
|
const time = m.timestamp ? formatUtcTimestamp(m.timestamp) : "-";
|
|
11001
11389
|
const senderType = ` type=${m.sender_type}`;
|
|
11002
11390
|
const content = indentAgentBodyContinuationLines(m.content ?? "");
|
|
11003
|
-
const attachSuffix = formatAgentAttachmentSuffix(m.attachments);
|
|
11391
|
+
const attachSuffix = formatAgentAttachmentSuffix(m.attachments, style);
|
|
11004
11392
|
const taskSuffix = m.task_status ? ` [task #${m.task_number} status=${m.task_status}${formatAgentTaskAssigneeSuffix(m.task_assignee_id, m.task_assignee_name)}]` : "";
|
|
11005
11393
|
return `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatAgentSenderHandle(m)}: ${content}${attachSuffix}${taskSuffix}${formatAgentReplyAffordanceSuffix(m)}${formatAgentTaskCurrentProjection(m.task_current_projection)}`;
|
|
11006
11394
|
}
|
|
11007
11395
|
/** Batch of received-message lines (`raft message check` output). */
|
|
11008
|
-
function formatAgentMessages(messages) {
|
|
11396
|
+
function formatAgentMessages(messages, style = "cli") {
|
|
11009
11397
|
if (messages.length === 0) return "No new inbox messages.";
|
|
11010
|
-
return messages.map(formatAgentMessageLine).join("\n");
|
|
11398
|
+
return messages.map((m) => formatAgentMessageLine(m, style)).join("\n");
|
|
11011
11399
|
}
|
|
11012
11400
|
/**
|
|
11013
11401
|
* `message check` hands over a bounded batch, oldest first per conversation;
|
|
11014
11402
|
* the server reports how many conversations still have unread after it.
|
|
11015
11403
|
*/
|
|
11016
|
-
function formatAgentInboxHint(hint) {
|
|
11404
|
+
function formatAgentInboxHint(hint, style = "cli") {
|
|
11017
11405
|
const n = hint.unread_conversations;
|
|
11018
|
-
return `Still unread: ${n} ${n === 1 ? "conversation" : "conversations"}. Run
|
|
11406
|
+
return `Still unread: ${n} ${n === 1 ? "conversation" : "conversations"}. Run \`${formatHint(RAFT_HINTS.inboxList(), style)}\` to list them.`;
|
|
11019
11407
|
}
|
|
11020
11408
|
//#endregion
|
|
11021
11409
|
//#region ../shared/src/agentOps/message.ts
|
|
@@ -11059,7 +11447,7 @@ function hasAgentMessageIdentity(envelope) {
|
|
|
11059
11447
|
return true;
|
|
11060
11448
|
}
|
|
11061
11449
|
/** Project one envelope; `null` when it has no conversation identity (skip it rather than render `#undefined`). */
|
|
11062
|
-
function projectRaftMessage(envelope) {
|
|
11450
|
+
function projectRaftMessage(envelope, style = "cli") {
|
|
11063
11451
|
if (!hasAgentMessageIdentity(envelope)) return null;
|
|
11064
11452
|
const like = toAgentMessageLike(envelope);
|
|
11065
11453
|
const id = nullableString(like.message_id);
|
|
@@ -11092,7 +11480,7 @@ function projectRaftMessage(envelope) {
|
|
|
11092
11480
|
id: nullableString(envelope.threadId),
|
|
11093
11481
|
replyCount: nullableNumber(envelope.replyCount)
|
|
11094
11482
|
},
|
|
11095
|
-
text: formatAgentMessageLine(like),
|
|
11483
|
+
text: formatAgentMessageLine(like, style),
|
|
11096
11484
|
raw: envelope
|
|
11097
11485
|
};
|
|
11098
11486
|
}
|
|
@@ -11100,8 +11488,8 @@ function sortBySeq(messages) {
|
|
|
11100
11488
|
return [...messages].sort((a, b) => (a.seq ?? Number.MAX_SAFE_INTEGER) - (b.seq ?? Number.MAX_SAFE_INTEGER));
|
|
11101
11489
|
}
|
|
11102
11490
|
/** Project a list, skipping envelopes without a conversation identity. */
|
|
11103
|
-
function projectRaftMessages(envelopes) {
|
|
11104
|
-
return envelopes.map(projectRaftMessage).filter((m) => m !== null);
|
|
11491
|
+
function projectRaftMessages(envelopes, style = "cli") {
|
|
11492
|
+
return envelopes.map((envelope) => projectRaftMessage(envelope, style)).filter((m) => m !== null);
|
|
11105
11493
|
}
|
|
11106
11494
|
/** The conversation fields a target string names; `null` for a shape this cannot read. */
|
|
11107
11495
|
function identityFromTarget(target) {
|
|
@@ -11133,12 +11521,12 @@ function identityFromTarget(target) {
|
|
|
11133
11521
|
* because the request already named the target; fill them from `target`
|
|
11134
11522
|
* instead of dropping the message.
|
|
11135
11523
|
*/
|
|
11136
|
-
function projectRaftMessagesInTarget(envelopes, target) {
|
|
11524
|
+
function projectRaftMessagesInTarget(envelopes, target, style = "cli") {
|
|
11137
11525
|
const identity = identityFromTarget(target);
|
|
11138
11526
|
return envelopes.map((envelope) => projectRaftMessage(identity && !hasAgentMessageIdentity(envelope) ? {
|
|
11139
11527
|
...envelope,
|
|
11140
11528
|
...identity
|
|
11141
|
-
} : envelope)).filter((m) => m !== null);
|
|
11529
|
+
} : envelope, style)).filter((m) => m !== null);
|
|
11142
11530
|
}
|
|
11143
11531
|
//#endregion
|
|
11144
11532
|
//#region ../shared/src/agentOps/interrupt.ts
|
|
@@ -11150,7 +11538,7 @@ function isInterrupted(outcome) {
|
|
|
11150
11538
|
function unreadMessagesInterrupt(input) {
|
|
11151
11539
|
const { hold, target } = input;
|
|
11152
11540
|
const withheld = input.withheld === true || hold.freshnessContextMode === "withheld";
|
|
11153
|
-
const heldMessages = withheld ? [] : projectRaftMessagesInTarget(hold.heldMessages ?? [], target);
|
|
11541
|
+
const heldMessages = withheld ? [] : projectRaftMessagesInTarget(hold.heldMessages ?? [], target, input.hints);
|
|
11154
11542
|
const newMessageCount = withheld ? hold.withheldMessageCount ?? hold.newMessageCount ?? 0 : hold.newMessageCount ?? heldMessages.length;
|
|
11155
11543
|
const omittedMessageCount = withheld ? 0 : hold.omittedMessageCount ?? 0;
|
|
11156
11544
|
return {
|
|
@@ -11441,18 +11829,9 @@ const inboxPullFields = {
|
|
|
11441
11829
|
};
|
|
11442
11830
|
const checkInboxRequestSchema = requestSchema()(object(inboxPullFields));
|
|
11443
11831
|
const MAX_DRAIN_ROUNDS = 50;
|
|
11444
|
-
function batchNext(batch) {
|
|
11445
|
-
if (batch.hasMore) return {
|
|
11446
|
-
|
|
11447
|
-
command: "raft message check",
|
|
11448
|
-
...batch.cursor === null ? {} : { args: { since: batch.cursor } },
|
|
11449
|
-
why: "The Server trimmed this batch; more messages are pending."
|
|
11450
|
-
};
|
|
11451
|
-
if ((batch.stillUnreadConversations ?? 0) > 0) return {
|
|
11452
|
-
kind: "list_inbox",
|
|
11453
|
-
command: "raft inbox check",
|
|
11454
|
-
why: "Conversations remain unread beyond this batch."
|
|
11455
|
-
};
|
|
11832
|
+
function batchNext(batch, style) {
|
|
11833
|
+
if (batch.hasMore) return hintStep("check_inbox_again", RAFT_HINTS.messageCheck(), "The Server trimmed this batch; more messages are pending.", style, batch.cursor === null ? void 0 : { since: batch.cursor });
|
|
11834
|
+
if ((batch.stillUnreadConversations ?? 0) > 0) return hintStep("list_inbox", RAFT_HINTS.inboxList(), "Conversations remain unread beyond this batch.", style);
|
|
11456
11835
|
if (batch.messages.length > 0) return {
|
|
11457
11836
|
kind: "reply_or_act",
|
|
11458
11837
|
args: { target: batch.messages[0].target },
|
|
@@ -11460,11 +11839,11 @@ function batchNext(batch) {
|
|
|
11460
11839
|
};
|
|
11461
11840
|
return null;
|
|
11462
11841
|
}
|
|
11463
|
-
function batchText(batch) {
|
|
11464
|
-
const lines = [formatAgentMessages(batch.messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11465
|
-
if (batch.hasMore) lines.push(
|
|
11842
|
+
function batchText(batch, style) {
|
|
11843
|
+
const lines = [formatAgentMessages(batch.messages.map((m) => toAgentMessageLike(m.raw)), style)];
|
|
11844
|
+
if (batch.hasMore) lines.push(`More messages are pending. Run \`${formatHint(RAFT_HINTS.messageCheck(), style)}\` again.`);
|
|
11466
11845
|
else if (batch.messages.length > 0) lines.push("No more new inbox messages.");
|
|
11467
|
-
if ((batch.stillUnreadConversations ?? 0) > 0) lines.push(formatAgentInboxHint({ unread_conversations: batch.stillUnreadConversations }));
|
|
11846
|
+
if ((batch.stillUnreadConversations ?? 0) > 0) lines.push(formatAgentInboxHint({ unread_conversations: batch.stillUnreadConversations }, style));
|
|
11468
11847
|
return lines.join("\n");
|
|
11469
11848
|
}
|
|
11470
11849
|
function recordExactSeen(frontier, messages) {
|
|
@@ -11477,7 +11856,8 @@ function recordExactSeen(frontier, messages) {
|
|
|
11477
11856
|
for (const [target, seqs] of byTarget) frontier.recordExact(target, seqs);
|
|
11478
11857
|
}
|
|
11479
11858
|
/** One bounded pull. Records exact seen seqs on `frontier` (sparse drains never advance the high-water mark). */
|
|
11480
|
-
async function checkInbox(client, request = {}, frontier) {
|
|
11859
|
+
async function checkInbox(client, request = {}, frontier, options = {}) {
|
|
11860
|
+
const style = options.hints ?? "cli";
|
|
11481
11861
|
const invalid = validateOpRequest(checkInboxRequestSchema, request);
|
|
11482
11862
|
if (invalid) return invalid;
|
|
11483
11863
|
const ack = request.ack ?? "cursor";
|
|
@@ -11488,7 +11868,7 @@ async function checkInbox(client, request = {}, frontier) {
|
|
|
11488
11868
|
});
|
|
11489
11869
|
if (!result.ok) return failureFromClientResult(result);
|
|
11490
11870
|
const data = result.data;
|
|
11491
|
-
const messages = sortBySeq(projectRaftMessages(data.events));
|
|
11871
|
+
const messages = sortBySeq(projectRaftMessages(data.events, style));
|
|
11492
11872
|
recordExactSeen(frontier, messages);
|
|
11493
11873
|
const batch = {
|
|
11494
11874
|
messages,
|
|
@@ -11502,8 +11882,8 @@ async function checkInbox(client, request = {}, frontier) {
|
|
|
11502
11882
|
ok: true,
|
|
11503
11883
|
state: messages.length > 0 ? "batch" : "empty",
|
|
11504
11884
|
data: batch,
|
|
11505
|
-
next: batchNext(batch),
|
|
11506
|
-
text: batchText(batch)
|
|
11885
|
+
next: batchNext(batch, style),
|
|
11886
|
+
text: batchText(batch, style)
|
|
11507
11887
|
};
|
|
11508
11888
|
}
|
|
11509
11889
|
const drainInboxRequestSchema = requestSchema()(object(inboxPullFields));
|
|
@@ -11517,7 +11897,7 @@ const drainInboxRequestSchema = requestSchema()(object(inboxPullFields));
|
|
|
11517
11897
|
* return value summarises the drain; the batch that ends the iteration is
|
|
11518
11898
|
* acknowledged only by the caller's next pull (`summary.cursor`).
|
|
11519
11899
|
*/
|
|
11520
|
-
async function* drainInbox(client, request = {}, frontier, onPull) {
|
|
11900
|
+
async function* drainInbox(client, request = {}, frontier, onPull, options = {}) {
|
|
11521
11901
|
const ack = request.ack ?? "cursor";
|
|
11522
11902
|
const invalid = validateOpRequest(drainInboxRequestSchema, request);
|
|
11523
11903
|
if (invalid) return {
|
|
@@ -11539,7 +11919,7 @@ async function* drainInbox(client, request = {}, frontier, onPull) {
|
|
|
11539
11919
|
...sent === null ? {} : { since: sent },
|
|
11540
11920
|
limit: request.limit,
|
|
11541
11921
|
ack
|
|
11542
|
-
}, frontier);
|
|
11922
|
+
}, frontier, options);
|
|
11543
11923
|
if (!round.ok) return {
|
|
11544
11924
|
cursor,
|
|
11545
11925
|
ackMode,
|
|
@@ -11589,25 +11969,18 @@ async function* drainInbox(client, request = {}, frontier, onPull) {
|
|
|
11589
11969
|
};
|
|
11590
11970
|
}
|
|
11591
11971
|
/** Fold a finished drain into one outcome; a drain that failed before its first batch is that failure. */
|
|
11592
|
-
function drainedInboxOutcome(batches, summary) {
|
|
11972
|
+
function drainedInboxOutcome(batches, summary, options = {}) {
|
|
11593
11973
|
if (summary.error && batches.length === 0) return summary.error;
|
|
11974
|
+
const style = options.hints ?? "cli";
|
|
11594
11975
|
const messages = batches.flatMap((batch) => batch.messages);
|
|
11595
|
-
const lines = [formatAgentMessages(messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11976
|
+
const lines = [formatAgentMessages(messages.map((m) => toAgentMessageLike(m.raw)), style)];
|
|
11596
11977
|
if (summary.error) lines.push("The drain stopped early on an error; more messages may be pending. Check the inbox again.");
|
|
11597
11978
|
else if (summary.hasMore) lines.push("More messages are pending. Check the inbox again.");
|
|
11598
11979
|
else if (messages.length > 0) lines.push("No more new inbox messages.");
|
|
11599
11980
|
const stillUnread = summary.stillUnreadConversations ?? 0;
|
|
11600
|
-
if (stillUnread > 0) lines.push(formatAgentInboxHint({ unread_conversations: stillUnread }));
|
|
11981
|
+
if (stillUnread > 0) lines.push(formatAgentInboxHint({ unread_conversations: stillUnread }, style));
|
|
11601
11982
|
const first = messages[0];
|
|
11602
|
-
const next = summary.hasMore ? {
|
|
11603
|
-
kind: "check_inbox_again",
|
|
11604
|
-
command: "raft message check",
|
|
11605
|
-
why: "More messages are pending."
|
|
11606
|
-
} : stillUnread > 0 ? {
|
|
11607
|
-
kind: "list_inbox",
|
|
11608
|
-
command: "raft inbox check",
|
|
11609
|
-
why: "Conversations remain unread beyond this drain."
|
|
11610
|
-
} : first ? {
|
|
11983
|
+
const next = summary.hasMore ? hintStep("check_inbox_again", RAFT_HINTS.messageCheck(), "More messages are pending.", style) : stillUnread > 0 ? hintStep("list_inbox", RAFT_HINTS.inboxList(), "Conversations remain unread beyond this drain.", style) : first ? {
|
|
11611
11984
|
kind: "reply_or_act",
|
|
11612
11985
|
args: { target: first.target },
|
|
11613
11986
|
why: "Handle the messages above; reply where each one came from."
|
|
@@ -11629,23 +12002,21 @@ const listInboxRequestSchema = requestSchema()(object({
|
|
|
11629
12002
|
before: number$1().int().nonnegative().optional().describe("Next page: the nextBeforeSeq of the previous page."),
|
|
11630
12003
|
limit: number$1().int().positive().optional().describe("Conversations per page, 1..50 (Server default 20).")
|
|
11631
12004
|
}));
|
|
11632
|
-
function
|
|
11633
|
-
return
|
|
12005
|
+
function openHint(item) {
|
|
12006
|
+
return RAFT_HINTS.messageRead({
|
|
12007
|
+
target: item.target,
|
|
12008
|
+
after: item.lastReadSeq
|
|
12009
|
+
});
|
|
11634
12010
|
}
|
|
11635
|
-
function listingNext(listing) {
|
|
12011
|
+
function listingNext(listing, style) {
|
|
11636
12012
|
const first = listing.conversations[0];
|
|
11637
|
-
if (first) return {
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
target: first.target,
|
|
11642
|
-
after: first.lastReadSeq
|
|
11643
|
-
},
|
|
11644
|
-
why: "Open the newest unread conversation from your read position."
|
|
11645
|
-
};
|
|
12013
|
+
if (first) return hintStep("read_target", openHint(first), "Open the newest unread conversation from your read position.", style, {
|
|
12014
|
+
target: first.target,
|
|
12015
|
+
after: first.lastReadSeq
|
|
12016
|
+
});
|
|
11646
12017
|
return null;
|
|
11647
12018
|
}
|
|
11648
|
-
function listingText(listing) {
|
|
12019
|
+
function listingText(listing, style) {
|
|
11649
12020
|
const t = listing.totals;
|
|
11650
12021
|
const head = t.conversations === 0 ? "Inbox: nothing unread." : `Inbox: ${t.conversations} unread ${t.conversations === 1 ? "conversation" : "conversations"} (${t.dms} DMs, ${t.mentions} with mentions).${listing.conversations.length > 0 ? " Newest activity first." : ""}`;
|
|
11651
12022
|
const rows = listing.conversations.map((c) => {
|
|
@@ -11653,8 +12024,14 @@ function listingText(listing) {
|
|
|
11653
12024
|
return `${c.target} · ${c.unread} unread${flags ? ` · ${flags}` : ""}${c.latestSenderName ? ` · latest @${c.latestSenderName}` : ""}\n open: ${c.openCommand}`;
|
|
11654
12025
|
});
|
|
11655
12026
|
const trailer = [];
|
|
11656
|
-
if (listing.hasMore && listing.nextBeforeSeq !== null)
|
|
11657
|
-
|
|
12027
|
+
if (listing.hasMore && listing.nextBeforeSeq !== null) {
|
|
12028
|
+
const more = RAFT_HINTS.inboxList({
|
|
12029
|
+
...listing.view === "mentions" ? { view: "mentions" } : {},
|
|
12030
|
+
before: listing.nextBeforeSeq
|
|
12031
|
+
});
|
|
12032
|
+
trailer.push(`More: ${formatHint(more, style)}`);
|
|
12033
|
+
}
|
|
12034
|
+
const next = listingNext(listing, style);
|
|
11658
12035
|
trailer.push(next?.command ? `Next: open the first conversation above: ${next.command}` : "Next: nothing to do.");
|
|
11659
12036
|
return [
|
|
11660
12037
|
head,
|
|
@@ -11663,7 +12040,8 @@ function listingText(listing) {
|
|
|
11663
12040
|
].join("\n");
|
|
11664
12041
|
}
|
|
11665
12042
|
/** The agent's Activity panel. Lists only; nothing is consumed. */
|
|
11666
|
-
async function listInbox(client, request = {}) {
|
|
12043
|
+
async function listInbox(client, request = {}, options = {}) {
|
|
12044
|
+
const style = options.hints ?? "cli";
|
|
11667
12045
|
const invalid = validateOpRequest(listInboxRequestSchema, request);
|
|
11668
12046
|
if (invalid) return invalid;
|
|
11669
12047
|
const result = await client.inbox.list({
|
|
@@ -11684,7 +12062,7 @@ async function listInbox(client, request = {}) {
|
|
|
11684
12062
|
activitySeq: item.activitySeq,
|
|
11685
12063
|
latestSenderName: item.latestSenderName,
|
|
11686
12064
|
latestAt: item.latestAt,
|
|
11687
|
-
openCommand:
|
|
12065
|
+
openCommand: formatHint(openHint(item), style)
|
|
11688
12066
|
})),
|
|
11689
12067
|
totals: data.totals,
|
|
11690
12068
|
hasMore: data.hasMore,
|
|
@@ -11694,8 +12072,8 @@ async function listInbox(client, request = {}) {
|
|
|
11694
12072
|
ok: true,
|
|
11695
12073
|
state: listing.conversations.length > 0 ? "listed" : "empty",
|
|
11696
12074
|
data: listing,
|
|
11697
|
-
next: listingNext(listing),
|
|
11698
|
-
text: listingText(listing)
|
|
12075
|
+
next: listingNext(listing, style),
|
|
12076
|
+
text: listingText(listing, style)
|
|
11699
12077
|
};
|
|
11700
12078
|
}
|
|
11701
12079
|
//#endregion
|
|
@@ -11710,38 +12088,34 @@ const readHistoryRequestSchema = requestSchema()(object({
|
|
|
11710
12088
|
limit: number$1().int().positive().optional().describe("Maximum messages in the window."),
|
|
11711
12089
|
consume: boolean().optional().describe("false reads without consuming: the conversation is not marked read and nothing counts as seen.")
|
|
11712
12090
|
}));
|
|
11713
|
-
function historyNext(page) {
|
|
12091
|
+
function historyNext(page, style) {
|
|
11714
12092
|
const seqs = page.messages.map((m) => m.seq).filter((s) => s !== null);
|
|
11715
12093
|
if (page.hasNewer && seqs.length > 0) {
|
|
11716
12094
|
const max = Math.max(...seqs);
|
|
11717
|
-
return {
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
why: "Newer messages exist beyond this window."
|
|
11725
|
-
};
|
|
12095
|
+
return hintStep("read_newer", RAFT_HINTS.messageRead({
|
|
12096
|
+
target: page.target,
|
|
12097
|
+
after: max
|
|
12098
|
+
}), "Newer messages exist beyond this window.", style, {
|
|
12099
|
+
target: page.target,
|
|
12100
|
+
after: max
|
|
12101
|
+
});
|
|
11726
12102
|
}
|
|
11727
12103
|
if (page.hasOlder && seqs.length > 0) {
|
|
11728
12104
|
const min = Math.min(...seqs);
|
|
11729
|
-
return {
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
why: "Older messages exist before this window; read them only if the task needs them."
|
|
11737
|
-
};
|
|
12105
|
+
return hintStep("read_older", RAFT_HINTS.messageRead({
|
|
12106
|
+
target: page.target,
|
|
12107
|
+
before: min
|
|
12108
|
+
}), "Older messages exist before this window; read them only if the task needs them.", style, {
|
|
12109
|
+
target: page.target,
|
|
12110
|
+
before: min
|
|
12111
|
+
});
|
|
11738
12112
|
}
|
|
11739
12113
|
return null;
|
|
11740
12114
|
}
|
|
11741
|
-
function historyText(page) {
|
|
12115
|
+
function historyText(page, style) {
|
|
11742
12116
|
if (page.messages.length === 0) return `No messages in ${page.target}.`;
|
|
11743
|
-
const lines = [formatAgentMessages(page.messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11744
|
-
const next = historyNext(page);
|
|
12117
|
+
const lines = [formatAgentMessages(page.messages.map((m) => toAgentMessageLike(m.raw)), style)];
|
|
12118
|
+
const next = historyNext(page, style);
|
|
11745
12119
|
if (next?.command) lines.push(`${next.kind === "read_newer" ? "Newer" : "Older"} exist: ${next.command}`);
|
|
11746
12120
|
return lines.join("\n");
|
|
11747
12121
|
}
|
|
@@ -11761,7 +12135,8 @@ function recordHistorySeen(frontier, requested, page, around) {
|
|
|
11761
12135
|
}
|
|
11762
12136
|
frontier.recordExact(page.target, seqs);
|
|
11763
12137
|
}
|
|
11764
|
-
async function readHistory(client, request, frontier) {
|
|
12138
|
+
async function readHistory(client, request, frontier, options = {}) {
|
|
12139
|
+
const style = options.hints ?? "cli";
|
|
11765
12140
|
const invalid = validateOpRequest(readHistoryRequestSchema, request);
|
|
11766
12141
|
if (invalid) return invalid;
|
|
11767
12142
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to read history." }));
|
|
@@ -11777,7 +12152,7 @@ async function readHistory(client, request, frontier) {
|
|
|
11777
12152
|
const data = result.data;
|
|
11778
12153
|
const page = {
|
|
11779
12154
|
target: typeof data.target === "string" && data.target ? data.target : request.target,
|
|
11780
|
-
messages: sortBySeq(projectRaftMessages(data.messages)),
|
|
12155
|
+
messages: sortBySeq(projectRaftMessages(data.messages, style)),
|
|
11781
12156
|
hasOlder: data.has_older === true,
|
|
11782
12157
|
hasNewer: data.has_newer === true,
|
|
11783
12158
|
lastReadSeq: typeof data.last_read_seq === "number" ? data.last_read_seq : null,
|
|
@@ -11788,8 +12163,8 @@ async function readHistory(client, request, frontier) {
|
|
|
11788
12163
|
ok: true,
|
|
11789
12164
|
state: page.messages.length > 0 ? "page" : "empty",
|
|
11790
12165
|
data: page,
|
|
11791
|
-
next: historyNext(page),
|
|
11792
|
-
text: historyText(page)
|
|
12166
|
+
next: historyNext(page, style),
|
|
12167
|
+
text: historyText(page, style)
|
|
11793
12168
|
};
|
|
11794
12169
|
}
|
|
11795
12170
|
/** Attempts for a keyed send whose request never reached the Server (transport failure only). */
|
|
@@ -11813,68 +12188,60 @@ const replyToRequestSchema = requestSchema()(object({
|
|
|
11813
12188
|
...sendMessageFields
|
|
11814
12189
|
}));
|
|
11815
12190
|
/** Today's held text (unchanged); it is the interrupt's `context` and the outcome's `text`. */
|
|
11816
|
-
function heldText(held, action) {
|
|
12191
|
+
function heldText(held, action, style) {
|
|
11817
12192
|
const noun = held.newMessageCount === 1 ? "message" : "messages";
|
|
11818
12193
|
const head = `Held — ${held.newMessageCount} unread ${noun} in ${held.target}. ${action}`;
|
|
11819
12194
|
if (held.withheld) return `${head}\nContext withheld (reviewer isolation).`;
|
|
11820
12195
|
const lines = [head];
|
|
11821
12196
|
if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
|
|
11822
12197
|
if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
|
|
11823
|
-
if (held.heldMessages.length > 0) lines.push(formatAgentMessages(held.heldMessages.map((m) => toAgentMessageLike(m.raw))));
|
|
12198
|
+
if (held.heldMessages.length > 0) lines.push(formatAgentMessages(held.heldMessages.map((m) => toAgentMessageLike(m.raw)), style));
|
|
11824
12199
|
if (!held.contextComplete) lines.push("Not all of them are shown here; read the conversation before sending again.");
|
|
11825
|
-
lines.push(`Full text:
|
|
12200
|
+
lines.push(`Full text: ${formatHint(RAFT_HINTS.messageRead({ target: held.target }), style)}`);
|
|
11826
12201
|
return lines.join("\n");
|
|
11827
12202
|
}
|
|
11828
12203
|
/**
|
|
11829
12204
|
* A Server freshness hold as the `unread_messages` interrupt: the structured
|
|
11830
12205
|
* details, today's held text as `context`, and the given resume / cancel.
|
|
11831
12206
|
*/
|
|
11832
|
-
function heldInterrupt(target, data, action, resume, cancel) {
|
|
12207
|
+
function heldInterrupt(target, data, action, resume, cancel, style = "cli") {
|
|
11833
12208
|
const interrupt = unreadMessagesInterrupt({
|
|
11834
12209
|
target,
|
|
11835
12210
|
hold: data,
|
|
11836
12211
|
context: "",
|
|
11837
12212
|
resume,
|
|
11838
|
-
...cancel ? { cancel } : {}
|
|
12213
|
+
...cancel ? { cancel } : {},
|
|
12214
|
+
hints: style
|
|
11839
12215
|
});
|
|
11840
12216
|
return {
|
|
11841
12217
|
...interrupt,
|
|
11842
|
-
context: heldText(interrupt, action)
|
|
12218
|
+
context: heldText(interrupt, action, style)
|
|
11843
12219
|
};
|
|
11844
12220
|
}
|
|
11845
|
-
function heldSendNext(interrupt) {
|
|
11846
|
-
return {
|
|
11847
|
-
kind: "resend",
|
|
11848
|
-
command: `raft message read --target "${interrupt.target}"`,
|
|
11849
|
-
args: { target: interrupt.target },
|
|
11850
|
-
why: interrupt.withheld ? "Newer messages exist in this conversation but were withheld; read them, then resume the send (interrupt.resume, same idempotencyKey) or cancel it." : !interrupt.contextComplete ? "Newer messages arrived in this conversation and not all of them are shown here; read the conversation, then resume the send (interrupt.resume, same idempotencyKey) or cancel it." : "Newer messages arrived in this conversation. Show interrupt.context to the model, call frontier.recordHeld(interrupt) to attest that, then resume the send (interrupt.resume, same idempotencyKey) or cancel it."
|
|
11851
|
-
};
|
|
12221
|
+
function heldSendNext(interrupt, style) {
|
|
12222
|
+
return hintStep("resend", RAFT_HINTS.messageRead({ target: interrupt.target }), interrupt.withheld ? "Newer messages exist in this conversation but were withheld; read them, then resume the send (interrupt.resume, same idempotencyKey) or cancel it." : !interrupt.contextComplete ? "Newer messages arrived in this conversation and not all of them are shown here; read the conversation, then resume the send (interrupt.resume, same idempotencyKey) or cancel it." : "Newer messages arrived in this conversation. Show interrupt.context to the model, call frontier.recordHeld(interrupt) to attest that, then resume the send (interrupt.resume, same idempotencyKey) or cancel it.", style, { target: interrupt.target });
|
|
11852
12223
|
}
|
|
11853
12224
|
function isHeldResponse(data) {
|
|
11854
12225
|
return Boolean(data) && typeof data === "object" && data.state === "held";
|
|
11855
12226
|
}
|
|
11856
|
-
function sentOutcome(target, data) {
|
|
12227
|
+
function sentOutcome(target, data, style) {
|
|
11857
12228
|
const sent = {
|
|
11858
12229
|
messageId: data.messageId,
|
|
11859
12230
|
messageSeq: typeof data.messageSeq === "number" ? data.messageSeq : null,
|
|
11860
12231
|
unresolvedMentionHandles: data.unresolvedMentionHandles ?? [],
|
|
11861
|
-
recentUnread: projectRaftMessages(data.recentUnread ?? [])
|
|
12232
|
+
recentUnread: projectRaftMessages(data.recentUnread ?? [], style)
|
|
11862
12233
|
};
|
|
11863
12234
|
const warn = sent.unresolvedMentionHandles.length > 0 ? ` Unresolved @handles: ${sent.unresolvedMentionHandles.map((h) => `@${h}`).join(", ")}.` : "";
|
|
11864
12235
|
return {
|
|
11865
12236
|
ok: true,
|
|
11866
12237
|
state: "sent",
|
|
11867
12238
|
data: sent,
|
|
11868
|
-
next: sent.recentUnread.length > 0 ? {
|
|
11869
|
-
kind: "read_target",
|
|
11870
|
-
command: `raft message read --target "${target}"`,
|
|
11871
|
-
args: { target },
|
|
11872
|
-
why: "Newer messages arrived while you were sending."
|
|
11873
|
-
} : null,
|
|
12239
|
+
next: sent.recentUnread.length > 0 ? hintStep("read_target", RAFT_HINTS.messageRead({ target }), "Newer messages arrived while you were sending.", style, { target }) : null,
|
|
11874
12240
|
text: `Message sent to ${target}. Message ID: ${sent.messageId}${warn}`
|
|
11875
12241
|
};
|
|
11876
12242
|
}
|
|
11877
|
-
async function sendMessage(client, request, frontier) {
|
|
12243
|
+
async function sendMessage(client, request, frontier, options = {}) {
|
|
12244
|
+
const style = options.hints ?? "cli";
|
|
11878
12245
|
const invalid = validateOpRequest(sendMessageRequestSchema, request);
|
|
11879
12246
|
if (invalid) return invalid;
|
|
11880
12247
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to send a message." }));
|
|
@@ -11898,16 +12265,16 @@ async function sendMessage(client, request, frontier) {
|
|
|
11898
12265
|
if (!result.ok) return failureFromClientResult(result);
|
|
11899
12266
|
const data = result.data;
|
|
11900
12267
|
if (isHeldResponse(data)) {
|
|
11901
|
-
const interrupt = heldInterrupt(request.target, data, "Your message was not sent.", inProcessSendResume(idempotencyKey));
|
|
12268
|
+
const interrupt = heldInterrupt(request.target, data, "Your message was not sent.", inProcessSendResume(idempotencyKey), void 0, style);
|
|
11902
12269
|
return {
|
|
11903
12270
|
ok: true,
|
|
11904
12271
|
state: "interrupted",
|
|
11905
12272
|
interrupt,
|
|
11906
|
-
next: heldSendNext(interrupt),
|
|
12273
|
+
next: heldSendNext(interrupt, style),
|
|
11907
12274
|
text: interrupt.context
|
|
11908
12275
|
};
|
|
11909
12276
|
}
|
|
11910
|
-
if (data.state === "sent") return sentOutcome(request.target, data);
|
|
12277
|
+
if (data.state === "sent") return sentOutcome(request.target, data, style);
|
|
11911
12278
|
return failureOutcome(opError("INVALID_RESPONSE", { message: `Unexpected send state "${data.state}".` }));
|
|
11912
12279
|
}
|
|
11913
12280
|
function taskTimestamps(t) {
|
|
@@ -11989,13 +12356,13 @@ function formatAgentMyTaskList(data, statusFilter) {
|
|
|
11989
12356
|
].join("\n");
|
|
11990
12357
|
}
|
|
11991
12358
|
/** Receipt for task create. */
|
|
11992
|
-
function formatAgentTasksCreated(channel, data) {
|
|
12359
|
+
function formatAgentTasksCreated(channel, data, style = "cli") {
|
|
11993
12360
|
const created = data.tasks.map((t) => {
|
|
11994
12361
|
const assignee = t.claimedById ? t.claimedByName ? `@${t.claimedByName}` : "<unresolved>" : "unassigned";
|
|
11995
12362
|
const resourceReceipt = t.requiresResourceReceipt ? " resource-receipt=pending" : "";
|
|
11996
12363
|
return `#${t.taskNumber} [${t.status}] assignee=${assignee} claimedAt=${t.claimedAt ?? "null"} msg=${t.messageId.slice(0, 8)}${resourceReceipt} "${t.title}"`;
|
|
11997
12364
|
}).join("\n");
|
|
11998
|
-
const threadHints = data.tasks.map((t) => `#${t.taskNumber} →
|
|
12365
|
+
const threadHints = data.tasks.map((t) => `#${t.taskNumber} → ${formatHint(RAFT_HINTS.messageSend({ target: `${channel}:${t.messageId.slice(0, 8)}` }), style)}`).join("\n");
|
|
11999
12366
|
const receipt = data.assignmentReceipt ? `\n\nAssignment receipt (msg=${data.assignmentReceipt.messageId.slice(0, 8)}):\n${data.assignmentReceipt.content}` : "";
|
|
12000
12367
|
return `Created ${data.tasks.length} task(s) in ${channel}:\n${created}${receipt}\n\nTo follow up in each task's thread:\n${threadHints}`;
|
|
12001
12368
|
}
|
|
@@ -12018,7 +12385,7 @@ function agentTaskThreadTarget(target, messageId) {
|
|
|
12018
12385
|
return `${canonicalAgentTaskTarget(target)}:${messageId.slice(0, 8)}`;
|
|
12019
12386
|
}
|
|
12020
12387
|
/** Claim results incl. concurrency-lock guidance on failed claims. */
|
|
12021
|
-
function formatAgentClaimResults(channel, data) {
|
|
12388
|
+
function formatAgentClaimResults(channel, data, style = "cli") {
|
|
12022
12389
|
const lines = data.results.map((r) => {
|
|
12023
12390
|
const label = r.taskNumber ? `#${r.taskNumber}` : `msg:${r.messageId}`;
|
|
12024
12391
|
if (r.success) return `${label} (msg:${r.messageId ? r.messageId.slice(0, 8) : ""}): claimed`;
|
|
@@ -12030,7 +12397,7 @@ function formatAgentClaimResults(channel, data) {
|
|
|
12030
12397
|
const failed = data.results.length - succeeded;
|
|
12031
12398
|
let summary = `${succeeded} claimed`;
|
|
12032
12399
|
if (failed > 0) summary += `, ${failed} failed`;
|
|
12033
|
-
const claimedMsgs = data.results.filter((r) => r.success && r.messageId).map((r) => `#${r.taskNumber} →
|
|
12400
|
+
const claimedMsgs = data.results.filter((r) => r.success && r.messageId).map((r) => `#${r.taskNumber} → ${formatHint(RAFT_HINTS.messageSend({ target: agentTaskThreadTarget(channel, r.messageId) }), style)}`).join("\n");
|
|
12034
12401
|
const threadHint = claimedMsgs ? `\n\nFollow up in each task's thread:\n${claimedMsgs}` : "";
|
|
12035
12402
|
return `Claim results (${summary}):\n${lines.join("\n")}${threadHint}`;
|
|
12036
12403
|
}
|
|
@@ -12047,13 +12414,13 @@ function formatAgentTaskDeleted(taskNumber) {
|
|
|
12047
12414
|
return `#${taskNumber} deleted.`;
|
|
12048
12415
|
}
|
|
12049
12416
|
/** Message→task conversion receipt. */
|
|
12050
|
-
function formatAgentTaskConverted(channel, task) {
|
|
12417
|
+
function formatAgentTaskConverted(channel, task, style = "cli") {
|
|
12051
12418
|
const target = `${canonicalAgentTaskTarget(channel)}:${task.messageId.slice(0, 8)}`;
|
|
12052
12419
|
return [
|
|
12053
12420
|
`Converted msg=${task.messageId.slice(0, 8)} to task #${task.taskNumber} [${task.status}] assignee=unassigned "${task.title}"`,
|
|
12054
12421
|
"",
|
|
12055
12422
|
`To follow up in the task's thread:`,
|
|
12056
|
-
|
|
12423
|
+
formatHint(RAFT_HINTS.messageSend({ target }), style)
|
|
12057
12424
|
].join("\n");
|
|
12058
12425
|
}
|
|
12059
12426
|
function formatAgentTaskAmended(data) {
|
|
@@ -12115,15 +12482,16 @@ const claimTasksRequestSchema = requestSchema()(object({
|
|
|
12115
12482
|
* A held task call: the interrupt whose resume is the identical command, with
|
|
12116
12483
|
* no cancel (a held task call saved nothing). `context` is today's held text.
|
|
12117
12484
|
*/
|
|
12118
|
-
function heldTaskInterrupt(target, data, action, after, argv) {
|
|
12485
|
+
function heldTaskInterrupt(target, data, action, after, argv, style) {
|
|
12119
12486
|
const interrupt = unreadMessagesInterrupt({
|
|
12120
12487
|
target,
|
|
12121
12488
|
hold: data,
|
|
12122
12489
|
context: "",
|
|
12123
|
-
resume: { argv }
|
|
12490
|
+
resume: { argv },
|
|
12491
|
+
hints: style
|
|
12124
12492
|
});
|
|
12125
12493
|
const noun = interrupt.newMessageCount === 1 ? "message" : "messages";
|
|
12126
|
-
const context = `Held — ${interrupt.newMessageCount} unread ${noun} in ${target}. ${action}\nRead them with:
|
|
12494
|
+
const context = `Held — ${interrupt.newMessageCount} unread ${noun} in ${target}. ${action}\nRead them with: ${formatHint(RAFT_HINTS.messageRead({ target }), style)}\n${after}`;
|
|
12127
12495
|
return {
|
|
12128
12496
|
...interrupt,
|
|
12129
12497
|
context
|
|
@@ -12135,17 +12503,14 @@ function rowState(result) {
|
|
|
12135
12503
|
if (result.conflict?.kind === "claim_conflict") return "conflict";
|
|
12136
12504
|
return "refused";
|
|
12137
12505
|
}
|
|
12138
|
-
function claimNext(claim) {
|
|
12506
|
+
function claimNext(claim, style) {
|
|
12139
12507
|
const first = claim.rows.find((row) => row.mayWork);
|
|
12140
12508
|
if (first) {
|
|
12141
12509
|
const thread = first.messageId ? agentTaskThreadTarget(claim.target, first.messageId) : null;
|
|
12142
|
-
|
|
12510
|
+
const why = "The claim is yours; post progress in the task's thread.";
|
|
12511
|
+
return thread ? hintStep("start_work", RAFT_HINTS.messageSend({ target: thread }), why, style, { target: thread }) : {
|
|
12143
12512
|
kind: "start_work",
|
|
12144
|
-
|
|
12145
|
-
command: `raft message send --target "${thread}"`,
|
|
12146
|
-
args: { thread }
|
|
12147
|
-
} : {},
|
|
12148
|
-
why: "The claim is yours; post progress in the task's thread."
|
|
12513
|
+
why
|
|
12149
12514
|
};
|
|
12150
12515
|
}
|
|
12151
12516
|
return {
|
|
@@ -12153,10 +12518,11 @@ function claimNext(claim) {
|
|
|
12153
12518
|
why: "No claim authorised work. Do not retry the identical claim and do not start conflicting execution; if you own this lane, correct the routing in the original thread."
|
|
12154
12519
|
};
|
|
12155
12520
|
}
|
|
12156
|
-
function claimText(claim) {
|
|
12157
|
-
return formatAgentClaimResults(claim.target, { results: claim.rows.map((row) => row.raw) });
|
|
12521
|
+
function claimText(claim, style) {
|
|
12522
|
+
return formatAgentClaimResults(claim.target, { results: claim.rows.map((row) => row.raw) }, style);
|
|
12158
12523
|
}
|
|
12159
|
-
async function claimTasks(client, request) {
|
|
12524
|
+
async function claimTasks(client, request, options = {}) {
|
|
12525
|
+
const style = options.hints ?? "cli";
|
|
12160
12526
|
const numbers = (request.taskNumbers ?? []).filter((n) => Number.isInteger(n) && n > 0);
|
|
12161
12527
|
const ids = (request.messageIds ?? []).map((id) => id.trim()).filter(Boolean);
|
|
12162
12528
|
const invalid = validateOpRequest(claimTasksRequestSchema, request);
|
|
@@ -12171,17 +12537,12 @@ async function claimTasks(client, request) {
|
|
|
12171
12537
|
if (!result.ok) return failureFromClientResult(result);
|
|
12172
12538
|
const data = result.data;
|
|
12173
12539
|
if (isHeldResponse(data)) {
|
|
12174
|
-
const interrupt = heldTaskInterrupt(request.target, data, "Your task claim was not applied.", "After reviewing, rerun the claim if it is still correct.", taskClaimArgv(request.target, numbers, ids));
|
|
12540
|
+
const interrupt = heldTaskInterrupt(request.target, data, "Your task claim was not applied.", "After reviewing, rerun the claim if it is still correct.", taskClaimArgv(request.target, numbers, ids), style);
|
|
12175
12541
|
return {
|
|
12176
12542
|
ok: true,
|
|
12177
12543
|
state: "interrupted",
|
|
12178
12544
|
interrupt,
|
|
12179
|
-
next: {
|
|
12180
|
-
kind: "retry_claim",
|
|
12181
|
-
command: `raft message read --target "${request.target}"`,
|
|
12182
|
-
args: { target: request.target },
|
|
12183
|
-
why: "Unread messages in this channel may change the task; read them (frontier.recordHeld(interrupt) once the model saw them), then resume the claim (interrupt.resume) if it is still right."
|
|
12184
|
-
},
|
|
12545
|
+
next: hintStep("retry_claim", RAFT_HINTS.messageRead({ target: request.target }), "Unread messages in this channel may change the task; read them (frontier.recordHeld(interrupt) once the model saw them), then resume the claim (interrupt.resume) if it is still right.", style, { target: request.target }),
|
|
12185
12546
|
text: interrupt.context
|
|
12186
12547
|
};
|
|
12187
12548
|
}
|
|
@@ -12208,8 +12569,8 @@ async function claimTasks(client, request) {
|
|
|
12208
12569
|
ok: true,
|
|
12209
12570
|
state: !claim.anyAuthorised ? "refused" : rows.every((row) => row.mayWork) ? "claimed" : "partial",
|
|
12210
12571
|
data: claim,
|
|
12211
|
-
next: claimNext(claim),
|
|
12212
|
-
text: claimText(claim)
|
|
12572
|
+
next: claimNext(claim, style),
|
|
12573
|
+
text: claimText(claim, style)
|
|
12213
12574
|
};
|
|
12214
12575
|
}
|
|
12215
12576
|
const listTasksRequestSchema = requestSchema()(object({
|
|
@@ -12224,7 +12585,7 @@ const listTasksRequestSchema = requestSchema()(object({
|
|
|
12224
12585
|
"all"
|
|
12225
12586
|
]).optional().describe("Filter by status; all includes done and closed.")
|
|
12226
12587
|
}));
|
|
12227
|
-
async function listTasks(client, request) {
|
|
12588
|
+
async function listTasks(client, request, options = {}) {
|
|
12228
12589
|
const invalid = validateOpRequest(listTasksRequestSchema, request);
|
|
12229
12590
|
if (invalid) return invalid;
|
|
12230
12591
|
const mine = request.mine === true;
|
|
@@ -12243,15 +12604,13 @@ async function listTasks(client, request) {
|
|
|
12243
12604
|
};
|
|
12244
12605
|
const text = mine ? formatAgentMyTaskList(data, request.status) : formatAgentTaskList(request.target, data, request.status);
|
|
12245
12606
|
const open = board.tasks.find((t) => t.status === "todo" && !t.claimedByName);
|
|
12246
|
-
const next = open?.taskNumber && board.target ? {
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
why: "An unassigned todo task is open; claim it before working on it."
|
|
12254
|
-
} : null;
|
|
12607
|
+
const next = open?.taskNumber && board.target ? hintStep("claim_task", RAFT_HINTS.taskClaim({
|
|
12608
|
+
target: board.target,
|
|
12609
|
+
taskNumber: open.taskNumber
|
|
12610
|
+
}), "An unassigned todo task is open; claim it before working on it.", options.hints, {
|
|
12611
|
+
target: board.target,
|
|
12612
|
+
taskNumbers: [open.taskNumber]
|
|
12613
|
+
}) : null;
|
|
12255
12614
|
return {
|
|
12256
12615
|
ok: true,
|
|
12257
12616
|
state: board.tasks.length > 0 ? "board" : "empty",
|
|
@@ -12266,21 +12625,24 @@ const createTasksRequestSchema = requestSchema()(object({
|
|
|
12266
12625
|
title: string().describe("Task title."),
|
|
12267
12626
|
createsResource: boolean().optional().describe("The task produces a resource (for example a document) that needs a receipt.")
|
|
12268
12627
|
})).describe("One entry per task to create."),
|
|
12269
|
-
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo.")
|
|
12628
|
+
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo."),
|
|
12629
|
+
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.")
|
|
12270
12630
|
}));
|
|
12271
|
-
async function createTasks(client, request) {
|
|
12631
|
+
async function createTasks(client, request, options = {}) {
|
|
12272
12632
|
const invalid = validateOpRequest(createTasksRequestSchema, request);
|
|
12273
12633
|
if (invalid) return invalid;
|
|
12274
12634
|
if (!request.target?.trim() || !request.tasks?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and at least one task title are required." }));
|
|
12635
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
12275
12636
|
const result = await client.tasks.create({
|
|
12276
12637
|
channel: request.target,
|
|
12277
12638
|
tasks: request.tasks.map((t) => ({
|
|
12278
12639
|
title: t.title,
|
|
12279
12640
|
...t.createsResource ? { creates_resource: true } : {}
|
|
12280
12641
|
})),
|
|
12281
|
-
...request.assignee ? { assignee: request.assignee } : {}
|
|
12642
|
+
...request.assignee ? { assignee: request.assignee } : {},
|
|
12643
|
+
idempotencyKey
|
|
12282
12644
|
});
|
|
12283
|
-
if (!result.ok) return failureFromClientResult(result);
|
|
12645
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
12284
12646
|
const data = result.data;
|
|
12285
12647
|
const first = data.tasks[0];
|
|
12286
12648
|
return {
|
|
@@ -12288,17 +12650,18 @@ async function createTasks(client, request) {
|
|
|
12288
12650
|
state: "created",
|
|
12289
12651
|
data: {
|
|
12290
12652
|
...data,
|
|
12291
|
-
target: request.target
|
|
12653
|
+
target: request.target,
|
|
12654
|
+
idempotencyKey
|
|
12292
12655
|
},
|
|
12293
|
-
next: first ?
|
|
12294
|
-
|
|
12295
|
-
command: `raft message send --target "${agentTaskThreadTarget(request.target, first.messageId)}"`,
|
|
12296
|
-
args: { thread: agentTaskThreadTarget(request.target, first.messageId) },
|
|
12297
|
-
why: "Follow up in each task's thread."
|
|
12298
|
-
} : null,
|
|
12299
|
-
text: formatAgentTasksCreated(request.target, data)
|
|
12656
|
+
next: first ? taskThreadStep(request.target, first.messageId, "Follow up in each task's thread.", options.hints) : null,
|
|
12657
|
+
text: formatAgentTasksCreated(request.target, data, options.hints)
|
|
12300
12658
|
};
|
|
12301
12659
|
}
|
|
12660
|
+
/** Post in a task's thread (a send: `content` is the caller's). */
|
|
12661
|
+
function taskThreadStep(target, messageId, why, style = "cli") {
|
|
12662
|
+
const thread = agentTaskThreadTarget(target, messageId);
|
|
12663
|
+
return hintStep("post_in_task_thread", RAFT_HINTS.messageSend({ target: thread }), why, style, { target: thread });
|
|
12664
|
+
}
|
|
12302
12665
|
const taskRefFields = {
|
|
12303
12666
|
target: taskChannelSchema,
|
|
12304
12667
|
taskNumber: taskNumberSchema
|
|
@@ -12388,22 +12751,17 @@ const updateTaskStatusRequestSchema = requestSchema()(object({
|
|
|
12388
12751
|
status: raftTaskStatusSchema.describe("todo → in_progress → in_review → done; closed from anywhere.")
|
|
12389
12752
|
}));
|
|
12390
12753
|
/** A held task write: the interrupt whose resume is the identical command; nothing to cancel. */
|
|
12391
|
-
function heldTaskWrite(target, data, action, kind, argv) {
|
|
12392
|
-
const interrupt = heldTaskInterrupt(target, data, action, "After reviewing, repeat the operation if it is still correct.", argv);
|
|
12754
|
+
function heldTaskWrite(target, data, action, kind, argv, style) {
|
|
12755
|
+
const interrupt = heldTaskInterrupt(target, data, action, "After reviewing, repeat the operation if it is still correct.", argv, style);
|
|
12393
12756
|
return {
|
|
12394
12757
|
ok: true,
|
|
12395
12758
|
state: "interrupted",
|
|
12396
12759
|
interrupt,
|
|
12397
|
-
next: {
|
|
12398
|
-
kind,
|
|
12399
|
-
command: `raft message read --target "${target}"`,
|
|
12400
|
-
args: { target },
|
|
12401
|
-
why: "Unread messages in this channel may change the task; read them (frontier.recordHeld(interrupt) once the model saw them), then resume (interrupt.resume) if it is still right."
|
|
12402
|
-
},
|
|
12760
|
+
next: hintStep(kind, RAFT_HINTS.messageRead({ target }), "Unread messages in this channel may change the task; read them (frontier.recordHeld(interrupt) once the model saw them), then resume (interrupt.resume) if it is still right.", style, { target }),
|
|
12403
12761
|
text: interrupt.context
|
|
12404
12762
|
};
|
|
12405
12763
|
}
|
|
12406
|
-
async function updateTaskStatus(client, request) {
|
|
12764
|
+
async function updateTaskStatus(client, request, options = {}) {
|
|
12407
12765
|
const invalid = requireTaskRef(request, updateTaskStatusRequestSchema);
|
|
12408
12766
|
if (invalid) return invalid;
|
|
12409
12767
|
const result = await client.tasks.updateStatus({
|
|
@@ -12412,7 +12770,7 @@ async function updateTaskStatus(client, request) {
|
|
|
12412
12770
|
status: request.status
|
|
12413
12771
|
});
|
|
12414
12772
|
if (!result.ok) return failureFromClientResult(result);
|
|
12415
|
-
if (isHeldResponse(result.data)) return heldTaskWrite(request.target, result.data, "The status change was not applied.", "retry_update_status", taskUpdateArgv(request.target, request.taskNumber, request.status));
|
|
12773
|
+
if (isHeldResponse(result.data)) return heldTaskWrite(request.target, result.data, "The status change was not applied.", "retry_update_status", taskUpdateArgv(request.target, request.taskNumber, request.status), options.hints ?? "cli");
|
|
12416
12774
|
return {
|
|
12417
12775
|
ok: true,
|
|
12418
12776
|
state: "updated",
|
|
@@ -12441,7 +12799,7 @@ function taskAmendArgv(request) {
|
|
|
12441
12799
|
...request.description === void 0 ? [] : request.description === null ? ["--clear-description"] : ["--description", request.description]
|
|
12442
12800
|
];
|
|
12443
12801
|
}
|
|
12444
|
-
async function amendTask(client, request) {
|
|
12802
|
+
async function amendTask(client, request, options = {}) {
|
|
12445
12803
|
const invalid = requireTaskRef(request, amendTaskRequestSchema);
|
|
12446
12804
|
if (invalid) return invalid;
|
|
12447
12805
|
if (request.title === void 0 && request.description === void 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass a new title, a new description, or description: null to clear it." }));
|
|
@@ -12452,7 +12810,7 @@ async function amendTask(client, request) {
|
|
|
12452
12810
|
...request.description === void 0 ? {} : { description: request.description }
|
|
12453
12811
|
});
|
|
12454
12812
|
if (!result.ok) return failureFromClientResult(result);
|
|
12455
|
-
if (isHeldResponse(result.data)) return heldTaskWrite(request.target, result.data, "The amendment was not applied.", "retry_amend", taskAmendArgv(request));
|
|
12813
|
+
if (isHeldResponse(result.data)) return heldTaskWrite(request.target, result.data, "The amendment was not applied.", "retry_amend", taskAmendArgv(request), options.hints ?? "cli");
|
|
12456
12814
|
return {
|
|
12457
12815
|
ok: true,
|
|
12458
12816
|
state: "amended",
|
|
@@ -12489,7 +12847,7 @@ async function taskHistory(client, request) {
|
|
|
12489
12847
|
* and picks the task; a miss says whether the Server asserted the list is
|
|
12490
12848
|
* complete, exactly as the CLI does.
|
|
12491
12849
|
*/
|
|
12492
|
-
async function showTask(client, request) {
|
|
12850
|
+
async function showTask(client, request, options = {}) {
|
|
12493
12851
|
const invalid = requireTaskRef(request);
|
|
12494
12852
|
if (invalid) return invalid;
|
|
12495
12853
|
const { target, taskNumber } = request;
|
|
@@ -12502,7 +12860,7 @@ async function showTask(client, request) {
|
|
|
12502
12860
|
const task = tasks.find((candidate) => candidate.taskNumber === taskNumber);
|
|
12503
12861
|
if (!task) return failureOutcome(opError("NOT_FOUND", {
|
|
12504
12862
|
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")`,
|
|
12505
|
-
nextAction: `Check the number with
|
|
12863
|
+
nextAction: `Check the number with \`${formatHint(RAFT_HINTS.taskListAll(target), options.hints)}\`.`
|
|
12506
12864
|
}));
|
|
12507
12865
|
return {
|
|
12508
12866
|
ok: true,
|
|
@@ -12519,7 +12877,7 @@ const convertMessageToTaskRequestSchema = requestSchema()(object({
|
|
|
12519
12877
|
target: taskChannelSchema,
|
|
12520
12878
|
messageId: string().describe("Full or short id of a top-level message in that channel.")
|
|
12521
12879
|
}));
|
|
12522
|
-
async function convertMessageToTask(client, request) {
|
|
12880
|
+
async function convertMessageToTask(client, request, options = {}) {
|
|
12523
12881
|
const invalid = validateOpRequest(convertMessageToTaskRequestSchema, request);
|
|
12524
12882
|
if (invalid) return invalid;
|
|
12525
12883
|
if (!request.target?.trim() || !request.messageId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and a message id are required." }));
|
|
@@ -12536,13 +12894,8 @@ async function convertMessageToTask(client, request) {
|
|
|
12536
12894
|
target: request.target,
|
|
12537
12895
|
task
|
|
12538
12896
|
},
|
|
12539
|
-
next:
|
|
12540
|
-
|
|
12541
|
-
command: `raft message send --target "${agentTaskThreadTarget(request.target, task.messageId)}"`,
|
|
12542
|
-
args: { thread: agentTaskThreadTarget(request.target, task.messageId) },
|
|
12543
|
-
why: "Follow up in the task's thread; it is unassigned until someone claims it."
|
|
12544
|
-
},
|
|
12545
|
-
text: formatAgentTaskConverted(request.target, task)
|
|
12897
|
+
next: taskThreadStep(request.target, task.messageId, "Follow up in the task's thread; it is unassigned until someone claims it.", options.hints),
|
|
12898
|
+
text: formatAgentTaskConverted(request.target, task, options.hints)
|
|
12546
12899
|
};
|
|
12547
12900
|
}
|
|
12548
12901
|
async function deleteTask(client, request) {
|
|
@@ -12772,7 +13125,7 @@ function formatCurrentAgent(data) {
|
|
|
12772
13125
|
return `${lines.join("\n")}\n\n`;
|
|
12773
13126
|
}
|
|
12774
13127
|
/** Server overview: runtime context, channels, agents, humans. */
|
|
12775
|
-
function formatAgentServerInfo(data) {
|
|
13128
|
+
function formatAgentServerInfo(data, style = "cli") {
|
|
12776
13129
|
let text = "## Server\n\n";
|
|
12777
13130
|
const channels = data.channels ?? [];
|
|
12778
13131
|
const agents = data.agents ?? [];
|
|
@@ -12780,8 +13133,14 @@ function formatAgentServerInfo(data) {
|
|
|
12780
13133
|
text += formatRuntimeContext(data.runtimeContext);
|
|
12781
13134
|
text += formatCurrentAgent(data);
|
|
12782
13135
|
text += "### Channels\n";
|
|
12783
|
-
text += "Visible public channels may appear even when `joined=false`. Private channels are shown only when you are a member; do not disclose private-channel names, membership, or content outside that channel.
|
|
12784
|
-
text += "
|
|
13136
|
+
text += "Visible public channels may appear even when `joined=false`. Private channels are shown only when you are a member; do not disclose private-channel names, membership, or content outside that channel. ";
|
|
13137
|
+
text += style === "cli" ? `Use channel attention commands (\`${formatHintName(RAFT_HINTS.channelJoinName())}\`, \`leave\`, \`mute\`, \`unmute\`; \`${formatHintName(RAFT_HINTS.threadUnfollowName())}\`) for your own delivery state. Existing channel management commands (\`${formatHintName(RAFT_HINTS.channelCreateName())}\`, \`update\`, \`archive\`, \`unarchive\`, \`add-member\`, \`remove-member\`) are authorized per channel; a channel-admin role never grants delete, visibility, federation, or server-profile actions. There is no Agent command for changing channel roles. Run any subcommand with \`--help\` for syntax.\n` : `Use the channel attention tools (${[
|
|
13138
|
+
RAFT_HINTS.channelJoinName(),
|
|
13139
|
+
RAFT_HINTS.channelLeaveName(),
|
|
13140
|
+
RAFT_HINTS.channelMuteName(),
|
|
13141
|
+
RAFT_HINTS.channelUnmuteName()
|
|
13142
|
+
].map((hint) => `\`${formatHintName(hint, style)}\``).join(", ")}; \`${formatHintName(RAFT_HINTS.threadUnfollowName(), style)}\`) for your own delivery state. Channel management (create, update, archive, unarchive, add-member, remove-member) has no tool: ${RAFT_NO_TOOL_WORDING}; a channel-admin role never grants delete, visibility, federation, or server-profile actions. There is no tool for changing channel roles.\n`;
|
|
13143
|
+
text += style === "cli" ? `Server-profile changes still use ${formatHintName(RAFT_HINTS.serverUpdateName())} and remain server-role gated.\n` : `Server-profile changes have no tool and remain server-role gated: ${RAFT_NO_TOOL_WORDING}.\n`;
|
|
12785
13144
|
text += "Mute state is shown when the server provides it; otherwise it is omitted.\n";
|
|
12786
13145
|
if (channels.length > 0) for (const t of channels) {
|
|
12787
13146
|
const statusParts = [channelVisibility(t), t.joined ? "joined" : "not joined"];
|
|
@@ -12804,7 +13163,8 @@ function formatAgentServerInfo(data) {
|
|
|
12804
13163
|
}
|
|
12805
13164
|
else text += " (none)\n";
|
|
12806
13165
|
text += "\n### Humans\n";
|
|
12807
|
-
|
|
13166
|
+
const newDm = formatHint(RAFT_HINTS.messageSend({ target: "dm:@name" }), style);
|
|
13167
|
+
text += style === "cli" ? `To start a new DM: ${newDm} <<'RAFTMSG' followed by the message body and RAFTMSG. To reply in an existing DM: reuse the target from received messages.\n` : `To start a new DM: ${newDm}. To reply in an existing DM: reuse the target from received messages.\n`;
|
|
12808
13168
|
text += "Role labels show server-level owner/admin authority; no role label means ordinary member.\n";
|
|
12809
13169
|
if (humans.length > 0) for (const u of humans) {
|
|
12810
13170
|
const role = roleLabel(u.role);
|
|
@@ -12844,7 +13204,7 @@ function formatPageFooter(page) {
|
|
|
12844
13204
|
return `${lines.join("\n")}\n`;
|
|
12845
13205
|
}
|
|
12846
13206
|
/** Single channel detail block. */
|
|
12847
|
-
function formatAgentChannelInfo(channel, memberCounts) {
|
|
13207
|
+
function formatAgentChannelInfo(channel, memberCounts, style = "cli") {
|
|
12848
13208
|
const lines = ["## Channel", ""];
|
|
12849
13209
|
lines.push(`Channel: ${agentChannelRef(channel.name, channel.type)}`);
|
|
12850
13210
|
if (channel.id) lines.push(`ID: ${channel.id}`);
|
|
@@ -12864,11 +13224,11 @@ function formatAgentChannelInfo(channel, memberCounts) {
|
|
|
12864
13224
|
lines.push(`Members: ${agents + humans} (${agents} agents, ${humans} humans)`);
|
|
12865
13225
|
}
|
|
12866
13226
|
lines.push("");
|
|
12867
|
-
lines.push(`More:
|
|
13227
|
+
lines.push(`More: ${formatHint(RAFT_HINTS.channelMembers(agentChannelRef(channel.name, channel.type)), style)}`);
|
|
12868
13228
|
return `${lines.join("\n")}\n`;
|
|
12869
13229
|
}
|
|
12870
13230
|
/** Compact server summary. */
|
|
12871
|
-
function formatAgentServerSummary(data) {
|
|
13231
|
+
function formatAgentServerSummary(data, style = "cli") {
|
|
12872
13232
|
const channels = data.channels ?? [];
|
|
12873
13233
|
const agents = data.agents ?? [];
|
|
12874
13234
|
const humans = data.humans ?? [];
|
|
@@ -12881,13 +13241,15 @@ function formatAgentServerSummary(data) {
|
|
|
12881
13241
|
`Humans: ${humans.length}`,
|
|
12882
13242
|
"",
|
|
12883
13243
|
"Narrow queries:",
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
13244
|
+
...[
|
|
13245
|
+
RAFT_HINTS.serverInfo({ view: "channels" }),
|
|
13246
|
+
RAFT_HINTS.serverInfo({ view: "agents" }),
|
|
13247
|
+
RAFT_HINTS.serverInfo({ view: "humans" }),
|
|
13248
|
+
RAFT_HINTS.channelInfo(),
|
|
13249
|
+
RAFT_HINTS.userInfo()
|
|
13250
|
+
].map((hint) => `- ${formatHint(hint, style)}`),
|
|
12889
13251
|
"",
|
|
12890
|
-
|
|
13252
|
+
`Full dump: ${formatHint(RAFT_HINTS.serverInfo({ view: "full" }), style)}`
|
|
12891
13253
|
].join("\n")}\n`;
|
|
12892
13254
|
}
|
|
12893
13255
|
/** Channel listing page. */
|
|
@@ -13168,7 +13530,7 @@ const unfollowThreadRequestSchema = requestSchema()(object({
|
|
|
13168
13530
|
reason: string().optional().describe("Short reason, kept with the unfollow.")
|
|
13169
13531
|
}));
|
|
13170
13532
|
const INVALID_CHANNEL_TARGET = "Target must be a regular channel in the form '#channel-name'. DMs and thread targets are not supported.";
|
|
13171
|
-
async function resolveRegularChannel(client, target) {
|
|
13533
|
+
async function resolveRegularChannel(client, target, options) {
|
|
13172
13534
|
const name = parseRaftRegularChannelTarget$1(target ?? "");
|
|
13173
13535
|
if (!name) return failureOutcome(opError("INVALID_REQUEST", { message: INVALID_CHANNEL_TARGET }));
|
|
13174
13536
|
const info = await client.server.info();
|
|
@@ -13176,7 +13538,7 @@ async function resolveRegularChannel(client, target) {
|
|
|
13176
13538
|
const channel = info.data.channels.find((candidate) => candidate.name === name);
|
|
13177
13539
|
if (!channel) return failureOutcome(opError("NOT_FOUND", {
|
|
13178
13540
|
message: `Channel not found: ${target}`,
|
|
13179
|
-
nextAction:
|
|
13541
|
+
nextAction: `List visible channels with \`${formatHint(RAFT_HINTS.serverInfo({ view: "channels" }), options.hints)}\`; private channels need a human to add you.`
|
|
13180
13542
|
}));
|
|
13181
13543
|
return {
|
|
13182
13544
|
id: channel.id,
|
|
@@ -13184,7 +13546,7 @@ async function resolveRegularChannel(client, target) {
|
|
|
13184
13546
|
joined: channel.joined
|
|
13185
13547
|
};
|
|
13186
13548
|
}
|
|
13187
|
-
async function joinChannel(client, request) {
|
|
13549
|
+
async function joinChannel(client, request, options = {}) {
|
|
13188
13550
|
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13189
13551
|
if (invalid) return invalid;
|
|
13190
13552
|
const result = await joinRaftChannelByTarget$1(client, { target: request.target });
|
|
@@ -13210,19 +13572,14 @@ async function joinChannel(client, request) {
|
|
|
13210
13572
|
target: result.data.target,
|
|
13211
13573
|
channelId: result.data.channelId
|
|
13212
13574
|
},
|
|
13213
|
-
next: {
|
|
13214
|
-
kind: "read_target",
|
|
13215
|
-
command: `raft message read --target "${result.data.target}"`,
|
|
13216
|
-
args: { target: result.data.target },
|
|
13217
|
-
why: "Read the channel before posting."
|
|
13218
|
-
},
|
|
13575
|
+
next: hintStep("read_target", RAFT_HINTS.messageRead({ target: result.data.target }), "Read the channel before posting.", options.hints, { target: result.data.target }),
|
|
13219
13576
|
text: state === "joined" ? `Joined ${result.data.target}.` : `Already a member of ${result.data.target}.`
|
|
13220
13577
|
};
|
|
13221
13578
|
}
|
|
13222
|
-
async function leaveChannel(client, request) {
|
|
13579
|
+
async function leaveChannel(client, request, options = {}) {
|
|
13223
13580
|
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13224
13581
|
if (invalid) return invalid;
|
|
13225
|
-
const channel = await resolveRegularChannel(client, request.target);
|
|
13582
|
+
const channel = await resolveRegularChannel(client, request.target, options);
|
|
13226
13583
|
if ("ok" in channel) return channel;
|
|
13227
13584
|
if (!channel.joined) return {
|
|
13228
13585
|
ok: true,
|
|
@@ -13253,10 +13610,10 @@ async function leaveChannel(client, request) {
|
|
|
13253
13610
|
function formatSeq(value) {
|
|
13254
13611
|
return value == null ? "none" : String(value);
|
|
13255
13612
|
}
|
|
13256
|
-
async function setChannelMute(client, request, action) {
|
|
13613
|
+
async function setChannelMute(client, request, action, options) {
|
|
13257
13614
|
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13258
13615
|
if (invalid) return invalid;
|
|
13259
|
-
const channel = await resolveRegularChannel(client, request.target);
|
|
13616
|
+
const channel = await resolveRegularChannel(client, request.target, options);
|
|
13260
13617
|
if ("ok" in channel) return channel;
|
|
13261
13618
|
const result = action === "mute" ? await client.channels.mute({ channelId: channel.id }, {}) : await client.channels.unmute({ channelId: channel.id });
|
|
13262
13619
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -13291,11 +13648,11 @@ async function setChannelMute(client, request, action) {
|
|
|
13291
13648
|
text: lines.join("\n")
|
|
13292
13649
|
};
|
|
13293
13650
|
}
|
|
13294
|
-
function muteChannel(client, request) {
|
|
13295
|
-
return setChannelMute(client, request, "mute");
|
|
13651
|
+
function muteChannel(client, request, options = {}) {
|
|
13652
|
+
return setChannelMute(client, request, "mute", options);
|
|
13296
13653
|
}
|
|
13297
|
-
function unmuteChannel(client, request) {
|
|
13298
|
-
return setChannelMute(client, request, "unmute");
|
|
13654
|
+
function unmuteChannel(client, request, options = {}) {
|
|
13655
|
+
return setChannelMute(client, request, "unmute", options);
|
|
13299
13656
|
}
|
|
13300
13657
|
async function channelMembers(client, request) {
|
|
13301
13658
|
const invalid = validateOpRequest(channelMembersRequestSchema, request);
|
|
@@ -13345,7 +13702,7 @@ const channelInfoRequestSchema = requestSchema()(object({ target: string().descr
|
|
|
13345
13702
|
* `raft channel info <target>`: the channel's facts from `server.info`, plus
|
|
13346
13703
|
* member counts from its roster when the Server shows it.
|
|
13347
13704
|
*/
|
|
13348
|
-
async function channelInfo(client, request) {
|
|
13705
|
+
async function channelInfo(client, request, options = {}) {
|
|
13349
13706
|
const invalid = validateOpRequest(channelInfoRequestSchema, request);
|
|
13350
13707
|
if (invalid) return invalid;
|
|
13351
13708
|
const trimmed = request.target.trim();
|
|
@@ -13357,7 +13714,10 @@ async function channelInfo(client, request) {
|
|
|
13357
13714
|
const channel = info.data.channels.find((candidate) => candidate.name === name);
|
|
13358
13715
|
if (!channel) return failureOutcome(opError("NOT_FOUND", {
|
|
13359
13716
|
message: `Channel not found or not visible: ${input}`,
|
|
13360
|
-
nextAction:
|
|
13717
|
+
nextAction: `Run \`${formatHint(RAFT_HINTS.serverInfo({
|
|
13718
|
+
view: "channels",
|
|
13719
|
+
query: true
|
|
13720
|
+
}), options.hints)}\` to inspect visible channels, or ask a channel member to add you if this is private.`
|
|
13361
13721
|
}));
|
|
13362
13722
|
const members = await client.channels.members({ channel: `#${name}` });
|
|
13363
13723
|
const memberCounts = members.ok ? {
|
|
@@ -13372,7 +13732,7 @@ async function channelInfo(client, request) {
|
|
|
13372
13732
|
memberCounts
|
|
13373
13733
|
},
|
|
13374
13734
|
next: null,
|
|
13375
|
-
text: formatAgentChannelInfo(channel, memberCounts)
|
|
13735
|
+
text: formatAgentChannelInfo(channel, memberCounts, options.hints)
|
|
13376
13736
|
};
|
|
13377
13737
|
}
|
|
13378
13738
|
//#endregion
|
|
@@ -13387,45 +13747,54 @@ const serverInfoRequestSchema = requestSchema()(object({
|
|
|
13387
13747
|
]).optional().describe("summary (default): counts; full: the whole overview; channels / agents / humans: one paged section."),
|
|
13388
13748
|
offset: number$1().optional().describe("Section paging: rows to skip."),
|
|
13389
13749
|
limit: number$1().optional().describe("Section paging: rows per page (default 50)."),
|
|
13390
|
-
joined: boolean().optional().describe("channels view only: only channels you have joined.")
|
|
13750
|
+
joined: boolean().optional().describe("channels view only: only channels you have joined."),
|
|
13751
|
+
query: string().optional().describe("channels / agents / humans view only: keep rows whose name, description, or other visible text contains this (case-insensitive).")
|
|
13391
13752
|
}));
|
|
13392
13753
|
const showProfileRequestSchema = requestSchema()(object({ target: string().optional().describe("`@handle` of someone else; omit for your own profile.") }));
|
|
13393
13754
|
/** The Agent API's profile body schema; at least one field is required (checked by the operation). */
|
|
13394
13755
|
const updateProfileRequestSchema = agentApiProfileUpdateBodySchema;
|
|
13395
|
-
|
|
13756
|
+
/** The next page of a section, or null on the last page (`--query` / `--joined` carried, in the CLI's order). */
|
|
13757
|
+
function nextPageArgs(section, request, offset, limit, total) {
|
|
13396
13758
|
const nextOffset = offset + limit;
|
|
13397
|
-
if (nextOffset >= total) return
|
|
13398
|
-
const
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13759
|
+
if (nextOffset >= total) return null;
|
|
13760
|
+
const query = request.query?.trim() || void 0;
|
|
13761
|
+
const joined = section === "channels" && request.joined === true ? true : void 0;
|
|
13762
|
+
return {
|
|
13763
|
+
view: section,
|
|
13764
|
+
offset: nextOffset,
|
|
13765
|
+
limit,
|
|
13766
|
+
...query === void 0 ? {} : { query },
|
|
13767
|
+
...joined ? { joined } : {}
|
|
13768
|
+
};
|
|
13406
13769
|
}
|
|
13407
|
-
|
|
13770
|
+
/** The CLI's `--query` match: any string field contains the needle, case-insensitively. */
|
|
13771
|
+
function includesQuery(row, query) {
|
|
13772
|
+
const needle = query?.trim().toLowerCase();
|
|
13773
|
+
if (!needle) return true;
|
|
13774
|
+
return Object.values(row).some((value) => typeof value === "string" && value.toLowerCase().includes(needle));
|
|
13775
|
+
}
|
|
13776
|
+
async function serverInfo(client, request = {}, options = {}) {
|
|
13408
13777
|
const invalid = validateOpRequest(serverInfoRequestSchema, request);
|
|
13409
13778
|
if (invalid) return invalid;
|
|
13779
|
+
const style = options.hints ?? "cli";
|
|
13410
13780
|
const result = await client.server.info();
|
|
13411
13781
|
if (!result.ok) return failureFromClientResult(result);
|
|
13412
13782
|
const server = result.data;
|
|
13413
13783
|
const view = request.view ?? "summary";
|
|
13414
|
-
if (view === "summary")
|
|
13415
|
-
|
|
13416
|
-
|
|
13417
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
}
|
|
13427
|
-
|
|
13428
|
-
};
|
|
13784
|
+
if (view === "summary") {
|
|
13785
|
+
const next = hintStep("list_channels", RAFT_HINTS.serverInfo({ view: "channels" }), "The summary only counts; list a section to see names.", style);
|
|
13786
|
+
return {
|
|
13787
|
+
ok: true,
|
|
13788
|
+
state: "info",
|
|
13789
|
+
data: {
|
|
13790
|
+
view,
|
|
13791
|
+
server,
|
|
13792
|
+
page: null
|
|
13793
|
+
},
|
|
13794
|
+
next,
|
|
13795
|
+
text: formatAgentServerSummary(server, style)
|
|
13796
|
+
};
|
|
13797
|
+
}
|
|
13429
13798
|
if (view === "full") return {
|
|
13430
13799
|
ok: true,
|
|
13431
13800
|
state: "info",
|
|
@@ -13435,30 +13804,23 @@ async function serverInfo(client, request = {}) {
|
|
|
13435
13804
|
page: null
|
|
13436
13805
|
},
|
|
13437
13806
|
next: null,
|
|
13438
|
-
text: formatAgentServerInfo(server)
|
|
13807
|
+
text: formatAgentServerInfo(server, style)
|
|
13439
13808
|
};
|
|
13440
13809
|
const limit = Math.max(1, Math.trunc(request.limit ?? 50));
|
|
13441
13810
|
const offset = Math.max(0, Math.trunc(request.offset ?? 0));
|
|
13442
|
-
const rows = view === "channels" ? server.channels.filter((c) => !request.joined || c.joined) : view === "agents" ? server.agents : server.humans;
|
|
13811
|
+
const rows = (view === "channels" ? server.channels.filter((c) => !request.joined || c.joined) : view === "agents" ? server.agents : server.humans).filter((row) => includesQuery(row, request.query));
|
|
13443
13812
|
const pageRows = rows.slice(offset, offset + limit);
|
|
13813
|
+
const nextArgs = nextPageArgs(view, request, offset, limit, rows.length);
|
|
13814
|
+
const nextHint = nextArgs ? RAFT_HINTS.serverInfo(nextArgs) : null;
|
|
13444
13815
|
const page = {
|
|
13445
13816
|
section: view,
|
|
13446
13817
|
total: rows.length,
|
|
13447
13818
|
offset,
|
|
13448
13819
|
limit,
|
|
13449
|
-
nextCommand:
|
|
13820
|
+
nextCommand: nextHint ? formatHint(nextHint, style) : void 0
|
|
13450
13821
|
};
|
|
13451
13822
|
const text = view === "channels" ? formatAgentServerChannels(pageRows, page) : view === "agents" ? formatAgentServerAgents(pageRows, page) : formatAgentServerHumans(pageRows, page);
|
|
13452
|
-
const next = page.
|
|
13453
|
-
kind: "next_page",
|
|
13454
|
-
command: page.nextCommand,
|
|
13455
|
-
args: {
|
|
13456
|
-
view,
|
|
13457
|
-
offset: offset + limit,
|
|
13458
|
-
limit
|
|
13459
|
-
},
|
|
13460
|
-
why: "More rows exist; one page is one page."
|
|
13461
|
-
} : null;
|
|
13823
|
+
const next = nextArgs && nextHint ? hintStep("next_page", nextHint, "More rows exist; one page is one page.", style, nextArgs) : null;
|
|
13462
13824
|
return {
|
|
13463
13825
|
ok: true,
|
|
13464
13826
|
state: "info",
|
|
@@ -13508,7 +13870,7 @@ const userInfoRequestSchema = requestSchema()(object({
|
|
|
13508
13870
|
* their memberships among one page of the visible channels, checked one
|
|
13509
13871
|
* channel roster at a time (a rejected roster is skipped and counted).
|
|
13510
13872
|
*/
|
|
13511
|
-
async function userInfo(client, request) {
|
|
13873
|
+
async function userInfo(client, request, options = {}) {
|
|
13512
13874
|
const invalid = validateOpRequest(userInfoRequestSchema, request);
|
|
13513
13875
|
if (invalid) return invalid;
|
|
13514
13876
|
const trimmed = request.name.trim();
|
|
@@ -13529,7 +13891,13 @@ async function userInfo(client, request) {
|
|
|
13529
13891
|
} : null;
|
|
13530
13892
|
if (!user) return failureOutcome(opError("NOT_FOUND", {
|
|
13531
13893
|
message: `User not found or not visible: @${name}`,
|
|
13532
|
-
nextAction:
|
|
13894
|
+
nextAction: `Run \`${formatHint(RAFT_HINTS.serverInfo({
|
|
13895
|
+
view: "agents",
|
|
13896
|
+
query: true
|
|
13897
|
+
}), options.hints)}\` or \`${formatHint(RAFT_HINTS.serverInfo({
|
|
13898
|
+
view: "humans",
|
|
13899
|
+
query: true
|
|
13900
|
+
}), options.hints)}\` to inspect visible users.`
|
|
13533
13901
|
}));
|
|
13534
13902
|
const visibleChannels = info.data.channels;
|
|
13535
13903
|
const memberships = [];
|
|
@@ -13548,22 +13916,22 @@ async function userInfo(client, request) {
|
|
|
13548
13916
|
});
|
|
13549
13917
|
}
|
|
13550
13918
|
const nextOffset = offset + limit;
|
|
13919
|
+
const nextHint = nextOffset < visibleChannels.length ? RAFT_HINTS.userInfo({
|
|
13920
|
+
name,
|
|
13921
|
+
offset: nextOffset,
|
|
13922
|
+
limit
|
|
13923
|
+
}) : null;
|
|
13551
13924
|
const page = {
|
|
13552
13925
|
total: visibleChannels.length,
|
|
13553
13926
|
offset,
|
|
13554
13927
|
limit,
|
|
13555
|
-
nextCommand:
|
|
13928
|
+
nextCommand: nextHint ? formatHint(nextHint, options.hints) : void 0
|
|
13556
13929
|
};
|
|
13557
|
-
const next = page.
|
|
13558
|
-
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
offset: nextOffset,
|
|
13563
|
-
limit
|
|
13564
|
-
},
|
|
13565
|
-
why: "Only one page of visible channels was inspected for memberships."
|
|
13566
|
-
} : null;
|
|
13930
|
+
const next = nextHint ? hintStep("next_page", nextHint, "Only one page of visible channels was inspected for memberships.", options.hints, {
|
|
13931
|
+
name: `@${name}`,
|
|
13932
|
+
offset: nextOffset,
|
|
13933
|
+
limit
|
|
13934
|
+
}) : null;
|
|
13567
13935
|
return {
|
|
13568
13936
|
ok: true,
|
|
13569
13937
|
state: "info",
|
|
@@ -13580,8 +13948,9 @@ async function userInfo(client, request) {
|
|
|
13580
13948
|
//#endregion
|
|
13581
13949
|
//#region ../shared/src/agentText/attachments.ts
|
|
13582
13950
|
/** Upload receipt with attachment id and send-usage hint. */
|
|
13583
|
-
function formatAgentAttachmentUploaded(attachment) {
|
|
13584
|
-
|
|
13951
|
+
function formatAgentAttachmentUploaded(attachment, style = "cli") {
|
|
13952
|
+
const send = formatHint(RAFT_HINTS.messageSend({ attachmentId: attachment.id }), style);
|
|
13953
|
+
return `File uploaded: ${attachment.filename} (${(attachment.sizeBytes / 1024).toFixed(1)}KB)\nAttachment ID: ${attachment.id}\n\nUse this ID with ${send} to include it in a message.\n`;
|
|
13585
13954
|
}
|
|
13586
13955
|
function anchorSummary(anchor) {
|
|
13587
13956
|
if (anchor.type === "md-section") {
|
|
@@ -13623,6 +13992,7 @@ const attachmentCommentsRequestSchema = requestSchema()(object({
|
|
|
13623
13992
|
attachmentId: string().describe("The attachment id."),
|
|
13624
13993
|
limit: number$1().int().positive().optional().describe("Maximum comments to return.")
|
|
13625
13994
|
}));
|
|
13995
|
+
const downloadAttachmentUrlRequestSchema = requestSchema()(object({ attachmentId: string().describe("The attachment id, as message lines show it (`id:…`).") }));
|
|
13626
13996
|
/** Compatibility fallback for Servers that predate the capability endpoint (the CLI's constant). */
|
|
13627
13997
|
const AGENT_ATTACHMENT_UPLOAD_FALLBACK_MAX_BYTES = 52428800;
|
|
13628
13998
|
const FILENAME_MIME_MAP = {
|
|
@@ -13675,7 +14045,7 @@ function inferAttachmentMimeType(filename, bytes, explicit) {
|
|
|
13675
14045
|
* `POST /upload` below the Server's direct-upload threshold, an upload session
|
|
13676
14046
|
* (create → PUT to a presigned URL → complete) at or above it.
|
|
13677
14047
|
*/
|
|
13678
|
-
async function uploadAttachment(client, transport, request) {
|
|
14048
|
+
async function uploadAttachment(client, transport, request, options = {}) {
|
|
13679
14049
|
if (!request.target?.trim() || !request.filename?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target and a filename are required to upload." }));
|
|
13680
14050
|
if (!(request.bytes instanceof Uint8Array) || request.bytes.byteLength === 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Refusing to upload a 0-byte attachment." }));
|
|
13681
14051
|
if (request.mimeType !== void 0 && !MIME_TYPE_RE.test(request.mimeType.trim())) return failureOutcome(opError("INVALID_REQUEST", { message: `mimeType must look like type/subtype, got: ${request.mimeType}` }));
|
|
@@ -13687,7 +14057,7 @@ async function uploadAttachment(client, transport, request) {
|
|
|
13687
14057
|
if (!resolved.ok) return failureFromClientResult(resolved);
|
|
13688
14058
|
const channelId = resolved.data.channelId;
|
|
13689
14059
|
const mimeType = inferAttachmentMimeType(request.filename, request.bytes, request.mimeType);
|
|
13690
|
-
if (threshold !== null && request.bytes.byteLength >= threshold) return uploadThroughSession(client, transport, request, channelId, mimeType);
|
|
14060
|
+
if (threshold !== null && request.bytes.byteLength >= threshold) return uploadThroughSession(client, transport, request, channelId, mimeType, options);
|
|
13691
14061
|
const copy = new Uint8Array(request.bytes.byteLength);
|
|
13692
14062
|
copy.set(request.bytes);
|
|
13693
14063
|
const form = new FormData();
|
|
@@ -13727,9 +14097,9 @@ async function uploadAttachment(client, transport, request) {
|
|
|
13727
14097
|
}
|
|
13728
14098
|
const parsed = agentApiContract.attachmentUpload.response.body.safeParse(body);
|
|
13729
14099
|
if (!parsed.success) return failureOutcome(opError("INVALID_RESPONSE"));
|
|
13730
|
-
return uploadedOutcome(request.target, parsed.data, mimeType);
|
|
14100
|
+
return uploadedOutcome(request.target, parsed.data, mimeType, options);
|
|
13731
14101
|
}
|
|
13732
|
-
function uploadedOutcome(target, data, mimeType) {
|
|
14102
|
+
function uploadedOutcome(target, data, mimeType, options) {
|
|
13733
14103
|
return {
|
|
13734
14104
|
ok: true,
|
|
13735
14105
|
state: "uploaded",
|
|
@@ -13738,16 +14108,14 @@ function uploadedOutcome(target, data, mimeType) {
|
|
|
13738
14108
|
target,
|
|
13739
14109
|
mimeType: data.mimeType ?? mimeType
|
|
13740
14110
|
},
|
|
13741
|
-
next: {
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
},
|
|
13750
|
-
text: formatAgentAttachmentUploaded(data)
|
|
14111
|
+
next: hintStep("send_with_attachment", RAFT_HINTS.messageSend({
|
|
14112
|
+
target,
|
|
14113
|
+
attachmentId: data.id
|
|
14114
|
+
}), "The upload alone posts nothing; send a message that links the attachment id.", options.hints, {
|
|
14115
|
+
target,
|
|
14116
|
+
attachmentIds: [data.id]
|
|
14117
|
+
}),
|
|
14118
|
+
text: formatAgentAttachmentUploaded(data, options.hints)
|
|
13751
14119
|
};
|
|
13752
14120
|
}
|
|
13753
14121
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -13758,7 +14126,7 @@ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
13758
14126
|
* verifies against the object store. A PUT that definitely failed cancels the
|
|
13759
14127
|
* session; a PUT whose outcome is unknown leaves it for completion to verify.
|
|
13760
14128
|
*/
|
|
13761
|
-
async function uploadThroughSession(client, transport, request, channelId, mimeType) {
|
|
14129
|
+
async function uploadThroughSession(client, transport, request, channelId, mimeType, options) {
|
|
13762
14130
|
const created = await client.attachments.createUploadSession({
|
|
13763
14131
|
channelId,
|
|
13764
14132
|
filename: request.filename,
|
|
@@ -13805,7 +14173,7 @@ async function uploadThroughSession(client, transport, request, channelId, mimeT
|
|
|
13805
14173
|
const completed = await client.attachments.completeUploadSession({ uploadId });
|
|
13806
14174
|
if (completed.ok) {
|
|
13807
14175
|
const attachment = completed.data.attachment;
|
|
13808
|
-
return uploadedOutcome(request.target, attachment, mimeType);
|
|
14176
|
+
return uploadedOutcome(request.target, attachment, mimeType, options);
|
|
13809
14177
|
}
|
|
13810
14178
|
const code = completed.error.kind === "http" ? completed.error.errorCode : null;
|
|
13811
14179
|
if (!(code === "UPLOAD_OBJECT_NOT_FOUND" || code === "UPLOAD_VERIFICATION_IN_PROGRESS") || attempt === 2) return failureFromClientResult(completed);
|
|
@@ -13829,6 +14197,46 @@ async function downloadAttachment(client, request) {
|
|
|
13829
14197
|
text: `Downloaded attachment ${request.attachmentId.slice(0, 8)} (${bytes.byteLength} bytes).`
|
|
13830
14198
|
};
|
|
13831
14199
|
}
|
|
14200
|
+
/**
|
|
14201
|
+
* Mint a short-lived URL for an attachment's bytes, for runtimes whose tools
|
|
14202
|
+
* cannot return binary data: the runtime (not the model) fetches `url` before
|
|
14203
|
+
* `expiresAt`. The URL is a bearer capability; do not log it. A Server whose
|
|
14204
|
+
* storage cannot presign answers 409 `download_url_unavailable`; that failure's
|
|
14205
|
+
* `next` points at the binary download (`attachments.download`).
|
|
14206
|
+
*/
|
|
14207
|
+
async function downloadAttachmentUrl(client, request, options = {}) {
|
|
14208
|
+
const invalid = validateOpRequest(downloadAttachmentUrlRequestSchema, request);
|
|
14209
|
+
if (invalid) return invalid;
|
|
14210
|
+
const attachmentId = request.attachmentId?.trim();
|
|
14211
|
+
if (!attachmentId) return failureOutcome(opError("INVALID_REQUEST", { message: "An attachment id is required." }));
|
|
14212
|
+
const result = await client.attachments.downloadUrl({ attachmentId });
|
|
14213
|
+
if (!result.ok) {
|
|
14214
|
+
const failure = failureFromClientResult(result);
|
|
14215
|
+
if (failure.error.serverCode !== "download_url_unavailable") return failure;
|
|
14216
|
+
const download = RAFT_HINTS.attachmentDownload(attachmentId);
|
|
14217
|
+
const nextAction = `This Server's storage cannot mint download URLs; download the bytes instead: \`${formatHint(download, options.hints)}\`.`;
|
|
14218
|
+
return {
|
|
14219
|
+
...failureOutcome({
|
|
14220
|
+
...failure.error,
|
|
14221
|
+
nextAction
|
|
14222
|
+
}),
|
|
14223
|
+
next: hintStep("download_bytes", download, nextAction, options.hints, { attachmentId })
|
|
14224
|
+
};
|
|
14225
|
+
}
|
|
14226
|
+
const { url, expiresAt, filename, mimeType } = result.data;
|
|
14227
|
+
return {
|
|
14228
|
+
ok: true,
|
|
14229
|
+
state: "url",
|
|
14230
|
+
data: {
|
|
14231
|
+
url,
|
|
14232
|
+
expiresAt,
|
|
14233
|
+
filename,
|
|
14234
|
+
mimeType
|
|
14235
|
+
},
|
|
14236
|
+
next: null,
|
|
14237
|
+
text: `Download URL for ${filename} (${mimeType}), valid until ${expiresAt}:\n${url}`
|
|
14238
|
+
};
|
|
14239
|
+
}
|
|
13832
14240
|
async function attachmentComments(client, request) {
|
|
13833
14241
|
const invalid = validateOpRequest(attachmentCommentsRequestSchema, request);
|
|
13834
14242
|
if (invalid) return invalid;
|
|
@@ -13977,14 +14385,15 @@ function renderSearchPreview(content, query) {
|
|
|
13977
14385
|
].join("");
|
|
13978
14386
|
}
|
|
13979
14387
|
/** Search results with <match>/<omit /> preview markup. */
|
|
13980
|
-
function formatAgentSearchResults(query, data, offset, sort, limit) {
|
|
14388
|
+
function formatAgentSearchResults(query, data, offset, sort, limit, style = "cli") {
|
|
13981
14389
|
const trimmedQuery = query.trim();
|
|
14390
|
+
const flag = (name) => formatHintFlag(name, name, style);
|
|
13982
14391
|
const oldestShown = data.results?.length ? data.results[data.results.length - 1]?.createdAt : void 0;
|
|
13983
|
-
const nextPageHint = sort === "recent" && oldestShown ? `page with
|
|
14392
|
+
const nextPageHint = sort === "recent" && oldestShown ? `page with ${flag("before")} ${oldestShown} (pages OLDER only; NOT a complete traversal: stored times are microsecond but this key is millisecond, so rows inside the boundary millisecond can be skipped, and a full page sharing one timestamp can repeat indefinitely; copy the key verbatim)` : `page with ${flag("offset")} ${(offset ?? 0) + (data.results?.length ?? 0)}`;
|
|
13984
14393
|
const effectiveLimit = Math.min(limit ?? 20, 50);
|
|
13985
14394
|
const countEqualsLimit = (data.results?.length ?? 0) === effectiveLimit;
|
|
13986
|
-
const limitSource = limit === void 0 ? `the server default of ${effectiveLimit}` : limit > 50 ? `the server cap of ${effectiveLimit} (your
|
|
13987
|
-
const atCapRemedy = effectiveLimit < 50 ?
|
|
14395
|
+
const limitSource = limit === void 0 ? `the server default of ${effectiveLimit}` : limit > 50 ? `the server cap of ${effectiveLimit} (your ${flag("limit")} ${limit} was clamped)` : `the ${flag("limit")} ${effectiveLimit} that was requested`;
|
|
14396
|
+
const atCapRemedy = effectiveLimit < 50 ? `re-run with a higher ${flag("limit")} to tell the two apart` : `this is already the server's maximum page, so completeness CANNOT be determined from this result: a higher ${flag("limit")} is clamped back to ${effectiveLimit} and paging will not reveal it; narrow the query (add ${flag("sender")}, ${flag("target")}, ${flag("after")} or ${flag("before")}) until fewer than ${effectiveLimit} results match`;
|
|
13988
14397
|
const truncation = data.hasMore === true ? `truncated=true · more results exist, ${nextPageHint}` : data.hasMore === false ? countEqualsLimit ? `truncated=unknown · server reported hasMore=false, but this page returned exactly ${limitSource}, which is also what a capped page returns; ${atCapRemedy}` : "truncated=false" : "truncated=unknown · server did not report hasMore";
|
|
13989
14398
|
if (!data.results || data.results.length === 0) return `No search results. (${truncation})`;
|
|
13990
14399
|
const formatted = data.results.map((result) => {
|
|
@@ -14033,7 +14442,7 @@ const reactRequestSchema = requestSchema()(object({
|
|
|
14033
14442
|
messageId: string().describe("Full or short message id."),
|
|
14034
14443
|
emoji: string().describe("One reaction emoji.")
|
|
14035
14444
|
}));
|
|
14036
|
-
async function searchMessages(client, request) {
|
|
14445
|
+
async function searchMessages(client, request, options = {}) {
|
|
14037
14446
|
const invalid = validateOpRequest(searchMessagesRequestSchema, request);
|
|
14038
14447
|
if (invalid) return invalid;
|
|
14039
14448
|
if (!request.query?.trim() && !request.target && !request.sender) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass a query, or filter by target or sender." }));
|
|
@@ -14055,11 +14464,16 @@ async function searchMessages(client, request) {
|
|
|
14055
14464
|
results: data.results,
|
|
14056
14465
|
hasMore
|
|
14057
14466
|
};
|
|
14467
|
+
const nextArgs = {
|
|
14468
|
+
...request,
|
|
14469
|
+
offset: (request.offset ?? 0) + data.results.length
|
|
14470
|
+
};
|
|
14058
14471
|
const next = hasMore ? {
|
|
14059
14472
|
kind: "next_search_page",
|
|
14060
|
-
args:
|
|
14061
|
-
|
|
14062
|
-
|
|
14473
|
+
args: nextArgs,
|
|
14474
|
+
operation: {
|
|
14475
|
+
name: "messages.search",
|
|
14476
|
+
args: nextArgs
|
|
14063
14477
|
},
|
|
14064
14478
|
why: "More results exist; page on, or narrow the query."
|
|
14065
14479
|
} : null;
|
|
@@ -14071,31 +14485,32 @@ async function searchMessages(client, request) {
|
|
|
14071
14485
|
text: formatAgentSearchResults(page.query, {
|
|
14072
14486
|
results: data.results,
|
|
14073
14487
|
...hasMore === null ? {} : { hasMore }
|
|
14074
|
-
}, request.offset, request.sort, request.limit)
|
|
14488
|
+
}, request.offset, request.sort, request.limit, options.hints)
|
|
14075
14489
|
};
|
|
14076
14490
|
}
|
|
14077
|
-
async function resolveMessage(client, request) {
|
|
14491
|
+
async function resolveMessage(client, request, options = {}) {
|
|
14078
14492
|
const invalid = validateOpRequest(resolveMessageRequestSchema, request);
|
|
14079
14493
|
if (invalid) return invalid;
|
|
14080
14494
|
if (!request.messageId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A message id is required." }));
|
|
14081
14495
|
const result = await client.messages.resolve({ msgId: request.messageId });
|
|
14082
14496
|
if (!result.ok) return failureFromClientResult(result);
|
|
14083
|
-
const message = projectRaftMessage(result.data.message);
|
|
14497
|
+
const message = projectRaftMessage(result.data.message, options.hints);
|
|
14084
14498
|
if (!message) return failureOutcome(opError("INVALID_RESPONSE", { message: "The resolved message carries no conversation identity." }));
|
|
14085
14499
|
return {
|
|
14086
14500
|
ok: true,
|
|
14087
14501
|
state: "message",
|
|
14088
14502
|
data: message,
|
|
14089
|
-
next: {
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14503
|
+
next: hintStep("read_target", RAFT_HINTS.messageRead({
|
|
14504
|
+
target: message.target,
|
|
14505
|
+
around: {
|
|
14506
|
+
shown: message.shortId ?? request.messageId,
|
|
14507
|
+
id: message.id ?? request.messageId
|
|
14508
|
+
}
|
|
14509
|
+
}), "Read the surrounding context before acting on one message.", options.hints, {
|
|
14510
|
+
target: message.target,
|
|
14511
|
+
around: message.id ?? request.messageId
|
|
14512
|
+
}),
|
|
14513
|
+
text: formatAgentMessages([toAgentMessageLike(message.raw)], options.hints)
|
|
14099
14514
|
};
|
|
14100
14515
|
}
|
|
14101
14516
|
async function reactToMessage(client, request, action = "add") {
|
|
@@ -14125,17 +14540,20 @@ function normalizeAgentMentionAction(action) {
|
|
|
14125
14540
|
if (action === "add" || action === "invite") return "add";
|
|
14126
14541
|
return null;
|
|
14127
14542
|
}
|
|
14128
|
-
function
|
|
14543
|
+
function actionVerbs(action) {
|
|
14129
14544
|
const verbs = action.availableActions.map(normalizeAgentMentionAction).filter((verb) => verb !== null);
|
|
14130
|
-
return Array.from(new Set(verbs))
|
|
14545
|
+
return Array.from(new Set(verbs));
|
|
14546
|
+
}
|
|
14547
|
+
function formatActionCommands(action, style) {
|
|
14548
|
+
return actionVerbs(action).map((verb) => ` ${verb}: ${formatHint(RAFT_HINTS.mentionAction(verb, action.resolutionId), style)}`);
|
|
14131
14549
|
}
|
|
14132
14550
|
function formatAuthoredMentionToken(targetHandle) {
|
|
14133
14551
|
return targetHandle.startsWith("@") ? targetHandle : `@${targetHandle}`;
|
|
14134
14552
|
}
|
|
14135
14553
|
const PENDING_MENTION_ACTION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14136
14554
|
/** Per-token mention recovery command line; null when the id is not a UUID (fail closed). */
|
|
14137
|
-
function formatAgentMentionNotifyRecoveryCommand(resolutionId) {
|
|
14138
|
-
return PENDING_MENTION_ACTION_ID_RE.test(resolutionId) ?
|
|
14555
|
+
function formatAgentMentionNotifyRecoveryCommand(resolutionId, style = "cli") {
|
|
14556
|
+
return PENDING_MENTION_ACTION_ID_RE.test(resolutionId) ? formatHint(RAFT_HINTS.mentionAction("notify", resolutionId), style) : null;
|
|
14139
14557
|
}
|
|
14140
14558
|
function toAgentSenderPendingMentionAction(action) {
|
|
14141
14559
|
return {
|
|
@@ -14192,22 +14610,24 @@ function normalizeAgentMentionActionResults(data) {
|
|
|
14192
14610
|
})).filter((item) => item.resolutionId.length > 0);
|
|
14193
14611
|
}
|
|
14194
14612
|
const PENDING_DEFAULT_LIMIT = 50;
|
|
14195
|
-
function pendingPageVerdict(hasMore, limit, shown) {
|
|
14196
|
-
const
|
|
14197
|
-
|
|
14613
|
+
function pendingPageVerdict(hasMore, limit, shown, style) {
|
|
14614
|
+
const limitFlag = formatHintFlag("limit", "limit", style);
|
|
14615
|
+
const asked = limit === void 0 ? "server default 50" : `${limitFlag} ${limit}`;
|
|
14616
|
+
if (hasMore === true) return (limit ?? PENDING_DEFAULT_LIMIT) >= 100 ? `shown ${shown}, ${asked} — truncated=true · more pending actions exist AND this is the server's maximum page (100); this route has no offset or cursor, so the rest cannot be reached from here — resolve what you can and re-run; rows with no available actions are not actionable here and will remain until they expire` : `shown ${shown}, ${asked} — truncated=true · more pending actions exist; raise ${limitFlag} (server caps at 100) or resolve these first and re-run`;
|
|
14198
14617
|
if (hasMore === false) return `shown ${shown}, ${asked} — truncated=false · this is the complete list`;
|
|
14199
14618
|
return `shown ${shown}, ${asked} — truncated=unknown · server did not report has_more, so completeness is NOT asserted`;
|
|
14200
14619
|
}
|
|
14201
14620
|
/** Undelivered-mentions partial result (send) or the pending list. */
|
|
14202
14621
|
function formatAgentPendingMentionActions(actions, opts = {}) {
|
|
14622
|
+
const style = opts.hints ?? "cli";
|
|
14203
14623
|
const unresolvedMentionHandles = opts.source === "send" ? Array.from(new Set(opts.unresolvedMentionHandles ?? [])) : [];
|
|
14204
|
-
const pageNote = opts.source === "pending" ? pendingPageVerdict(opts.hasMore, opts.limit, actions.length) : "";
|
|
14624
|
+
const pageNote = opts.source === "pending" ? pendingPageVerdict(opts.hasMore, opts.limit, actions.length, style) : "";
|
|
14205
14625
|
if (actions.length === 0 && unresolvedMentionHandles.length === 0) return opts.source === "pending" ? `Pending mention actions\n\nNo pending mention actions. (${pageNote})\n` : "";
|
|
14206
14626
|
if (opts.source === "send") {
|
|
14207
14627
|
const lines = [
|
|
14208
14628
|
"Undelivered mentions — partial result",
|
|
14209
14629
|
"Message effect: status=queued. Queue acceptance is the only message proof.",
|
|
14210
|
-
|
|
14630
|
+
`Do not rerun \`${formatHintName(RAFT_HINTS.messageSendName(), style)}\`; the message is already queued and a retry could duplicate it.`,
|
|
14211
14631
|
"Each row below is bound to the literal @token from your message.",
|
|
14212
14632
|
"For a literal name rather than a recipient, wrap the @handle in inline or fenced code.",
|
|
14213
14633
|
""
|
|
@@ -14221,10 +14641,10 @@ function formatAgentPendingMentionActions(actions, opts = {}) {
|
|
|
14221
14641
|
if (action.messageId) lines.push(` message: ${action.messageId}`);
|
|
14222
14642
|
lines.push(` expires: ${action.expiresAt ?? "unknown"}`);
|
|
14223
14643
|
if (action.recoveryCommand) {
|
|
14224
|
-
lines.push(` recovery: ${action.recoveryCommand}`);
|
|
14644
|
+
lines.push(` recovery: ${style === "cli" ? action.recoveryCommand : formatAgentMentionNotifyRecoveryCommand(action.resolutionId, style)}`);
|
|
14225
14645
|
lines.push(" note: the handle resolved, but the target was not in this conversation at send time. This does not prove the person left the server.");
|
|
14226
14646
|
lines.push(" note: notify exits nonzero unless the target queue accepts the delivery.");
|
|
14227
|
-
} else lines.push(
|
|
14647
|
+
} else lines.push(` recovery: unavailable because the pending action id is invalid; inspect \`${formatHint(RAFT_HINTS.mentionPending(), style)}\` without resending the message.`);
|
|
14228
14648
|
}
|
|
14229
14649
|
for (const rawHandle of unresolvedMentionHandles) {
|
|
14230
14650
|
const warning = toAgentSenderUnresolvedMentionWarning(rawHandle);
|
|
@@ -14248,11 +14668,11 @@ function formatAgentPendingMentionActions(actions, opts = {}) {
|
|
|
14248
14668
|
if (action.messageId) lines.push(` message: ${action.messageId}`);
|
|
14249
14669
|
lines.push(` reason: ${formatPendingReason(action.reason)}`);
|
|
14250
14670
|
if (action.expiresAt) lines.push(` expires: ${action.expiresAt}`);
|
|
14251
|
-
const commands = formatActionCommands(action);
|
|
14671
|
+
const commands = formatActionCommands(action, style);
|
|
14252
14672
|
if (commands.length > 0) {
|
|
14253
14673
|
lines.push(" recovery commands:");
|
|
14254
14674
|
lines.push(...commands);
|
|
14255
|
-
if (
|
|
14675
|
+
if (actionVerbs(action).includes("notify")) lines.push(" note: notify exits nonzero unless the target queue accepts the delivery.");
|
|
14256
14676
|
}
|
|
14257
14677
|
}
|
|
14258
14678
|
return `${lines.join("\n")}\n`;
|
|
@@ -14308,7 +14728,7 @@ const executeMentionActionRequestSchema = requestSchema()(object({
|
|
|
14308
14728
|
resolutionIds: array(string()).describe("resolutionId values from mentions.pending.")
|
|
14309
14729
|
}));
|
|
14310
14730
|
const senderMentionDeliveriesRequestSchema = requestSchema()(object({ messageId: string().describe("Id of a message you sent.") }));
|
|
14311
|
-
async function pendingMentionActions(client, request = {}) {
|
|
14731
|
+
async function pendingMentionActions(client, request = {}, options = {}) {
|
|
14312
14732
|
const invalid = validateOpRequest(pendingMentionActionsRequestSchema, request);
|
|
14313
14733
|
if (invalid) return invalid;
|
|
14314
14734
|
const result = await client.mentions.pendingActions(request.limit === void 0 ? {} : { limit: String(request.limit) });
|
|
@@ -14316,15 +14736,10 @@ async function pendingMentionActions(client, request = {}) {
|
|
|
14316
14736
|
const actions = normalizeAgentPendingMentionActions(result.data);
|
|
14317
14737
|
const hasMore = typeof result.data.has_more === "boolean" ? result.data.has_more : null;
|
|
14318
14738
|
const first = actions.find((a) => a.availableActions.length > 0);
|
|
14319
|
-
const next = first ? {
|
|
14320
|
-
|
|
14321
|
-
|
|
14322
|
-
|
|
14323
|
-
action: "notify",
|
|
14324
|
-
resolutionIds: [first.resolutionId]
|
|
14325
|
-
},
|
|
14326
|
-
why: "This @mention reached nobody at send time; notify the target (or add them) so the message is seen."
|
|
14327
|
-
} : null;
|
|
14739
|
+
const next = first ? hintStep("resolve_mention", RAFT_HINTS.mentionAction("notify", first.resolutionId), "This @mention reached nobody at send time; notify the target (or add them) so the message is seen.", options.hints, {
|
|
14740
|
+
action: "notify",
|
|
14741
|
+
resolutionIds: [first.resolutionId]
|
|
14742
|
+
}) : null;
|
|
14328
14743
|
return {
|
|
14329
14744
|
ok: true,
|
|
14330
14745
|
state: actions.length > 0 ? "pending" : "empty",
|
|
@@ -14337,7 +14752,8 @@ async function pendingMentionActions(client, request = {}) {
|
|
|
14337
14752
|
text: formatAgentPendingMentionActions(actions, {
|
|
14338
14753
|
source: "pending",
|
|
14339
14754
|
...hasMore === null ? {} : { hasMore },
|
|
14340
|
-
limit: request.limit
|
|
14755
|
+
limit: request.limit,
|
|
14756
|
+
hints: options.hints
|
|
14341
14757
|
})
|
|
14342
14758
|
};
|
|
14343
14759
|
}
|
|
@@ -14435,7 +14851,7 @@ async function getManualTopic(client, request) {
|
|
|
14435
14851
|
text: formatAgentKnowledgeStdout(result.data.content)
|
|
14436
14852
|
};
|
|
14437
14853
|
}
|
|
14438
|
-
async function searchManual(client, request) {
|
|
14854
|
+
async function searchManual(client, request, options = {}) {
|
|
14439
14855
|
const malformed = validateOpRequest(searchManualRequestSchema, request);
|
|
14440
14856
|
if (malformed) return malformed;
|
|
14441
14857
|
const invalid = requireContext(request);
|
|
@@ -14451,12 +14867,7 @@ async function searchManual(client, request) {
|
|
|
14451
14867
|
const data = result.data;
|
|
14452
14868
|
const results = data.results;
|
|
14453
14869
|
const first = results[0];
|
|
14454
|
-
const next = first ? {
|
|
14455
|
-
kind: "read_manual_topic",
|
|
14456
|
-
command: `raft manual get ${first.slug} --intent "…" --reason "…"`,
|
|
14457
|
-
args: { topic: first.slug },
|
|
14458
|
-
why: "Open the best-matching topic."
|
|
14459
|
-
} : null;
|
|
14870
|
+
const next = first ? hintStep("read_manual_topic", RAFT_HINTS.manualGet(first.slug), "Open the best-matching topic.", options.hints, { topic: first.slug }) : null;
|
|
14460
14871
|
return {
|
|
14461
14872
|
ok: true,
|
|
14462
14873
|
state: results.length > 0 ? "results" : "empty",
|
|
@@ -14657,43 +15068,45 @@ function formatAgentActionCardPosted(target, messageId) {
|
|
|
14657
15068
|
* the preparer in the card's own thread; a card posted inside a thread has no
|
|
14658
15069
|
* thread of its own, so the reply lands in that same thread.
|
|
14659
15070
|
*/
|
|
14660
|
-
function awaitConfirmationNext(target, messageId) {
|
|
15071
|
+
function awaitConfirmationNext(target, messageId, style) {
|
|
14661
15072
|
const short = messageId.slice(0, 8);
|
|
14662
|
-
if (getParentTargetForThread(target) !== null) return {
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
15073
|
+
if (getParentTargetForThread(target) !== null) return hintStep("await_confirmation", RAFT_HINTS.messageRead({
|
|
15074
|
+
target,
|
|
15075
|
+
around: {
|
|
15076
|
+
shown: short,
|
|
15077
|
+
id: messageId
|
|
15078
|
+
}
|
|
15079
|
+
}), "A human must click the card to commit it. When it is executed (or fails), the outcome arrives as a reply in this thread that @mentions you; you do not need to poll.", style, {
|
|
15080
|
+
target,
|
|
15081
|
+
around: messageId,
|
|
15082
|
+
messageId
|
|
15083
|
+
});
|
|
14671
15084
|
const thread = agentTaskThreadTarget(target, messageId);
|
|
14672
|
-
return {
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
target: thread,
|
|
14677
|
-
messageId
|
|
14678
|
-
},
|
|
14679
|
-
why: "A human must click the card to commit it. When it is executed (or fails), the outcome arrives as a reply in the card's thread that @mentions you; you do not need to poll."
|
|
14680
|
-
};
|
|
15085
|
+
return hintStep("await_confirmation", RAFT_HINTS.messageRead({ target: thread }), "A human must click the card to commit it. When it is executed (or fails), the outcome arrives as a reply in the card's thread that @mentions you; you do not need to poll.", style, {
|
|
15086
|
+
target: thread,
|
|
15087
|
+
messageId
|
|
15088
|
+
});
|
|
14681
15089
|
}
|
|
14682
|
-
async function prepareActionCard(client, request) {
|
|
15090
|
+
async function prepareActionCard(client, request, options = {}) {
|
|
14683
15091
|
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." }));
|
|
14684
15092
|
const invalid = validateOpRequest(prepareActionCardRequestSchema, request);
|
|
14685
15093
|
if (invalid) return invalid;
|
|
14686
|
-
const
|
|
14687
|
-
|
|
15094
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
15095
|
+
const result = await client.actions.prepare({
|
|
15096
|
+
...request,
|
|
15097
|
+
idempotencyKey
|
|
15098
|
+
});
|
|
15099
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
14688
15100
|
const card = {
|
|
14689
15101
|
target: request.target,
|
|
14690
|
-
messageId: result.data.messageId
|
|
15102
|
+
messageId: result.data.messageId,
|
|
15103
|
+
idempotencyKey
|
|
14691
15104
|
};
|
|
14692
15105
|
return {
|
|
14693
15106
|
ok: true,
|
|
14694
15107
|
state: "prepared",
|
|
14695
15108
|
data: card,
|
|
14696
|
-
next: awaitConfirmationNext(request.target, card.messageId),
|
|
15109
|
+
next: awaitConfirmationNext(request.target, card.messageId, options.hints ?? "cli"),
|
|
14697
15110
|
text: formatAgentActionCardPosted(request.target, card.messageId)
|
|
14698
15111
|
};
|
|
14699
15112
|
}
|
|
@@ -15104,6 +15517,14 @@ const OPERATION_DEFS = [
|
|
|
15104
15517
|
consumes: nothing,
|
|
15105
15518
|
output: small
|
|
15106
15519
|
},
|
|
15520
|
+
{
|
|
15521
|
+
name: "attachments.downloadUrl",
|
|
15522
|
+
description: "Get a short-lived (5 minute) URL for an attachment's bytes, with its filename and MIME type, for runtimes that fetch files themselves. The URL grants access to the file: do not post it in messages.",
|
|
15523
|
+
schema: downloadAttachmentUrlRequestSchema,
|
|
15524
|
+
routes: ["attachmentDownloadUrl"],
|
|
15525
|
+
consumes: nothing,
|
|
15526
|
+
output: small
|
|
15527
|
+
},
|
|
15107
15528
|
{
|
|
15108
15529
|
name: "attachments.comments",
|
|
15109
15530
|
description: "List the comments on an attachment.",
|
|
@@ -15148,9 +15569,11 @@ const OPERATION_DEFS = [
|
|
|
15148
15569
|
schema: prepareActionCardRequestSchema,
|
|
15149
15570
|
fieldDescriptions: {
|
|
15150
15571
|
target: "Conversation to post the card in: `#channel`, `dm:@peer`, or a thread.",
|
|
15151
|
-
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described."
|
|
15572
|
+
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described.",
|
|
15573
|
+
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."
|
|
15152
15574
|
},
|
|
15153
15575
|
routes: ["actionPrepare"],
|
|
15576
|
+
idempotencyArg: "idempotencyKey",
|
|
15154
15577
|
consumes: nothing,
|
|
15155
15578
|
output: small
|
|
15156
15579
|
},
|
|
@@ -15201,6 +15624,7 @@ const OPERATION_DEFS = [
|
|
|
15201
15624
|
description: "Create one or more tasks on a channel's board; each gets its own thread.",
|
|
15202
15625
|
schema: createTasksRequestSchema,
|
|
15203
15626
|
routes: ["taskCreate"],
|
|
15627
|
+
idempotencyArg: "idempotencyKey",
|
|
15204
15628
|
consumes: nothing,
|
|
15205
15629
|
output: small
|
|
15206
15630
|
},
|
|
@@ -15402,10 +15826,6 @@ const OPERATION_DEFS = [
|
|
|
15402
15826
|
output: small
|
|
15403
15827
|
}
|
|
15404
15828
|
];
|
|
15405
|
-
/** `tasks.updateStatus` → `tasks_update_status`. */
|
|
15406
|
-
function raftToolNameFor(name) {
|
|
15407
|
-
return name.replace(/\./g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
15408
|
-
}
|
|
15409
15829
|
const IDEMPOTENCY_STRENGTH = {
|
|
15410
15830
|
natural: 0,
|
|
15411
15831
|
key: 1,
|
|
@@ -15477,11 +15897,17 @@ function createRaft(options) {
|
|
|
15477
15897
|
const serverUrl = requireServerUrl(options.serverUrl);
|
|
15478
15898
|
const authorization = `Bearer ${requireAgentCredential(options.credential)}`;
|
|
15479
15899
|
const frontier = SeenFrontier.fromSnapshot(options.frontier);
|
|
15900
|
+
if (options.hints !== void 0 && options.hints !== "cli" && options.hints !== "tool") throw new TypeError("createRaft: hints must be \"cli\" or \"tool\".");
|
|
15901
|
+
const hints = options.hints ?? "cli";
|
|
15902
|
+
/** How every operation renders its hints. */
|
|
15903
|
+
const style = { hints };
|
|
15904
|
+
/** The SDK's default NOT_FOUND next action names a command; render it in the configured style. */
|
|
15905
|
+
const styled = (outcome) => restyleDefaultNextAction(outcome, hints);
|
|
15480
15906
|
const session = new RaftStateSession(options.state, frontier, options.onStateSaveError);
|
|
15481
15907
|
/** Load before, run, then save if the operation succeeded and changed the state. */
|
|
15482
15908
|
const withState = async (run) => {
|
|
15483
15909
|
await session.ensureLoaded();
|
|
15484
|
-
const outcome = await run();
|
|
15910
|
+
const outcome = styled(await run());
|
|
15485
15911
|
if (outcome.ok) await session.save();
|
|
15486
15912
|
return outcome;
|
|
15487
15913
|
};
|
|
@@ -15517,7 +15943,7 @@ function createRaft(options) {
|
|
|
15517
15943
|
const outcome = await sendMessage(api, pending ? {
|
|
15518
15944
|
...request,
|
|
15519
15945
|
idempotencyKey: pending.idempotencyKey
|
|
15520
|
-
} : request, seen);
|
|
15946
|
+
} : request, seen, style);
|
|
15521
15947
|
if (outcome.ok && outcome.state === "interrupted" && outcome.interrupt.resume.idempotencyKey) session.rememberContinuation({
|
|
15522
15948
|
target: request.target,
|
|
15523
15949
|
idempotencyKey: outcome.interrupt.resume.idempotencyKey,
|
|
@@ -15532,7 +15958,7 @@ function createRaft(options) {
|
|
|
15532
15958
|
const outcome = await checkInbox(inboxApi, {
|
|
15533
15959
|
...request,
|
|
15534
15960
|
...since === void 0 ? {} : { since }
|
|
15535
|
-
}, seen);
|
|
15961
|
+
}, seen, style);
|
|
15536
15962
|
if (outcome.ok) {
|
|
15537
15963
|
session.markDirty();
|
|
15538
15964
|
if (since !== void 0 && (request.ack ?? "cursor") === "cursor") session.commit(since);
|
|
@@ -15551,7 +15977,7 @@ function createRaft(options) {
|
|
|
15551
15977
|
if (batch.ackMode === "cursor" && batch.messages.length > 0) session.setPending(batch.cursor);
|
|
15552
15978
|
session.markDirty();
|
|
15553
15979
|
await session.save();
|
|
15554
|
-
});
|
|
15980
|
+
}, style);
|
|
15555
15981
|
})();
|
|
15556
15982
|
const commitInbox = async (target) => {
|
|
15557
15983
|
await session.ensureLoaded();
|
|
@@ -15563,7 +15989,7 @@ function createRaft(options) {
|
|
|
15563
15989
|
};
|
|
15564
15990
|
/** `seen` undefined: read without recording anything (a code read). */
|
|
15565
15991
|
const readWithState = (request, seen) => withState(async () => {
|
|
15566
|
-
const outcome = await readHistory(api, request, seen);
|
|
15992
|
+
const outcome = await readHistory(api, request, seen, style);
|
|
15567
15993
|
if (outcome.ok && seen) session.markDirty();
|
|
15568
15994
|
return outcome;
|
|
15569
15995
|
});
|
|
@@ -15572,16 +15998,16 @@ function createRaft(options) {
|
|
|
15572
15998
|
wake: {
|
|
15573
15999
|
verifyNotice: (input) => verifyInboxNotice(input),
|
|
15574
16000
|
webhook: {
|
|
15575
|
-
status: () => webhookStatus(api),
|
|
15576
|
-
register: (request) => registerWebhook(api, request),
|
|
15577
|
-
unregister: () => unregisterWebhook(api)
|
|
16001
|
+
status: () => webhookStatus(api).then(styled),
|
|
16002
|
+
register: (request) => registerWebhook(api, request).then(styled),
|
|
16003
|
+
unregister: () => unregisterWebhook(api).then(styled)
|
|
15578
16004
|
}
|
|
15579
16005
|
},
|
|
15580
16006
|
inbox: {
|
|
15581
16007
|
check: (request = {}) => checkWithState(request, frontier),
|
|
15582
16008
|
commit: (target) => commitInbox(target),
|
|
15583
16009
|
drain: (request = {}) => drainWithState(request, frontier),
|
|
15584
|
-
list: (request) => listInbox(api, request)
|
|
16010
|
+
list: (request) => listInbox(api, request, style).then(styled)
|
|
15585
16011
|
},
|
|
15586
16012
|
messages: {
|
|
15587
16013
|
read: (request) => readWithState(request, frontier),
|
|
@@ -15590,10 +16016,10 @@ function createRaft(options) {
|
|
|
15590
16016
|
...request,
|
|
15591
16017
|
target: message.target
|
|
15592
16018
|
}, frontier),
|
|
15593
|
-
search: (request) => searchMessages(api, request),
|
|
15594
|
-
resolve: (request) => resolveMessage(api, request),
|
|
15595
|
-
react: (request) => reactToMessage(api, request, "add"),
|
|
15596
|
-
unreact: (request) => reactToMessage(api, request, "remove")
|
|
16019
|
+
search: (request) => searchMessages(api, request, style).then(styled),
|
|
16020
|
+
resolve: (request) => resolveMessage(api, request, style).then(styled),
|
|
16021
|
+
react: (request) => reactToMessage(api, request, "add").then(styled),
|
|
16022
|
+
unreact: (request) => reactToMessage(api, request, "remove").then(styled)
|
|
15597
16023
|
},
|
|
15598
16024
|
attachments: {
|
|
15599
16025
|
upload: (request) => uploadAttachment(api, {
|
|
@@ -15601,51 +16027,52 @@ function createRaft(options) {
|
|
|
15601
16027
|
fetch: options.fetch ?? fetch,
|
|
15602
16028
|
headers: Object.fromEntries(new Headers(options.headers)),
|
|
15603
16029
|
authorization
|
|
15604
|
-
}, request),
|
|
15605
|
-
download: (request) => downloadAttachment(api, request),
|
|
15606
|
-
|
|
16030
|
+
}, request, style).then(styled),
|
|
16031
|
+
download: (request) => downloadAttachment(api, request).then(styled),
|
|
16032
|
+
downloadUrl: (request) => downloadAttachmentUrl(api, request, style).then(styled),
|
|
16033
|
+
comments: (request) => attachmentComments(api, request).then(styled)
|
|
15607
16034
|
},
|
|
15608
16035
|
mentions: {
|
|
15609
|
-
pending: (request) => pendingMentionActions(api, request),
|
|
15610
|
-
execute: (request) => executeMentionAction(api, request),
|
|
15611
|
-
deliveries: (request) => senderMentionDeliveries(api, request)
|
|
16036
|
+
pending: (request) => pendingMentionActions(api, request, style).then(styled),
|
|
16037
|
+
execute: (request) => executeMentionAction(api, request).then(styled),
|
|
16038
|
+
deliveries: (request) => senderMentionDeliveries(api, request).then(styled)
|
|
15612
16039
|
},
|
|
15613
|
-
actions: { prepare: (request) => prepareActionCard(api, request) },
|
|
16040
|
+
actions: { prepare: (request) => prepareActionCard(api, request, style).then(styled) },
|
|
15614
16041
|
manual: {
|
|
15615
|
-
get: (request) => getManualTopic(api, request),
|
|
15616
|
-
search: (request) => searchManual(api, request)
|
|
16042
|
+
get: (request) => getManualTopic(api, request).then(styled),
|
|
16043
|
+
search: (request) => searchManual(api, request, style).then(styled)
|
|
15617
16044
|
},
|
|
15618
16045
|
tasks: {
|
|
15619
|
-
claim: (request) => claimTasks(api, request),
|
|
15620
|
-
list: (request) => listTasks(api, request),
|
|
15621
|
-
create: (request) => createTasks(api, request),
|
|
15622
|
-
unclaim: (request) => unclaimTask(api, request),
|
|
15623
|
-
assign: (request) => assignTask(api, request),
|
|
15624
|
-
unassign: (request) => unassignTask(api, request),
|
|
15625
|
-
updateStatus: (request) => updateTaskStatus(api, request),
|
|
15626
|
-
amend: (request) => amendTask(api, request),
|
|
15627
|
-
history: (request) => taskHistory(api, request),
|
|
15628
|
-
show: (request) => showTask(api, request),
|
|
15629
|
-
convert: (request) => convertMessageToTask(api, request),
|
|
15630
|
-
delete: (request) => deleteTask(api, request)
|
|
16046
|
+
claim: (request) => claimTasks(api, request, style).then(styled),
|
|
16047
|
+
list: (request) => listTasks(api, request, style).then(styled),
|
|
16048
|
+
create: (request) => createTasks(api, request, style).then(styled),
|
|
16049
|
+
unclaim: (request) => unclaimTask(api, request).then(styled),
|
|
16050
|
+
assign: (request) => assignTask(api, request).then(styled),
|
|
16051
|
+
unassign: (request) => unassignTask(api, request).then(styled),
|
|
16052
|
+
updateStatus: (request) => updateTaskStatus(api, request, style).then(styled),
|
|
16053
|
+
amend: (request) => amendTask(api, request, style).then(styled),
|
|
16054
|
+
history: (request) => taskHistory(api, request).then(styled),
|
|
16055
|
+
show: (request) => showTask(api, request, style).then(styled),
|
|
16056
|
+
convert: (request) => convertMessageToTask(api, request, style).then(styled),
|
|
16057
|
+
delete: (request) => deleteTask(api, request).then(styled)
|
|
15631
16058
|
},
|
|
15632
16059
|
channels: {
|
|
15633
|
-
join: (request) => joinChannel(api, request),
|
|
15634
|
-
leave: (request) => leaveChannel(api, request),
|
|
15635
|
-
mute: (request) => muteChannel(api, request),
|
|
15636
|
-
unmute: (request) => unmuteChannel(api, request),
|
|
15637
|
-
members: (request) => channelMembers(api, request),
|
|
15638
|
-
info: (request) => channelInfo(api, request)
|
|
16060
|
+
join: (request) => joinChannel(api, request, style).then(styled),
|
|
16061
|
+
leave: (request) => leaveChannel(api, request, style).then(styled),
|
|
16062
|
+
mute: (request) => muteChannel(api, request, style).then(styled),
|
|
16063
|
+
unmute: (request) => unmuteChannel(api, request, style).then(styled),
|
|
16064
|
+
members: (request) => channelMembers(api, request).then(styled),
|
|
16065
|
+
info: (request) => channelInfo(api, request, style).then(styled)
|
|
15639
16066
|
},
|
|
15640
16067
|
threads: {
|
|
15641
|
-
list: () => listThreads(api),
|
|
15642
|
-
unfollow: (request) => unfollowThread(api, request)
|
|
16068
|
+
list: () => listThreads(api).then(styled),
|
|
16069
|
+
unfollow: (request) => unfollowThread(api, request).then(styled)
|
|
15643
16070
|
},
|
|
15644
|
-
server: { info: (request) => serverInfo(api, request) },
|
|
15645
|
-
users: { info: (request) => userInfo(api, request) },
|
|
16071
|
+
server: { info: (request) => serverInfo(api, request, style).then(styled) },
|
|
16072
|
+
users: { info: (request) => userInfo(api, request, style).then(styled) },
|
|
15646
16073
|
profile: {
|
|
15647
|
-
show: (request) => showProfile(api, request),
|
|
15648
|
-
update: (request) => updateProfile(api, request)
|
|
16074
|
+
show: (request) => showProfile(api, request).then(styled),
|
|
16075
|
+
update: (request) => updateProfile(api, request).then(styled)
|
|
15649
16076
|
},
|
|
15650
16077
|
frontier,
|
|
15651
16078
|
state: {
|
|
@@ -15666,7 +16093,7 @@ function createRaft(options) {
|
|
|
15666
16093
|
const drain = drainWithState(args, call.seen);
|
|
15667
16094
|
const batches = [];
|
|
15668
16095
|
for (let step = await drain.next();; step = await drain.next()) {
|
|
15669
|
-
if (step.done) return drainedInboxOutcome(batches, step.value);
|
|
16096
|
+
if (step.done) return styled(drainedInboxOutcome(batches, step.value, style));
|
|
15670
16097
|
batches.push(step.value);
|
|
15671
16098
|
}
|
|
15672
16099
|
},
|
|
@@ -15694,6 +16121,7 @@ function createRaft(options) {
|
|
|
15694
16121
|
"messages.resolve": (args) => raft.messages.resolve(args),
|
|
15695
16122
|
"messages.react": (args) => raft.messages.react(args),
|
|
15696
16123
|
"messages.unreact": (args) => raft.messages.unreact(args),
|
|
16124
|
+
"attachments.downloadUrl": (args) => raft.attachments.downloadUrl(args),
|
|
15697
16125
|
"attachments.comments": (args) => raft.attachments.comments(args),
|
|
15698
16126
|
"mentions.pending": (args) => raft.mentions.pending(args),
|
|
15699
16127
|
"mentions.execute": (args) => raft.mentions.execute(args),
|
|
@@ -15766,4 +16194,4 @@ function whoamiOutcome(result) {
|
|
|
15766
16194
|
};
|
|
15767
16195
|
}
|
|
15768
16196
|
//#endregion
|
|
15769
|
-
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 };
|
|
16197
|
+
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, downloadAttachmentUrlRequestSchema, 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 };
|