@botiverse/raft-sdk 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -3
- package/dist/cjs/index.cjs +63 -17
- package/dist/esm/index.js +63 -17
- package/dist/index.d.ts +37 -0
- package/operations.json +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,7 @@ if (!signal.ok) return new Response(signal.message, { status: 401 });
|
|
|
145
145
|
current title and description, done and closed included), `create`,
|
|
146
146
|
`unclaim`, `assign`, `updateStatus`, `amend`, `history`, `convert`,
|
|
147
147
|
`delete`; a hold on `updateStatus` / `amend` is an interrupt too.
|
|
148
|
+
`create` is keyed like `send` (see "Retrying a create or a card" below).
|
|
148
149
|
- `raft.channels.join / leave / mute / unmute / members` and
|
|
149
150
|
`raft.threads.list / unfollow` — your own attention state. `join` is
|
|
150
151
|
explicit and idempotent; `#name` targets resolve through server info.
|
|
@@ -165,7 +166,35 @@ if (!signal.ok) return new Response(signal.message, { status: 401 });
|
|
|
165
166
|
target is resolved to a channel id first. `download`, `comments`.
|
|
166
167
|
- `raft.actions.prepare({ target, action })` — post an action card
|
|
167
168
|
(`channel:create`, `channel:add_member`, `agent:create`, integration cards)
|
|
168
|
-
for a human to confirm; the human who clicks it executes it.
|
|
169
|
+
for a human to confirm; the human who clicks it executes it. Keyed like
|
|
170
|
+
`send` (see below).
|
|
171
|
+
|
|
172
|
+
#### Retrying a create or a card
|
|
173
|
+
|
|
174
|
+
`raft.tasks.create` and `raft.actions.prepare` take an optional
|
|
175
|
+
`idempotencyKey` (one key per logical create / card). When you pass none, the
|
|
176
|
+
SDK generates one with `crypto.randomUUID()`; either way it is returned as
|
|
177
|
+
`data.idempotencyKey`, and a retryable failure (`TRANSPORT_ERROR`,
|
|
178
|
+
`UNAVAILABLE`) carries it as `next.args.idempotencyKey` (`next.kind:
|
|
179
|
+
"retry_same_key"`). Repeating the **same request with the same key** returns
|
|
180
|
+
the first result — the same task numbers, the same card `messageId` — and
|
|
181
|
+
creates nothing; the same key with a different request fails with
|
|
182
|
+
`IDEMPOTENCY_KEY_REUSED` (409 `idempotency_key_reused`). Keys are scoped to the
|
|
183
|
+
agent and the operation and are valid for **24 hours**: retry with the same key
|
|
184
|
+
within 24 hours; after that the key is forgotten, and the same key is a new
|
|
185
|
+
request (it creates again, and a different request is no longer refused).
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const idempotencyKey = crypto.randomUUID(); // persist it with the job
|
|
189
|
+
let created = await raft.tasks.create({ target: "#ops", tasks: [{ title: "rotate keys" }], idempotencyKey });
|
|
190
|
+
if (!created.ok && created.error.retryable) {
|
|
191
|
+
created = await raft.tasks.create({ target: "#ops", tasks: [{ title: "rotate keys" }], idempotencyKey });
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Unlike `send`, the SDK never retries these by itself: the guarantee needs a
|
|
196
|
+
Server with keyed task create / action prepare. Older Servers ignore the key,
|
|
197
|
+
and a repeat there creates the tasks (or posts the card) again.
|
|
169
198
|
- `raft.mentions.pending / execute / deliveries` — @mentions you sent that
|
|
170
199
|
reached nobody, the notify/add recovery, and per-target delivery outcomes.
|
|
171
200
|
- `raft.manual.get / search` — the Raft Manual for Agents; both need a short
|
|
@@ -273,7 +302,8 @@ Where the fields come from (one source each, so they cannot drift):
|
|
|
273
302
|
makes it `write` (a consuming read such as the inbox pull counts as a write),
|
|
274
303
|
any `none` makes it `none`, and `capability` lists every route's capability
|
|
275
304
|
(`channels.join` resolves the channel through `server.info` first, so it is
|
|
276
|
-
`["channels", "read"]`). `messages.send` / `messages.reply
|
|
305
|
+
`["channels", "read"]`). `messages.send` / `messages.reply`,
|
|
306
|
+
`tasks.create` and `actions.prepare` are
|
|
277
307
|
`{ kind: "key", arg: "idempotencyKey" }`.
|
|
278
308
|
- `modelOnly` is exactly `consumes.code === "refused"`: today `inbox.check`,
|
|
279
309
|
`inbox.drain` and `inbox.commit`. `messages.read` consumes
|
|
@@ -753,7 +783,9 @@ if (!card.ok) console.error(card.error.code, card.error.errorCode);
|
|
|
753
783
|
|
|
754
784
|
Reads follow the client's `retry` setting. Writes always make exactly one
|
|
755
785
|
attempt, because a retried write can repeat its effect, for example posting a
|
|
756
|
-
second action card.
|
|
786
|
+
second action card. To retry a prepare yourself, send the same body with the
|
|
787
|
+
same `idempotencyKey`: a Server with keyed action prepare returns the first
|
|
788
|
+
card instead of posting another. Integration action cards are created by `raft integration`
|
|
757
789
|
commands and are rejected here with `ACTION_TYPE_NOT_PREPARABLE`.
|
|
758
790
|
|
|
759
791
|
Errors use the stable codes `INVALID_REQUEST` (nothing was sent),
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4713,7 +4713,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4713
4713
|
mentionActionsExecute: write,
|
|
4714
4714
|
taskClaim: write,
|
|
4715
4715
|
taskList: read,
|
|
4716
|
-
taskCreate:
|
|
4716
|
+
taskCreate: keyedWrite,
|
|
4717
4717
|
taskUnclaim: destructive,
|
|
4718
4718
|
taskAssign: naturalDestructive,
|
|
4719
4719
|
taskUpdateStatus: naturalDestructive,
|
|
@@ -4745,7 +4745,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4745
4745
|
integrationAppLogoUpdate: naturalDestructive,
|
|
4746
4746
|
integrationAppList: read,
|
|
4747
4747
|
integrationAppStatus: read,
|
|
4748
|
-
actionPrepare:
|
|
4748
|
+
actionPrepare: keyedWrite,
|
|
4749
4749
|
attachmentUpload: write,
|
|
4750
4750
|
attachmentUploadCapabilities: read,
|
|
4751
4751
|
attachmentUploadSessionCreate: write,
|
|
@@ -5396,7 +5396,15 @@ const agentApiTaskCreateBodySchema = passthroughObject({
|
|
|
5396
5396
|
title: string().trim().min(1),
|
|
5397
5397
|
creates_resource: boolean().optional()
|
|
5398
5398
|
})).min(1),
|
|
5399
|
-
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional()
|
|
5399
|
+
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional(),
|
|
5400
|
+
/**
|
|
5401
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5402
|
+
* the same request replays the first response without creating anything;
|
|
5403
|
+
* the same key with a different request is refused (409
|
|
5404
|
+
* `idempotency_key_reused`). Scoped to the agent and this route, and valid
|
|
5405
|
+
* for 24 hours; after that the key is forgotten and is a new request.
|
|
5406
|
+
*/
|
|
5407
|
+
idempotencyKey: optionalStringSchema
|
|
5400
5408
|
});
|
|
5401
5409
|
const agentApiTaskUnclaimBodySchema = passthroughObject({
|
|
5402
5410
|
channel: string().trim().min(1),
|
|
@@ -5647,7 +5655,15 @@ const agentApiIntegrationAppStatusQuerySchema = passthroughObject({
|
|
|
5647
5655
|
});
|
|
5648
5656
|
const agentApiActionPrepareBodySchema = passthroughObject({
|
|
5649
5657
|
target: string().trim().min(1),
|
|
5650
|
-
action: actionCardActionSchema
|
|
5658
|
+
action: actionCardActionSchema,
|
|
5659
|
+
/**
|
|
5660
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5661
|
+
* the same request replays the first response (same card messageId)
|
|
5662
|
+
* without preparing another card; the same key with a different request is
|
|
5663
|
+
* refused (409 `idempotency_key_reused`). Scoped to the agent and this
|
|
5664
|
+
* route, and valid for 24 hours; after that the key is forgotten.
|
|
5665
|
+
*/
|
|
5666
|
+
idempotencyKey: optionalStringSchema
|
|
5651
5667
|
});
|
|
5652
5668
|
const agentApiServerUpdateBodySchema = passthroughObject({
|
|
5653
5669
|
name: string().trim().min(1).max(100).optional(),
|
|
@@ -8728,7 +8744,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
8728
8744
|
"capability": "tasks",
|
|
8729
8745
|
"description": "Create one or more tasks in a channel.",
|
|
8730
8746
|
"sideEffect": "write",
|
|
8731
|
-
"idempotency": "
|
|
8747
|
+
"idempotency": "key",
|
|
8732
8748
|
"destructive": false,
|
|
8733
8749
|
"audience": "both",
|
|
8734
8750
|
"request": {
|
|
@@ -9528,7 +9544,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9528
9544
|
"capability": "tasks",
|
|
9529
9545
|
"description": "Prepare an action card for a human to commit.",
|
|
9530
9546
|
"sideEffect": "write",
|
|
9531
|
-
"idempotency": "
|
|
9547
|
+
"idempotency": "key",
|
|
9532
9548
|
"destructive": false,
|
|
9533
9549
|
"audience": "both",
|
|
9534
9550
|
"request": {
|
|
@@ -9843,7 +9859,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9843
9859
|
}
|
|
9844
9860
|
];
|
|
9845
9861
|
/** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
|
|
9846
|
-
const AGENT_API_MANIFEST_VERSION = "
|
|
9862
|
+
const AGENT_API_MANIFEST_VERSION = "2a3c2201ea57a9d1";
|
|
9847
9863
|
//#endregion
|
|
9848
9864
|
//#region src/routes.ts
|
|
9849
9865
|
const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
|
|
@@ -10449,7 +10465,7 @@ const DEFAULT_MESSAGES = {
|
|
|
10449
10465
|
SCOPE_DENIED: "This agent's scope set does not allow this operation.",
|
|
10450
10466
|
NOT_FOUND: "The target or message does not exist or is not visible to this agent.",
|
|
10451
10467
|
CONFLICT: "The Raft Server refused the operation because of the current state.",
|
|
10452
|
-
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different
|
|
10468
|
+
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different request.",
|
|
10453
10469
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10454
10470
|
UNAVAILABLE: "The Raft Server could not serve this operation right now.",
|
|
10455
10471
|
MODEL_ONLY: "This operation only counts when the model sees its result, so it cannot be run from code; nothing was sent."
|
|
@@ -10463,7 +10479,7 @@ const DEFAULT_NEXT_ACTION = {
|
|
|
10463
10479
|
SCOPE_DENIED: "Ask a human with editAgents authority to extend this agent's scopes.",
|
|
10464
10480
|
NOT_FOUND: "Check the target spelling with `raft server info --channels` or resolve the message id first.",
|
|
10465
10481
|
CONFLICT: "Read the current state before repeating this operation.",
|
|
10466
|
-
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different
|
|
10482
|
+
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different request, or resend the identical request to reconcile.",
|
|
10467
10483
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10468
10484
|
UNAVAILABLE: "Retry in a moment.",
|
|
10469
10485
|
MODEL_ONLY: "Call it as a model tool call instead of from code."
|
|
@@ -10552,6 +10568,24 @@ function failureOutcome(error) {
|
|
|
10552
10568
|
text: formatOpErrorText(error)
|
|
10553
10569
|
};
|
|
10554
10570
|
}
|
|
10571
|
+
/**
|
|
10572
|
+
* A failure of a keyed write (`idempotencyKey`). A retryable failure (the
|
|
10573
|
+
* request may not have reached the Server, or the Server was unavailable)
|
|
10574
|
+
* says to repeat the SAME request with the SAME key, and carries the key in
|
|
10575
|
+
* `next.args.idempotencyKey`, so a caller that let the SDK generate it can
|
|
10576
|
+
* still retry without acting twice.
|
|
10577
|
+
*/
|
|
10578
|
+
function keyedWriteFailure(failure, idempotencyKey) {
|
|
10579
|
+
if (!failure.error.retryable) return failure;
|
|
10580
|
+
return {
|
|
10581
|
+
...failure,
|
|
10582
|
+
next: {
|
|
10583
|
+
kind: "retry_same_key",
|
|
10584
|
+
args: { idempotencyKey },
|
|
10585
|
+
why: "Repeat the same request with this idempotencyKey: if the first attempt was committed, the Server returns its result instead of acting twice."
|
|
10586
|
+
}
|
|
10587
|
+
};
|
|
10588
|
+
}
|
|
10555
10589
|
/** Map a shared-client failure to a failure outcome. */
|
|
10556
10590
|
function failureFromClientResult(result) {
|
|
10557
10591
|
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
@@ -12267,21 +12301,24 @@ const createTasksRequestSchema = requestSchema()(object({
|
|
|
12267
12301
|
title: string().describe("Task title."),
|
|
12268
12302
|
createsResource: boolean().optional().describe("The task produces a resource (for example a document) that needs a receipt.")
|
|
12269
12303
|
})).describe("One entry per task to create."),
|
|
12270
|
-
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo.")
|
|
12304
|
+
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo."),
|
|
12305
|
+
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.")
|
|
12271
12306
|
}));
|
|
12272
12307
|
async function createTasks(client, request) {
|
|
12273
12308
|
const invalid = validateOpRequest(createTasksRequestSchema, request);
|
|
12274
12309
|
if (invalid) return invalid;
|
|
12275
12310
|
if (!request.target?.trim() || !request.tasks?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and at least one task title are required." }));
|
|
12311
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
12276
12312
|
const result = await client.tasks.create({
|
|
12277
12313
|
channel: request.target,
|
|
12278
12314
|
tasks: request.tasks.map((t) => ({
|
|
12279
12315
|
title: t.title,
|
|
12280
12316
|
...t.createsResource ? { creates_resource: true } : {}
|
|
12281
12317
|
})),
|
|
12282
|
-
...request.assignee ? { assignee: request.assignee } : {}
|
|
12318
|
+
...request.assignee ? { assignee: request.assignee } : {},
|
|
12319
|
+
idempotencyKey
|
|
12283
12320
|
});
|
|
12284
|
-
if (!result.ok) return failureFromClientResult(result);
|
|
12321
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
12285
12322
|
const data = result.data;
|
|
12286
12323
|
const first = data.tasks[0];
|
|
12287
12324
|
return {
|
|
@@ -12289,7 +12326,8 @@ async function createTasks(client, request) {
|
|
|
12289
12326
|
state: "created",
|
|
12290
12327
|
data: {
|
|
12291
12328
|
...data,
|
|
12292
|
-
target: request.target
|
|
12329
|
+
target: request.target,
|
|
12330
|
+
idempotencyKey
|
|
12293
12331
|
},
|
|
12294
12332
|
next: first ? {
|
|
12295
12333
|
kind: "post_in_task_thread",
|
|
@@ -14684,11 +14722,16 @@ async function prepareActionCard(client, request) {
|
|
|
14684
14722
|
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." }));
|
|
14685
14723
|
const invalid = validateOpRequest(prepareActionCardRequestSchema, request);
|
|
14686
14724
|
if (invalid) return invalid;
|
|
14687
|
-
const
|
|
14688
|
-
|
|
14725
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
14726
|
+
const result = await client.actions.prepare({
|
|
14727
|
+
...request,
|
|
14728
|
+
idempotencyKey
|
|
14729
|
+
});
|
|
14730
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
14689
14731
|
const card = {
|
|
14690
14732
|
target: request.target,
|
|
14691
|
-
messageId: result.data.messageId
|
|
14733
|
+
messageId: result.data.messageId,
|
|
14734
|
+
idempotencyKey
|
|
14692
14735
|
};
|
|
14693
14736
|
return {
|
|
14694
14737
|
ok: true,
|
|
@@ -15149,9 +15192,11 @@ const OPERATION_DEFS = [
|
|
|
15149
15192
|
schema: prepareActionCardRequestSchema,
|
|
15150
15193
|
fieldDescriptions: {
|
|
15151
15194
|
target: "Conversation to post the card in: `#channel`, `dm:@peer`, or a thread.",
|
|
15152
|
-
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described."
|
|
15195
|
+
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described.",
|
|
15196
|
+
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."
|
|
15153
15197
|
},
|
|
15154
15198
|
routes: ["actionPrepare"],
|
|
15199
|
+
idempotencyArg: "idempotencyKey",
|
|
15155
15200
|
consumes: nothing,
|
|
15156
15201
|
output: small
|
|
15157
15202
|
},
|
|
@@ -15202,6 +15247,7 @@ const OPERATION_DEFS = [
|
|
|
15202
15247
|
description: "Create one or more tasks on a channel's board; each gets its own thread.",
|
|
15203
15248
|
schema: createTasksRequestSchema,
|
|
15204
15249
|
routes: ["taskCreate"],
|
|
15250
|
+
idempotencyArg: "idempotencyKey",
|
|
15205
15251
|
consumes: nothing,
|
|
15206
15252
|
output: small
|
|
15207
15253
|
},
|
package/dist/esm/index.js
CHANGED
|
@@ -4712,7 +4712,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4712
4712
|
mentionActionsExecute: write,
|
|
4713
4713
|
taskClaim: write,
|
|
4714
4714
|
taskList: read,
|
|
4715
|
-
taskCreate:
|
|
4715
|
+
taskCreate: keyedWrite,
|
|
4716
4716
|
taskUnclaim: destructive,
|
|
4717
4717
|
taskAssign: naturalDestructive,
|
|
4718
4718
|
taskUpdateStatus: naturalDestructive,
|
|
@@ -4744,7 +4744,7 @@ const AGENT_API_ROUTE_META = {
|
|
|
4744
4744
|
integrationAppLogoUpdate: naturalDestructive,
|
|
4745
4745
|
integrationAppList: read,
|
|
4746
4746
|
integrationAppStatus: read,
|
|
4747
|
-
actionPrepare:
|
|
4747
|
+
actionPrepare: keyedWrite,
|
|
4748
4748
|
attachmentUpload: write,
|
|
4749
4749
|
attachmentUploadCapabilities: read,
|
|
4750
4750
|
attachmentUploadSessionCreate: write,
|
|
@@ -5395,7 +5395,15 @@ const agentApiTaskCreateBodySchema = passthroughObject({
|
|
|
5395
5395
|
title: string().trim().min(1),
|
|
5396
5396
|
creates_resource: boolean().optional()
|
|
5397
5397
|
})).min(1),
|
|
5398
|
-
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional()
|
|
5398
|
+
assignee: string().trim().refine((value) => value.startsWith("@") && value.slice(1).trim().length > 0, { message: "assignee must be an @handle" }).optional(),
|
|
5399
|
+
/**
|
|
5400
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5401
|
+
* the same request replays the first response without creating anything;
|
|
5402
|
+
* the same key with a different request is refused (409
|
|
5403
|
+
* `idempotency_key_reused`). Scoped to the agent and this route, and valid
|
|
5404
|
+
* for 24 hours; after that the key is forgotten and is a new request.
|
|
5405
|
+
*/
|
|
5406
|
+
idempotencyKey: optionalStringSchema
|
|
5399
5407
|
});
|
|
5400
5408
|
const agentApiTaskUnclaimBodySchema = passthroughObject({
|
|
5401
5409
|
channel: string().trim().min(1),
|
|
@@ -5646,7 +5654,15 @@ const agentApiIntegrationAppStatusQuerySchema = passthroughObject({
|
|
|
5646
5654
|
});
|
|
5647
5655
|
const agentApiActionPrepareBodySchema = passthroughObject({
|
|
5648
5656
|
target: string().trim().min(1),
|
|
5649
|
-
action: actionCardActionSchema
|
|
5657
|
+
action: actionCardActionSchema,
|
|
5658
|
+
/**
|
|
5659
|
+
* Retry key (same rules as message send's): a repeat with the same key and
|
|
5660
|
+
* the same request replays the first response (same card messageId)
|
|
5661
|
+
* without preparing another card; the same key with a different request is
|
|
5662
|
+
* refused (409 `idempotency_key_reused`). Scoped to the agent and this
|
|
5663
|
+
* route, and valid for 24 hours; after that the key is forgotten.
|
|
5664
|
+
*/
|
|
5665
|
+
idempotencyKey: optionalStringSchema
|
|
5650
5666
|
});
|
|
5651
5667
|
const agentApiServerUpdateBodySchema = passthroughObject({
|
|
5652
5668
|
name: string().trim().min(1).max(100).optional(),
|
|
@@ -8727,7 +8743,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
8727
8743
|
"capability": "tasks",
|
|
8728
8744
|
"description": "Create one or more tasks in a channel.",
|
|
8729
8745
|
"sideEffect": "write",
|
|
8730
|
-
"idempotency": "
|
|
8746
|
+
"idempotency": "key",
|
|
8731
8747
|
"destructive": false,
|
|
8732
8748
|
"audience": "both",
|
|
8733
8749
|
"request": {
|
|
@@ -9527,7 +9543,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9527
9543
|
"capability": "tasks",
|
|
9528
9544
|
"description": "Prepare an action card for a human to commit.",
|
|
9529
9545
|
"sideEffect": "write",
|
|
9530
|
-
"idempotency": "
|
|
9546
|
+
"idempotency": "key",
|
|
9531
9547
|
"destructive": false,
|
|
9532
9548
|
"audience": "both",
|
|
9533
9549
|
"request": {
|
|
@@ -9842,7 +9858,7 @@ const AGENT_API_ROUTE_MANIFEST = [
|
|
|
9842
9858
|
}
|
|
9843
9859
|
];
|
|
9844
9860
|
/** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
|
|
9845
|
-
const AGENT_API_MANIFEST_VERSION = "
|
|
9861
|
+
const AGENT_API_MANIFEST_VERSION = "2a3c2201ea57a9d1";
|
|
9846
9862
|
//#endregion
|
|
9847
9863
|
//#region src/routes.ts
|
|
9848
9864
|
const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
|
|
@@ -10448,7 +10464,7 @@ const DEFAULT_MESSAGES = {
|
|
|
10448
10464
|
SCOPE_DENIED: "This agent's scope set does not allow this operation.",
|
|
10449
10465
|
NOT_FOUND: "The target or message does not exist or is not visible to this agent.",
|
|
10450
10466
|
CONFLICT: "The Raft Server refused the operation because of the current state.",
|
|
10451
|
-
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different
|
|
10467
|
+
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different request.",
|
|
10452
10468
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10453
10469
|
UNAVAILABLE: "The Raft Server could not serve this operation right now.",
|
|
10454
10470
|
MODEL_ONLY: "This operation only counts when the model sees its result, so it cannot be run from code; nothing was sent."
|
|
@@ -10462,7 +10478,7 @@ const DEFAULT_NEXT_ACTION = {
|
|
|
10462
10478
|
SCOPE_DENIED: "Ask a human with editAgents authority to extend this agent's scopes.",
|
|
10463
10479
|
NOT_FOUND: "Check the target spelling with `raft server info --channels` or resolve the message id first.",
|
|
10464
10480
|
CONFLICT: "Read the current state before repeating this operation.",
|
|
10465
|
-
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different
|
|
10481
|
+
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different request, or resend the identical request to reconcile.",
|
|
10466
10482
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10467
10483
|
UNAVAILABLE: "Retry in a moment.",
|
|
10468
10484
|
MODEL_ONLY: "Call it as a model tool call instead of from code."
|
|
@@ -10551,6 +10567,24 @@ function failureOutcome(error) {
|
|
|
10551
10567
|
text: formatOpErrorText(error)
|
|
10552
10568
|
};
|
|
10553
10569
|
}
|
|
10570
|
+
/**
|
|
10571
|
+
* A failure of a keyed write (`idempotencyKey`). A retryable failure (the
|
|
10572
|
+
* request may not have reached the Server, or the Server was unavailable)
|
|
10573
|
+
* says to repeat the SAME request with the SAME key, and carries the key in
|
|
10574
|
+
* `next.args.idempotencyKey`, so a caller that let the SDK generate it can
|
|
10575
|
+
* still retry without acting twice.
|
|
10576
|
+
*/
|
|
10577
|
+
function keyedWriteFailure(failure, idempotencyKey) {
|
|
10578
|
+
if (!failure.error.retryable) return failure;
|
|
10579
|
+
return {
|
|
10580
|
+
...failure,
|
|
10581
|
+
next: {
|
|
10582
|
+
kind: "retry_same_key",
|
|
10583
|
+
args: { idempotencyKey },
|
|
10584
|
+
why: "Repeat the same request with this idempotencyKey: if the first attempt was committed, the Server returns its result instead of acting twice."
|
|
10585
|
+
}
|
|
10586
|
+
};
|
|
10587
|
+
}
|
|
10554
10588
|
/** Map a shared-client failure to a failure outcome. */
|
|
10555
10589
|
function failureFromClientResult(result) {
|
|
10556
10590
|
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
@@ -12266,21 +12300,24 @@ const createTasksRequestSchema = requestSchema()(object({
|
|
|
12266
12300
|
title: string().describe("Task title."),
|
|
12267
12301
|
createsResource: boolean().optional().describe("The task produces a resource (for example a document) that needs a receipt.")
|
|
12268
12302
|
})).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.")
|
|
12303
|
+
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo."),
|
|
12304
|
+
idempotencyKey: string().optional().describe("One key per logical create; generated when omitted and returned. Repeat the same request with the same key within 24 hours to retry without creating the tasks twice.")
|
|
12270
12305
|
}));
|
|
12271
12306
|
async function createTasks(client, request) {
|
|
12272
12307
|
const invalid = validateOpRequest(createTasksRequestSchema, request);
|
|
12273
12308
|
if (invalid) return invalid;
|
|
12274
12309
|
if (!request.target?.trim() || !request.tasks?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and at least one task title are required." }));
|
|
12310
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
12275
12311
|
const result = await client.tasks.create({
|
|
12276
12312
|
channel: request.target,
|
|
12277
12313
|
tasks: request.tasks.map((t) => ({
|
|
12278
12314
|
title: t.title,
|
|
12279
12315
|
...t.createsResource ? { creates_resource: true } : {}
|
|
12280
12316
|
})),
|
|
12281
|
-
...request.assignee ? { assignee: request.assignee } : {}
|
|
12317
|
+
...request.assignee ? { assignee: request.assignee } : {},
|
|
12318
|
+
idempotencyKey
|
|
12282
12319
|
});
|
|
12283
|
-
if (!result.ok) return failureFromClientResult(result);
|
|
12320
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
12284
12321
|
const data = result.data;
|
|
12285
12322
|
const first = data.tasks[0];
|
|
12286
12323
|
return {
|
|
@@ -12288,7 +12325,8 @@ async function createTasks(client, request) {
|
|
|
12288
12325
|
state: "created",
|
|
12289
12326
|
data: {
|
|
12290
12327
|
...data,
|
|
12291
|
-
target: request.target
|
|
12328
|
+
target: request.target,
|
|
12329
|
+
idempotencyKey
|
|
12292
12330
|
},
|
|
12293
12331
|
next: first ? {
|
|
12294
12332
|
kind: "post_in_task_thread",
|
|
@@ -14683,11 +14721,16 @@ async function prepareActionCard(client, request) {
|
|
|
14683
14721
|
if (typeof request?.target !== "string" || !request.target.trim() || !request.action) return failureOutcome(opError("INVALID_REQUEST", { message: "A target and an action are required to prepare a card." }));
|
|
14684
14722
|
const invalid = validateOpRequest(prepareActionCardRequestSchema, request);
|
|
14685
14723
|
if (invalid) return invalid;
|
|
14686
|
-
const
|
|
14687
|
-
|
|
14724
|
+
const idempotencyKey = request.idempotencyKey?.trim() || globalThis.crypto.randomUUID();
|
|
14725
|
+
const result = await client.actions.prepare({
|
|
14726
|
+
...request,
|
|
14727
|
+
idempotencyKey
|
|
14728
|
+
});
|
|
14729
|
+
if (!result.ok) return keyedWriteFailure(failureFromClientResult(result), idempotencyKey);
|
|
14688
14730
|
const card = {
|
|
14689
14731
|
target: request.target,
|
|
14690
|
-
messageId: result.data.messageId
|
|
14732
|
+
messageId: result.data.messageId,
|
|
14733
|
+
idempotencyKey
|
|
14691
14734
|
};
|
|
14692
14735
|
return {
|
|
14693
14736
|
ok: true,
|
|
@@ -15148,9 +15191,11 @@ const OPERATION_DEFS = [
|
|
|
15148
15191
|
schema: prepareActionCardRequestSchema,
|
|
15149
15192
|
fieldDescriptions: {
|
|
15150
15193
|
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."
|
|
15194
|
+
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described.",
|
|
15195
|
+
idempotencyKey: "One key per logical prepare; generated when omitted and returned. Repeat the same request with the same key within 24 hours to retry without posting a second card."
|
|
15152
15196
|
},
|
|
15153
15197
|
routes: ["actionPrepare"],
|
|
15198
|
+
idempotencyArg: "idempotencyKey",
|
|
15154
15199
|
consumes: nothing,
|
|
15155
15200
|
output: small
|
|
15156
15201
|
},
|
|
@@ -15201,6 +15246,7 @@ const OPERATION_DEFS = [
|
|
|
15201
15246
|
description: "Create one or more tasks on a channel's board; each gets its own thread.",
|
|
15202
15247
|
schema: createTasksRequestSchema,
|
|
15203
15248
|
routes: ["taskCreate"],
|
|
15249
|
+
idempotencyArg: "idempotencyKey",
|
|
15204
15250
|
consumes: nothing,
|
|
15205
15251
|
output: small
|
|
15206
15252
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -878,6 +878,19 @@ interface CreateTasksRequest {
|
|
|
878
878
|
}>;
|
|
879
879
|
/** `@handle`; yourself to start in_progress, or (owner/admin) someone else to reserve a todo. */
|
|
880
880
|
assignee?: string;
|
|
881
|
+
/**
|
|
882
|
+
* One key per logical create. Generated with `crypto.randomUUID()` when
|
|
883
|
+
* omitted and returned as `data.idempotencyKey` (and, on a retryable
|
|
884
|
+
* failure, as `next.args.idempotencyKey`). Repeating the same request with
|
|
885
|
+
* the same key returns the first result (same task numbers) and creates
|
|
886
|
+
* nothing; the same key with a different request fails with
|
|
887
|
+
* `IDEMPOTENCY_KEY_REUSED`. A key is valid for 24 hours: retry within that
|
|
888
|
+
* window; after it the Server forgets the key and the same request creates
|
|
889
|
+
* again. The SDK never retries on its own: Servers
|
|
890
|
+
* without keyed task create ignore the key, and a repeat there creates the
|
|
891
|
+
* tasks again.
|
|
892
|
+
*/
|
|
893
|
+
idempotencyKey?: string;
|
|
881
894
|
}
|
|
882
895
|
export declare const createTasksRequestSchema: z.ZodObject<{
|
|
883
896
|
target: z.ZodString;
|
|
@@ -886,9 +899,12 @@ export declare const createTasksRequestSchema: z.ZodObject<{
|
|
|
886
899
|
createsResource: z.ZodOptional<z.ZodBoolean>;
|
|
887
900
|
}, z.core.$strip>>;
|
|
888
901
|
assignee: z.ZodOptional<z.ZodString>;
|
|
902
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
889
903
|
}, z.core.$strip>;
|
|
890
904
|
type RaftTasksCreated = AgentApiTaskCreateResponse & {
|
|
891
905
|
target: string;
|
|
906
|
+
/** The key this create was sent with (the caller's, or the generated one). */
|
|
907
|
+
idempotencyKey: string;
|
|
892
908
|
};
|
|
893
909
|
interface TaskRef {
|
|
894
910
|
target: string;
|
|
@@ -1330,6 +1346,15 @@ export declare function parseRaftState(value: unknown): RaftState | null;
|
|
|
1330
1346
|
export declare function hashRaftSendContent(target: string, content: string, attachmentIds?: readonly string[]): Promise<string>;
|
|
1331
1347
|
//#endregion
|
|
1332
1348
|
//#region ../shared/src/agentOps/actions.d.ts
|
|
1349
|
+
/**
|
|
1350
|
+
* `idempotencyKey`: one key per logical prepare. Generated with
|
|
1351
|
+
* `crypto.randomUUID()` when omitted and returned as `data.idempotencyKey`
|
|
1352
|
+
* (and, on a retryable failure, as `next.args.idempotencyKey`). Repeating the
|
|
1353
|
+
* same request with the same key returns the first card (same `messageId`)
|
|
1354
|
+
* and posts nothing; the same key with a different request fails with
|
|
1355
|
+
* `IDEMPOTENCY_KEY_REUSED`. A key is valid for 24 hours; after that the
|
|
1356
|
+
* Server forgets it. Servers without keyed prepare ignore the key.
|
|
1357
|
+
*/
|
|
1333
1358
|
type PrepareActionCardRequest = AgentApiActionPrepareBody;
|
|
1334
1359
|
/**
|
|
1335
1360
|
* The Agent API's own body schema (a discriminated union over the card
|
|
@@ -1412,11 +1437,14 @@ export declare const prepareActionCardRequestSchema: import("zod").ZodObject<{
|
|
|
1412
1437
|
targetAgent: import("zod").ZodString;
|
|
1413
1438
|
draftHint: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1414
1439
|
}, import("zod/v4/core").$strip>], "type">;
|
|
1440
|
+
idempotencyKey: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1415
1441
|
}, import("zod/v4/core").$loose>;
|
|
1416
1442
|
interface RaftPreparedCard {
|
|
1417
1443
|
target: string;
|
|
1418
1444
|
/** The card message; a human commits it by clicking its action verb. */
|
|
1419
1445
|
messageId: string;
|
|
1446
|
+
/** The key this prepare was sent with (the caller's `idempotencyKey`, or the generated one). */
|
|
1447
|
+
idempotencyKey: string;
|
|
1420
1448
|
}
|
|
1421
1449
|
//#endregion
|
|
1422
1450
|
//#region ../shared/src/agentApiRawClient.d.ts
|
|
@@ -2112,6 +2140,7 @@ declare const agentApiTaskCreateBodySchema: z.ZodObject<{
|
|
|
2112
2140
|
creates_resource: z.ZodOptional<z.ZodBoolean>;
|
|
2113
2141
|
}, z.core.$loose>>;
|
|
2114
2142
|
assignee: z.ZodOptional<z.ZodString>;
|
|
2143
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
2115
2144
|
}, z.core.$loose>;
|
|
2116
2145
|
declare const agentApiTaskUnclaimBodySchema: z.ZodObject<{
|
|
2117
2146
|
channel: z.ZodString;
|
|
@@ -2393,6 +2422,7 @@ declare const agentApiActionPrepareBodySchema: z.ZodObject<{
|
|
|
2393
2422
|
targetAgent: z.ZodString;
|
|
2394
2423
|
draftHint: z.ZodOptional<z.ZodString>;
|
|
2395
2424
|
}, z.core.$strip>], "type">;
|
|
2425
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
2396
2426
|
}, z.core.$loose>;
|
|
2397
2427
|
declare const agentApiServerUpdateBodySchema: z.ZodObject<{
|
|
2398
2428
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -6151,6 +6181,7 @@ declare const agentApiContract: {
|
|
|
6151
6181
|
creates_resource: z.ZodOptional<z.ZodBoolean>;
|
|
6152
6182
|
}, z.core.$loose>>;
|
|
6153
6183
|
assignee: z.ZodOptional<z.ZodString>;
|
|
6184
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
6154
6185
|
}, z.core.$loose>;
|
|
6155
6186
|
};
|
|
6156
6187
|
readonly response: {
|
|
@@ -7993,6 +8024,7 @@ declare const agentApiContract: {
|
|
|
7993
8024
|
targetAgent: z.ZodString;
|
|
7994
8025
|
draftHint: z.ZodOptional<z.ZodString>;
|
|
7995
8026
|
}, z.core.$strip>], "type">;
|
|
8027
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
7996
8028
|
}, z.core.$loose>;
|
|
7997
8029
|
};
|
|
7998
8030
|
readonly response: {
|
|
@@ -9908,12 +9940,15 @@ declare const OPERATION_DEFS: readonly [{
|
|
|
9908
9940
|
targetAgent: z.ZodString;
|
|
9909
9941
|
draftHint: z.ZodOptional<z.ZodString>;
|
|
9910
9942
|
}, z.core.$strip>], "type">;
|
|
9943
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
9911
9944
|
}, z.core.$loose>;
|
|
9912
9945
|
readonly fieldDescriptions: {
|
|
9913
9946
|
readonly target: "Conversation to post the card in: `#channel`, `dm:@peer`, or a thread.";
|
|
9914
9947
|
readonly action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described.";
|
|
9948
|
+
readonly 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.";
|
|
9915
9949
|
};
|
|
9916
9950
|
readonly routes: readonly ["actionPrepare"];
|
|
9951
|
+
readonly idempotencyArg: "idempotencyKey";
|
|
9917
9952
|
readonly consumes: {
|
|
9918
9953
|
model: never[];
|
|
9919
9954
|
code: never[];
|
|
@@ -10009,8 +10044,10 @@ declare const OPERATION_DEFS: readonly [{
|
|
|
10009
10044
|
createsResource: z.ZodOptional<z.ZodBoolean>;
|
|
10010
10045
|
}, z.core.$strip>>;
|
|
10011
10046
|
assignee: z.ZodOptional<z.ZodString>;
|
|
10047
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
10012
10048
|
}, z.core.$strip>;
|
|
10013
10049
|
readonly routes: readonly ["taskCreate"];
|
|
10050
|
+
readonly idempotencyArg: "idempotencyKey";
|
|
10014
10051
|
readonly consumes: {
|
|
10015
10052
|
model: never[];
|
|
10016
10053
|
code: never[];
|
package/operations.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "raft-sdk-operations.v1",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "c2d4e5cdeb300d10",
|
|
4
4
|
"operations": [
|
|
5
5
|
{
|
|
6
6
|
"name": "identity.whoami",
|
|
@@ -961,6 +961,10 @@
|
|
|
961
961
|
"type"
|
|
962
962
|
],
|
|
963
963
|
"description": "The operation the card proposes: `type` picks it, and the other fields apply per type as described."
|
|
964
|
+
},
|
|
965
|
+
"idempotencyKey": {
|
|
966
|
+
"type": "string",
|
|
967
|
+
"description": "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."
|
|
964
968
|
}
|
|
965
969
|
},
|
|
966
970
|
"required": [
|
|
@@ -970,7 +974,8 @@
|
|
|
970
974
|
},
|
|
971
975
|
"sideEffect": "write",
|
|
972
976
|
"idempotency": {
|
|
973
|
-
"kind": "
|
|
977
|
+
"kind": "key",
|
|
978
|
+
"arg": "idempotencyKey"
|
|
974
979
|
},
|
|
975
980
|
"capability": [
|
|
976
981
|
"tasks"
|
|
@@ -1218,6 +1223,10 @@
|
|
|
1218
1223
|
"assignee": {
|
|
1219
1224
|
"type": "string",
|
|
1220
1225
|
"description": "`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo."
|
|
1226
|
+
},
|
|
1227
|
+
"idempotencyKey": {
|
|
1228
|
+
"type": "string",
|
|
1229
|
+
"description": "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."
|
|
1221
1230
|
}
|
|
1222
1231
|
},
|
|
1223
1232
|
"required": [
|
|
@@ -1227,7 +1236,8 @@
|
|
|
1227
1236
|
},
|
|
1228
1237
|
"sideEffect": "write",
|
|
1229
1238
|
"idempotency": {
|
|
1230
|
-
"kind": "
|
|
1239
|
+
"kind": "key",
|
|
1240
|
+
"arg": "idempotencyKey"
|
|
1231
1241
|
},
|
|
1232
1242
|
"capability": [
|
|
1233
1243
|
"tasks"
|