@botiverse/raft-sdk 0.2.0 → 1.0.0-alpha.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 +150 -8
- package/dist/cjs/index.cjs +4268 -416
- package/dist/esm/index.js +4260 -417
- package/dist/index.d.ts +8211 -54
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -4440,6 +4440,184 @@ function createAgentApiMessageClient(options) {
|
|
|
4440
4440
|
} };
|
|
4441
4441
|
}
|
|
4442
4442
|
//#endregion
|
|
4443
|
+
//#region ../shared/src/agentApiRouteMeta.ts
|
|
4444
|
+
const read = {
|
|
4445
|
+
sideEffect: "read",
|
|
4446
|
+
idempotency: "natural",
|
|
4447
|
+
destructive: false,
|
|
4448
|
+
audience: "both"
|
|
4449
|
+
};
|
|
4450
|
+
const naturalWrite = {
|
|
4451
|
+
sideEffect: "write",
|
|
4452
|
+
idempotency: "natural",
|
|
4453
|
+
destructive: false,
|
|
4454
|
+
audience: "both"
|
|
4455
|
+
};
|
|
4456
|
+
const keyedWrite = {
|
|
4457
|
+
sideEffect: "write",
|
|
4458
|
+
idempotency: "key",
|
|
4459
|
+
destructive: false,
|
|
4460
|
+
audience: "both"
|
|
4461
|
+
};
|
|
4462
|
+
const write = {
|
|
4463
|
+
sideEffect: "write",
|
|
4464
|
+
idempotency: "none",
|
|
4465
|
+
destructive: false,
|
|
4466
|
+
audience: "both"
|
|
4467
|
+
};
|
|
4468
|
+
const naturalDestructive = {
|
|
4469
|
+
sideEffect: "write",
|
|
4470
|
+
idempotency: "natural",
|
|
4471
|
+
destructive: true,
|
|
4472
|
+
audience: "both"
|
|
4473
|
+
};
|
|
4474
|
+
const destructive = {
|
|
4475
|
+
sideEffect: "write",
|
|
4476
|
+
idempotency: "none",
|
|
4477
|
+
destructive: true,
|
|
4478
|
+
audience: "both"
|
|
4479
|
+
};
|
|
4480
|
+
const managedNaturalWrite = {
|
|
4481
|
+
sideEffect: "write",
|
|
4482
|
+
idempotency: "natural",
|
|
4483
|
+
destructive: false,
|
|
4484
|
+
audience: "managed"
|
|
4485
|
+
};
|
|
4486
|
+
const managedWrite = {
|
|
4487
|
+
sideEffect: "write",
|
|
4488
|
+
idempotency: "none",
|
|
4489
|
+
destructive: false,
|
|
4490
|
+
audience: "managed"
|
|
4491
|
+
};
|
|
4492
|
+
const managedNaturalDestructive = {
|
|
4493
|
+
sideEffect: "write",
|
|
4494
|
+
idempotency: "natural",
|
|
4495
|
+
destructive: true,
|
|
4496
|
+
audience: "managed"
|
|
4497
|
+
};
|
|
4498
|
+
const AGENT_API_ROUTE_META = {
|
|
4499
|
+
feedbackLocatorIngest: naturalWrite,
|
|
4500
|
+
feedbackLocatorList: read,
|
|
4501
|
+
events: {
|
|
4502
|
+
sideEffect: "destructive_read",
|
|
4503
|
+
idempotency: "none",
|
|
4504
|
+
destructive: true,
|
|
4505
|
+
audience: "both"
|
|
4506
|
+
},
|
|
4507
|
+
historyRead: read,
|
|
4508
|
+
knowledgeGet: read,
|
|
4509
|
+
knowledgeSearch: read,
|
|
4510
|
+
managedMcpTools: read,
|
|
4511
|
+
managedMcpCall: destructive,
|
|
4512
|
+
messageSend: keyedWrite,
|
|
4513
|
+
messageSendV2: keyedWrite,
|
|
4514
|
+
messageResolve: read,
|
|
4515
|
+
messageSearch: read,
|
|
4516
|
+
messageReactionAdd: naturalWrite,
|
|
4517
|
+
messageReactionRemove: naturalDestructive,
|
|
4518
|
+
channelJoin: naturalWrite,
|
|
4519
|
+
channelLeave: naturalDestructive,
|
|
4520
|
+
channelMute: naturalWrite,
|
|
4521
|
+
channelUnmute: naturalWrite,
|
|
4522
|
+
channelArchive: naturalDestructive,
|
|
4523
|
+
channelUnarchive: naturalWrite,
|
|
4524
|
+
channelMembers: read,
|
|
4525
|
+
resolveChannel: read,
|
|
4526
|
+
threadUnfollow: naturalDestructive,
|
|
4527
|
+
threadList: read,
|
|
4528
|
+
inboxList: read,
|
|
4529
|
+
agentContext: read,
|
|
4530
|
+
serverInfo: read,
|
|
4531
|
+
serverUpdate: naturalDestructive,
|
|
4532
|
+
mentionActionsPending: read,
|
|
4533
|
+
senderMentionDeliveries: read,
|
|
4534
|
+
mentionActionsExecute: write,
|
|
4535
|
+
taskClaim: write,
|
|
4536
|
+
taskList: read,
|
|
4537
|
+
taskCreate: write,
|
|
4538
|
+
taskUnclaim: destructive,
|
|
4539
|
+
taskAssign: naturalDestructive,
|
|
4540
|
+
taskUpdateStatus: naturalDestructive,
|
|
4541
|
+
taskResourceReceipt: write,
|
|
4542
|
+
taskDelete: destructive,
|
|
4543
|
+
taskConvert: write,
|
|
4544
|
+
taskAmend: destructive,
|
|
4545
|
+
taskHistory: read,
|
|
4546
|
+
migrationBegin: {
|
|
4547
|
+
sideEffect: "write",
|
|
4548
|
+
idempotency: "none",
|
|
4549
|
+
destructive: true,
|
|
4550
|
+
audience: "managed"
|
|
4551
|
+
},
|
|
4552
|
+
migrationStatus: read,
|
|
4553
|
+
migrationReady: managedNaturalWrite,
|
|
4554
|
+
migrationArrived: managedNaturalWrite,
|
|
4555
|
+
reminderList: read,
|
|
4556
|
+
reminderCreate: managedWrite,
|
|
4557
|
+
reminderCancel: naturalDestructive,
|
|
4558
|
+
reminderSnooze: managedNaturalDestructive,
|
|
4559
|
+
reminderUpdate: managedNaturalDestructive,
|
|
4560
|
+
appSourceAck: naturalWrite,
|
|
4561
|
+
reminderLog: read,
|
|
4562
|
+
appConfigGet: read,
|
|
4563
|
+
appConfigPatch: destructive,
|
|
4564
|
+
profileShow: read,
|
|
4565
|
+
profileUpdate: naturalDestructive,
|
|
4566
|
+
profileAvatarUpdate: naturalDestructive,
|
|
4567
|
+
integrationList: read,
|
|
4568
|
+
integrationMarketplaceSearch: read,
|
|
4569
|
+
integrationLogin: naturalWrite,
|
|
4570
|
+
integrationAppPrepare: write,
|
|
4571
|
+
integrationAppRotateSecret: destructive,
|
|
4572
|
+
integrationAppTransferOwner: naturalDestructive,
|
|
4573
|
+
integrationAppUpdate: naturalDestructive,
|
|
4574
|
+
integrationAppManage: destructive,
|
|
4575
|
+
integrationAppLogoUpdate: naturalDestructive,
|
|
4576
|
+
integrationAppList: read,
|
|
4577
|
+
integrationAppStatus: read,
|
|
4578
|
+
actionPrepare: write,
|
|
4579
|
+
attachmentUpload: write,
|
|
4580
|
+
attachmentUploadCapabilities: read,
|
|
4581
|
+
attachmentUploadSessionCreate: write,
|
|
4582
|
+
attachmentUploadSessionComplete: naturalWrite,
|
|
4583
|
+
attachmentUploadSessionCancel: naturalDestructive,
|
|
4584
|
+
attachmentUploadSessionStatus: read,
|
|
4585
|
+
attachmentDownload: read,
|
|
4586
|
+
attachmentCommentsList: read,
|
|
4587
|
+
pushWebhookStatus: {
|
|
4588
|
+
sideEffect: "read",
|
|
4589
|
+
idempotency: "natural",
|
|
4590
|
+
destructive: false,
|
|
4591
|
+
audience: "external"
|
|
4592
|
+
},
|
|
4593
|
+
pushWebhookRegister: {
|
|
4594
|
+
sideEffect: "write",
|
|
4595
|
+
idempotency: "natural",
|
|
4596
|
+
destructive: true,
|
|
4597
|
+
audience: "external"
|
|
4598
|
+
},
|
|
4599
|
+
pushWebhookDelete: {
|
|
4600
|
+
sideEffect: "write",
|
|
4601
|
+
idempotency: "natural",
|
|
4602
|
+
destructive: true,
|
|
4603
|
+
audience: "external"
|
|
4604
|
+
},
|
|
4605
|
+
mentionsList: read
|
|
4606
|
+
};
|
|
4607
|
+
function toAgentApiToolAnnotations(meta) {
|
|
4608
|
+
return {
|
|
4609
|
+
readOnlyHint: meta.sideEffect === "read",
|
|
4610
|
+
destructiveHint: meta.destructive,
|
|
4611
|
+
idempotentHint: meta.idempotency !== "none"
|
|
4612
|
+
};
|
|
4613
|
+
}
|
|
4614
|
+
function getAgentApiRetryPolicy(meta) {
|
|
4615
|
+
if (meta.sideEffect === "read") return "retry";
|
|
4616
|
+
if (meta.sideEffect === "write" && meta.idempotency === "key") return "retry_when_keyed";
|
|
4617
|
+
if (meta.sideEffect === "write" && meta.idempotency === "natural") return "retry";
|
|
4618
|
+
return "single_attempt";
|
|
4619
|
+
}
|
|
4620
|
+
//#endregion
|
|
4443
4621
|
//#region ../shared/src/actionCards.ts
|
|
4444
4622
|
const uuidSchema = uuid();
|
|
4445
4623
|
/**
|
|
@@ -4819,7 +4997,8 @@ const taskStatusSchema = _enum([
|
|
|
4819
4997
|
]);
|
|
4820
4998
|
const agentApiEventsQuerySchema = passthroughObject({
|
|
4821
4999
|
since: optionalStringSchema,
|
|
4822
|
-
limit: optionalStringSchema
|
|
5000
|
+
limit: optionalStringSchema,
|
|
5001
|
+
ack: _enum(["cursor", "immediate"]).optional()
|
|
4823
5002
|
});
|
|
4824
5003
|
const agentApiHistoryQuerySchema = passthroughObject({
|
|
4825
5004
|
channel: string().trim().min(1),
|
|
@@ -5623,15 +5802,18 @@ const agentApiEventsResponseSchema = passthroughObject({
|
|
|
5623
5802
|
pending_notice_ids: array(string()).optional(),
|
|
5624
5803
|
wake_reason: string().nullable().optional(),
|
|
5625
5804
|
has_more: boolean(),
|
|
5626
|
-
inbox_hint: agentApiEventsInboxHintSchema.nullable().optional()
|
|
5805
|
+
inbox_hint: agentApiEventsInboxHintSchema.nullable().optional(),
|
|
5806
|
+
ack_mode: _enum(["cursor", "immediate"]).optional()
|
|
5807
|
+
});
|
|
5808
|
+
const agentApiHistoryConsumptionScopeSchema = object({
|
|
5809
|
+
agent_id: string().uuid(),
|
|
5810
|
+
channel_id: string().uuid(),
|
|
5811
|
+
channel_type: _enum(["dm", "thread"]),
|
|
5812
|
+
target: string().min(1)
|
|
5627
5813
|
});
|
|
5628
5814
|
const agentApiHistoryResponseSchema = passthroughObject({
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
channel_id: string().uuid(),
|
|
5632
|
-
channel_type: _enum(["dm", "thread"]),
|
|
5633
|
-
target: string().min(1)
|
|
5634
|
-
}).optional(),
|
|
5815
|
+
target: string().min(1).optional(),
|
|
5816
|
+
consumption_scope: agentApiHistoryConsumptionScopeSchema.optional(),
|
|
5635
5817
|
messages: array(agentApiMessageEnvelopeSchema),
|
|
5636
5818
|
has_more: boolean(),
|
|
5637
5819
|
has_older: boolean(),
|
|
@@ -6002,6 +6184,45 @@ const agentApiMigrationArrivedBodySchema = passthroughObject({
|
|
|
6002
6184
|
});
|
|
6003
6185
|
const agentApiMigrationResponseSchema = passthroughObject({ migration: agentApiMigrationSummarySchema });
|
|
6004
6186
|
const agentApiMigrationStatusResponseSchema = passthroughObject({ migration: agentApiMigrationSummarySchema.nullable() });
|
|
6187
|
+
const agentApiPushWebhookRegisterBodySchema = object({
|
|
6188
|
+
/** Public HTTPS endpoint that receives `raft-agent-inbox-notice.v1` POSTs. */
|
|
6189
|
+
url: string().trim().min(1),
|
|
6190
|
+
/** Receiver-owned HMAC-SHA256 secret; stored encrypted, never returned. */
|
|
6191
|
+
secret: string().min(1).max(512)
|
|
6192
|
+
}).strict();
|
|
6193
|
+
const agentApiPushWebhookStatusResponseSchema = passthroughObject({
|
|
6194
|
+
registered: boolean(),
|
|
6195
|
+
url: nullableStringSchema,
|
|
6196
|
+
enabled: boolean(),
|
|
6197
|
+
disabledReason: _enum([
|
|
6198
|
+
"endpoint_rejected",
|
|
6199
|
+
"credential_revoked",
|
|
6200
|
+
"agent_inactive"
|
|
6201
|
+
]).nullable(),
|
|
6202
|
+
disabledAt: nullableStringSchema,
|
|
6203
|
+
lastAttemptAt: nullableStringSchema,
|
|
6204
|
+
lastDeliveryAt: nullableStringSchema,
|
|
6205
|
+
lastError: nullableStringSchema,
|
|
6206
|
+
consecutiveFailures: number$1().int().nonnegative(),
|
|
6207
|
+
nextAttemptAt: nullableStringSchema
|
|
6208
|
+
});
|
|
6209
|
+
const agentApiMentionsListQuerySchema = passthroughObject({
|
|
6210
|
+
limit: optionalStringSchema,
|
|
6211
|
+
before_seq: optionalStringSchema
|
|
6212
|
+
});
|
|
6213
|
+
const agentApiMentionsListResponseSchema = passthroughObject({
|
|
6214
|
+
mentions: array(passthroughObject({
|
|
6215
|
+
id: string(),
|
|
6216
|
+
messageId: string(),
|
|
6217
|
+
messageSeq: number$1(),
|
|
6218
|
+
channelId: string(),
|
|
6219
|
+
handleAtSendTime: nullableStringSchema.optional(),
|
|
6220
|
+
source: string(),
|
|
6221
|
+
confidence: unknown().optional(),
|
|
6222
|
+
createdAt: string()
|
|
6223
|
+
})),
|
|
6224
|
+
has_more: boolean()
|
|
6225
|
+
});
|
|
6005
6226
|
function route(input) {
|
|
6006
6227
|
return {
|
|
6007
6228
|
...input,
|
|
@@ -7043,6 +7264,58 @@ const agentApiContract = {
|
|
|
7043
7264
|
query: agentApiAttachmentCommentsQuerySchema
|
|
7044
7265
|
},
|
|
7045
7266
|
response: { body: agentApiAttachmentCommentsResponseSchema }
|
|
7267
|
+
}),
|
|
7268
|
+
pushWebhookStatus: route({
|
|
7269
|
+
key: "pushWebhookStatus",
|
|
7270
|
+
method: "GET",
|
|
7271
|
+
path: "/push-webhook",
|
|
7272
|
+
client: {
|
|
7273
|
+
resource: "pushWebhook",
|
|
7274
|
+
method: "status"
|
|
7275
|
+
},
|
|
7276
|
+
capability: "read",
|
|
7277
|
+
description: "Read the bound External Agent's inbox push registration and delivery status.",
|
|
7278
|
+
request: {},
|
|
7279
|
+
response: { body: agentApiPushWebhookStatusResponseSchema }
|
|
7280
|
+
}),
|
|
7281
|
+
pushWebhookRegister: route({
|
|
7282
|
+
key: "pushWebhookRegister",
|
|
7283
|
+
method: "PUT",
|
|
7284
|
+
path: "/push-webhook",
|
|
7285
|
+
client: {
|
|
7286
|
+
resource: "pushWebhook",
|
|
7287
|
+
method: "register"
|
|
7288
|
+
},
|
|
7289
|
+
capability: "read",
|
|
7290
|
+
description: "Register or replace the bound External Agent's inbox push webhook (raft-agent-inbox-notice.v1); a new registration replaces the secret and re-enables delivery.",
|
|
7291
|
+
request: { body: agentApiPushWebhookRegisterBodySchema },
|
|
7292
|
+
response: { body: agentApiPushWebhookStatusResponseSchema }
|
|
7293
|
+
}),
|
|
7294
|
+
pushWebhookDelete: route({
|
|
7295
|
+
key: "pushWebhookDelete",
|
|
7296
|
+
method: "DELETE",
|
|
7297
|
+
path: "/push-webhook",
|
|
7298
|
+
client: {
|
|
7299
|
+
resource: "pushWebhook",
|
|
7300
|
+
method: "unregister"
|
|
7301
|
+
},
|
|
7302
|
+
capability: "read",
|
|
7303
|
+
description: "Remove the bound External Agent's inbox push registration.",
|
|
7304
|
+
request: {},
|
|
7305
|
+
response: { kind: "empty" }
|
|
7306
|
+
}),
|
|
7307
|
+
mentionsList: route({
|
|
7308
|
+
key: "mentionsList",
|
|
7309
|
+
method: "GET",
|
|
7310
|
+
path: "/mentions",
|
|
7311
|
+
client: {
|
|
7312
|
+
resource: "mentions",
|
|
7313
|
+
method: "list"
|
|
7314
|
+
},
|
|
7315
|
+
capability: "mentions",
|
|
7316
|
+
description: "List notifiable @mentions of the bound agent, newest first.",
|
|
7317
|
+
request: { query: agentApiMentionsListQuerySchema },
|
|
7318
|
+
response: { body: agentApiMentionsListResponseSchema }
|
|
7046
7319
|
})
|
|
7047
7320
|
};
|
|
7048
7321
|
function getAgentApiResponseKind(response) {
|
|
@@ -7050,10 +7323,15 @@ function getAgentApiResponseKind(response) {
|
|
|
7050
7323
|
}
|
|
7051
7324
|
function parseAgentApiResponse(key, value) {
|
|
7052
7325
|
const response = agentApiContract[key].response;
|
|
7053
|
-
|
|
7326
|
+
const kind = getAgentApiResponseKind(response);
|
|
7327
|
+
if (kind === "binary") {
|
|
7054
7328
|
if (!(value instanceof Uint8Array)) throw new TypeError(`Agent API ${key} response did not contain binary bytes`);
|
|
7055
7329
|
return value;
|
|
7056
7330
|
}
|
|
7331
|
+
if (kind === "empty") {
|
|
7332
|
+
if (value !== null && value !== void 0) throw new TypeError(`Agent API ${key} response must be empty`);
|
|
7333
|
+
return null;
|
|
7334
|
+
}
|
|
7057
7335
|
if (!("body" in response)) throw new TypeError(`Agent API ${key} response contract is missing a JSON body schema`);
|
|
7058
7336
|
return response.body.parse(value);
|
|
7059
7337
|
}
|
|
@@ -7174,6 +7452,18 @@ async function requestAgentApiRawRoute(transport, routeKey, options = {}) {
|
|
|
7174
7452
|
data: response.data
|
|
7175
7453
|
};
|
|
7176
7454
|
}
|
|
7455
|
+
if (getAgentApiResponseKind(route.response) === "empty") {
|
|
7456
|
+
if (response.data !== null) return failure$3(routeKey, "response_contract_mismatch", `Agent API ${route.key} response must be empty`, {
|
|
7457
|
+
status: response.status,
|
|
7458
|
+
response: response.data
|
|
7459
|
+
});
|
|
7460
|
+
return {
|
|
7461
|
+
ok: true,
|
|
7462
|
+
routeKey,
|
|
7463
|
+
status: response.status,
|
|
7464
|
+
data: null
|
|
7465
|
+
};
|
|
7466
|
+
}
|
|
7177
7467
|
if (response.data === null) return failure$3(routeKey, "empty_response", `Agent API ${route.key} returned an empty response body`, { status: response.status });
|
|
7178
7468
|
try {
|
|
7179
7469
|
return {
|
|
@@ -7429,7 +7719,8 @@ async function joinRaftChannelByTarget(client, request) {
|
|
|
7429
7719
|
const sequence = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
7430
7720
|
const requestSchema = object({
|
|
7431
7721
|
since: union([sequence, literal("latest")]).optional(),
|
|
7432
|
-
limit: number$1().int().min(1).max(200).optional()
|
|
7722
|
+
limit: number$1().int().min(1).max(200).optional(),
|
|
7723
|
+
ack: literal("cursor").optional()
|
|
7433
7724
|
}).strict();
|
|
7434
7725
|
const metadataSchema = object({
|
|
7435
7726
|
seq: sequence.optional(),
|
|
@@ -7463,12 +7754,13 @@ function projectMessage(message) {
|
|
|
7463
7754
|
externalMessage: message.external_message
|
|
7464
7755
|
};
|
|
7465
7756
|
}
|
|
7466
|
-
function failure$2(code, status) {
|
|
7757
|
+
function failure$2(code, status, cursorAck = false) {
|
|
7758
|
+
const ackNote = cursorAck ? "The batch was not acknowledged; receiving again with the same since returns it again" : "Delivery acknowledgement may already have occurred";
|
|
7467
7759
|
const messages = {
|
|
7468
|
-
INVALID_REQUEST: "Receive requires a nonnegative safe integer or latest cursor
|
|
7469
|
-
TRANSPORT_ERROR:
|
|
7470
|
-
HTTP_ERROR:
|
|
7471
|
-
INVALID_RESPONSE:
|
|
7760
|
+
INVALID_REQUEST: "Receive requires a nonnegative safe integer or latest cursor, an integer limit from 1 to 200, and ack omitted or \"cursor\".",
|
|
7761
|
+
TRANSPORT_ERROR: `Event receive transport failed. ${ackNote}; no retry was attempted.`,
|
|
7762
|
+
HTTP_ERROR: `Event receive returned an HTTP error. ${ackNote}; no retry was attempted.`,
|
|
7763
|
+
INVALID_RESPONSE: `Event receive response did not match the SDK contract. ${ackNote}; no retry was attempted.`
|
|
7472
7764
|
};
|
|
7473
7765
|
return {
|
|
7474
7766
|
ok: false,
|
|
@@ -7483,11 +7775,13 @@ function failure$2(code, status) {
|
|
|
7483
7775
|
async function receiveRaftEvents(client, request = {}) {
|
|
7484
7776
|
const parsed = requestSchema.safeParse(request);
|
|
7485
7777
|
if (!parsed.success) return failure$2("INVALID_REQUEST");
|
|
7778
|
+
const cursorAck = parsed.data.ack === "cursor";
|
|
7486
7779
|
const result = await client.events.get({
|
|
7487
7780
|
...parsed.data.since === void 0 ? {} : { since: String(parsed.data.since) },
|
|
7488
|
-
...parsed.data.limit === void 0 ? {} : { limit: String(parsed.data.limit) }
|
|
7781
|
+
...parsed.data.limit === void 0 ? {} : { limit: String(parsed.data.limit) },
|
|
7782
|
+
...cursorAck ? { ack: "cursor" } : {}
|
|
7489
7783
|
});
|
|
7490
|
-
if (!result.ok) return failure$2(result.error.kind === "transport" ? "TRANSPORT_ERROR" : result.error.kind === "http" ? "HTTP_ERROR" : "INVALID_RESPONSE", result.status);
|
|
7784
|
+
if (!result.ok) return failure$2(result.error.kind === "transport" ? "TRANSPORT_ERROR" : result.error.kind === "http" ? "HTTP_ERROR" : "INVALID_RESPONSE", result.status, cursorAck);
|
|
7491
7785
|
try {
|
|
7492
7786
|
return {
|
|
7493
7787
|
ok: true,
|
|
@@ -7497,206 +7791,2328 @@ async function receiveRaftEvents(client, request = {}) {
|
|
|
7497
7791
|
lastSeenSeq: sequence.nullable().parse(result.data.last_seen_seq),
|
|
7498
7792
|
lastSeenMessageId: result.data.last_seen_msgId,
|
|
7499
7793
|
hasMore: result.data.has_more,
|
|
7500
|
-
replyTarget: result.data.reply_target
|
|
7794
|
+
replyTarget: result.data.reply_target,
|
|
7795
|
+
ackMode: result.data.ack_mode ?? null
|
|
7501
7796
|
}
|
|
7502
7797
|
};
|
|
7503
7798
|
} catch {
|
|
7504
|
-
return failure$2("INVALID_RESPONSE", result.status);
|
|
7799
|
+
return failure$2("INVALID_RESPONSE", result.status, cursorAck);
|
|
7505
7800
|
}
|
|
7506
7801
|
}
|
|
7507
7802
|
//#endregion
|
|
7508
|
-
//#region src/
|
|
7509
|
-
const
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
ok: false,
|
|
7534
|
-
...status === void 0 ? {} : { status },
|
|
7535
|
-
error: {
|
|
7536
|
-
code,
|
|
7537
|
-
message: messages[code]
|
|
7803
|
+
//#region ../shared/src/generated/agentApiRoutes.ts
|
|
7804
|
+
const AGENT_API_ROUTE_MANIFEST = [
|
|
7805
|
+
{
|
|
7806
|
+
"key": "feedbackLocatorIngest",
|
|
7807
|
+
"method": "POST",
|
|
7808
|
+
"path": "/feedback-locators",
|
|
7809
|
+
"fullPath": "/internal/agent-api/feedback-locators",
|
|
7810
|
+
"client": {
|
|
7811
|
+
"resource": "feedbackLocators",
|
|
7812
|
+
"method": "create"
|
|
7813
|
+
},
|
|
7814
|
+
"capability": "send",
|
|
7815
|
+
"description": "Validate and atomically index one locator-only feedback artifact.",
|
|
7816
|
+
"sideEffect": "write",
|
|
7817
|
+
"idempotency": "natural",
|
|
7818
|
+
"destructive": false,
|
|
7819
|
+
"audience": "both",
|
|
7820
|
+
"request": {
|
|
7821
|
+
"params": false,
|
|
7822
|
+
"query": false,
|
|
7823
|
+
"body": true
|
|
7824
|
+
},
|
|
7825
|
+
"response": {
|
|
7826
|
+
"kind": "json",
|
|
7827
|
+
"body": true
|
|
7538
7828
|
}
|
|
7539
|
-
}
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7829
|
+
},
|
|
7830
|
+
{
|
|
7831
|
+
"key": "feedbackLocatorList",
|
|
7832
|
+
"method": "GET",
|
|
7833
|
+
"path": "/feedback-locators",
|
|
7834
|
+
"fullPath": "/internal/agent-api/feedback-locators",
|
|
7835
|
+
"client": {
|
|
7836
|
+
"resource": "feedbackLocators",
|
|
7837
|
+
"method": "list"
|
|
7838
|
+
},
|
|
7839
|
+
"capability": "read",
|
|
7840
|
+
"description": "Query the locator-only feedback index without returning session or feedback content.",
|
|
7841
|
+
"sideEffect": "read",
|
|
7842
|
+
"idempotency": "natural",
|
|
7843
|
+
"destructive": false,
|
|
7844
|
+
"audience": "both",
|
|
7845
|
+
"request": {
|
|
7846
|
+
"params": false,
|
|
7847
|
+
"query": true,
|
|
7848
|
+
"body": false
|
|
7849
|
+
},
|
|
7850
|
+
"response": {
|
|
7851
|
+
"kind": "json",
|
|
7852
|
+
"body": true
|
|
7556
7853
|
}
|
|
7557
|
-
}
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
code,
|
|
7582
|
-
message: MESSAGES[code],
|
|
7583
|
-
...safeErrorCode ? { errorCode: safeErrorCode } : {}
|
|
7854
|
+
},
|
|
7855
|
+
{
|
|
7856
|
+
"key": "events",
|
|
7857
|
+
"method": "GET",
|
|
7858
|
+
"path": "/events",
|
|
7859
|
+
"fullPath": "/internal/agent-api/events",
|
|
7860
|
+
"client": {
|
|
7861
|
+
"resource": "events",
|
|
7862
|
+
"method": "get"
|
|
7863
|
+
},
|
|
7864
|
+
"capability": "read",
|
|
7865
|
+
"description": "Drain pending events for the bound agent credential.",
|
|
7866
|
+
"sideEffect": "destructive_read",
|
|
7867
|
+
"idempotency": "none",
|
|
7868
|
+
"destructive": true,
|
|
7869
|
+
"audience": "both",
|
|
7870
|
+
"request": {
|
|
7871
|
+
"params": false,
|
|
7872
|
+
"query": true,
|
|
7873
|
+
"body": false
|
|
7874
|
+
},
|
|
7875
|
+
"response": {
|
|
7876
|
+
"kind": "json",
|
|
7877
|
+
"body": true
|
|
7584
7878
|
}
|
|
7585
|
-
}
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
const { error } = result;
|
|
7595
|
-
if (error.kind === "transport") return failure("TRANSPORT_ERROR");
|
|
7596
|
-
if (error.kind === "http") return failure("HTTP_ERROR", error.status, error.errorCode);
|
|
7597
|
-
return failure(error.reason === "request_contract_mismatch" || error.reason === "missing_path_param" || error.reason === "missing_route" ? "INVALID_REQUEST" : "INVALID_RESPONSE", result.status);
|
|
7598
|
-
}
|
|
7599
|
-
const AVATAR_PATH = "/internal/agent-api/profile/avatar";
|
|
7600
|
-
/** One multipart POST; never retried, because the upload is a write. */
|
|
7601
|
-
async function uploadRaftAvatar(transport, upload) {
|
|
7602
|
-
if (!upload || !AVATAR_MIME_TYPES.has(upload.mimeType) || typeof upload.filename !== "string" || !upload.filename.trim()) return failure("INVALID_REQUEST");
|
|
7603
|
-
const blob = upload.data instanceof Blob ? upload.data : upload.data instanceof Uint8Array ? new Blob([Uint8Array.from(upload.data)], { type: upload.mimeType }) : null;
|
|
7604
|
-
if (!blob || blob.size === 0 || blob.size > AVATAR_MAX_BYTES) return failure("INVALID_REQUEST");
|
|
7605
|
-
const form = new FormData();
|
|
7606
|
-
form.append("avatar", blob, upload.filename);
|
|
7607
|
-
await transport.beforeRequest?.({
|
|
7608
|
-
method: "POST",
|
|
7609
|
-
path: AVATAR_PATH
|
|
7610
|
-
});
|
|
7611
|
-
const headers = new Headers(transport.headers);
|
|
7612
|
-
headers.delete("content-type");
|
|
7613
|
-
headers.set("accept", "application/json");
|
|
7614
|
-
headers.set("authorization", transport.authorization);
|
|
7615
|
-
let response;
|
|
7616
|
-
try {
|
|
7617
|
-
response = await transport.fetch(`${transport.serverUrl}${AVATAR_PATH}`, {
|
|
7618
|
-
method: "POST",
|
|
7619
|
-
headers,
|
|
7620
|
-
body: form
|
|
7621
|
-
});
|
|
7622
|
-
} catch {
|
|
7623
|
-
return failure("TRANSPORT_ERROR");
|
|
7624
|
-
}
|
|
7625
|
-
let body = null;
|
|
7626
|
-
try {
|
|
7627
|
-
body = await response.json();
|
|
7628
|
-
} catch {
|
|
7629
|
-
body = null;
|
|
7630
|
-
}
|
|
7631
|
-
if (!response.ok) {
|
|
7632
|
-
const errorCode = body && typeof body === "object" ? body.errorCode : void 0;
|
|
7633
|
-
return failure("HTTP_ERROR", response.status, typeof errorCode === "string" ? errorCode : null);
|
|
7634
|
-
}
|
|
7635
|
-
try {
|
|
7636
|
-
return {
|
|
7637
|
-
ok: true,
|
|
7638
|
-
status: response.status,
|
|
7639
|
-
data: parseAgentApiResponse("profileAvatarUpdate", body)
|
|
7640
|
-
};
|
|
7641
|
-
} catch {
|
|
7642
|
-
return failure("INVALID_RESPONSE", response.status);
|
|
7643
|
-
}
|
|
7644
|
-
}
|
|
7645
|
-
/** `readApi` may retry; `writeApi` must make exactly one attempt. */
|
|
7646
|
-
function createRaftManageClient(readApi, writeApi, avatar) {
|
|
7647
|
-
return {
|
|
7648
|
-
profile: {
|
|
7649
|
-
show: async (request = {}) => toRaftApiResult(await readApi.profile.show(request.target === void 0 ? {} : { target: request.target })),
|
|
7650
|
-
update: async (request) => toRaftApiResult(await writeApi.profile.update(request)),
|
|
7651
|
-
updateAvatar: (upload) => uploadRaftAvatar(avatar, upload)
|
|
7879
|
+
},
|
|
7880
|
+
{
|
|
7881
|
+
"key": "historyRead",
|
|
7882
|
+
"method": "GET",
|
|
7883
|
+
"path": "/history",
|
|
7884
|
+
"fullPath": "/internal/agent-api/history",
|
|
7885
|
+
"client": {
|
|
7886
|
+
"resource": "history",
|
|
7887
|
+
"method": "read"
|
|
7652
7888
|
},
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7889
|
+
"capability": "read",
|
|
7890
|
+
"description": "Read visible history for a channel, DM, or thread target.",
|
|
7891
|
+
"sideEffect": "read",
|
|
7892
|
+
"idempotency": "natural",
|
|
7893
|
+
"destructive": false,
|
|
7894
|
+
"audience": "both",
|
|
7895
|
+
"request": {
|
|
7896
|
+
"params": false,
|
|
7897
|
+
"query": true,
|
|
7898
|
+
"body": false
|
|
7899
|
+
},
|
|
7900
|
+
"response": {
|
|
7901
|
+
"kind": "json",
|
|
7902
|
+
"body": true
|
|
7658
7903
|
}
|
|
7659
|
-
}
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
}
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7904
|
+
},
|
|
7905
|
+
{
|
|
7906
|
+
"key": "knowledgeGet",
|
|
7907
|
+
"method": "GET",
|
|
7908
|
+
"path": "/knowledge",
|
|
7909
|
+
"fullPath": "/internal/agent-api/knowledge",
|
|
7910
|
+
"client": {
|
|
7911
|
+
"resource": "knowledge",
|
|
7912
|
+
"method": "get"
|
|
7913
|
+
},
|
|
7914
|
+
"capability": "knowledge",
|
|
7915
|
+
"description": "Fetch a Slock Manual for Agents topic from the current server.",
|
|
7916
|
+
"sideEffect": "read",
|
|
7917
|
+
"idempotency": "natural",
|
|
7918
|
+
"destructive": false,
|
|
7919
|
+
"audience": "both",
|
|
7920
|
+
"request": {
|
|
7921
|
+
"params": false,
|
|
7922
|
+
"query": true,
|
|
7923
|
+
"body": false
|
|
7924
|
+
},
|
|
7925
|
+
"response": {
|
|
7926
|
+
"kind": "json",
|
|
7927
|
+
"body": true
|
|
7928
|
+
}
|
|
7929
|
+
},
|
|
7930
|
+
{
|
|
7931
|
+
"key": "knowledgeSearch",
|
|
7932
|
+
"method": "GET",
|
|
7933
|
+
"path": "/knowledge/search",
|
|
7934
|
+
"fullPath": "/internal/agent-api/knowledge/search",
|
|
7935
|
+
"client": {
|
|
7936
|
+
"resource": "knowledge",
|
|
7937
|
+
"method": "search"
|
|
7938
|
+
},
|
|
7939
|
+
"capability": "knowledge",
|
|
7940
|
+
"description": "Search Slock Manual for Agents topics from the current server.",
|
|
7941
|
+
"sideEffect": "read",
|
|
7942
|
+
"idempotency": "natural",
|
|
7943
|
+
"destructive": false,
|
|
7944
|
+
"audience": "both",
|
|
7945
|
+
"request": {
|
|
7946
|
+
"params": false,
|
|
7947
|
+
"query": true,
|
|
7948
|
+
"body": false
|
|
7949
|
+
},
|
|
7950
|
+
"response": {
|
|
7951
|
+
"kind": "json",
|
|
7952
|
+
"body": true
|
|
7953
|
+
}
|
|
7954
|
+
},
|
|
7955
|
+
{
|
|
7956
|
+
"key": "managedMcpTools",
|
|
7957
|
+
"method": "GET",
|
|
7958
|
+
"path": "/mcp/tools",
|
|
7959
|
+
"fullPath": "/internal/agent-api/mcp/tools",
|
|
7960
|
+
"client": {
|
|
7961
|
+
"resource": "mcp",
|
|
7962
|
+
"method": "tools"
|
|
7963
|
+
},
|
|
7964
|
+
"capability": "mcp",
|
|
7965
|
+
"description": "Fetch the current Server-authorized managed MCP tool catalog for this Agent.",
|
|
7966
|
+
"sideEffect": "read",
|
|
7967
|
+
"idempotency": "natural",
|
|
7968
|
+
"destructive": false,
|
|
7969
|
+
"audience": "both",
|
|
7970
|
+
"request": {
|
|
7971
|
+
"params": false,
|
|
7972
|
+
"query": false,
|
|
7973
|
+
"body": false
|
|
7974
|
+
},
|
|
7975
|
+
"response": {
|
|
7976
|
+
"kind": "json",
|
|
7977
|
+
"body": true
|
|
7978
|
+
}
|
|
7979
|
+
},
|
|
7980
|
+
{
|
|
7981
|
+
"key": "managedMcpCall",
|
|
7982
|
+
"method": "POST",
|
|
7983
|
+
"path": "/mcp/call",
|
|
7984
|
+
"fullPath": "/internal/agent-api/mcp/call",
|
|
7985
|
+
"client": {
|
|
7986
|
+
"resource": "mcp",
|
|
7987
|
+
"method": "call"
|
|
7988
|
+
},
|
|
7989
|
+
"capability": "mcp",
|
|
7990
|
+
"description": "Call an allowlisted managed MCP tool through the Server gateway.",
|
|
7991
|
+
"sideEffect": "write",
|
|
7992
|
+
"idempotency": "none",
|
|
7993
|
+
"destructive": true,
|
|
7994
|
+
"audience": "both",
|
|
7995
|
+
"request": {
|
|
7996
|
+
"params": false,
|
|
7997
|
+
"query": false,
|
|
7998
|
+
"body": true
|
|
7999
|
+
},
|
|
8000
|
+
"response": {
|
|
8001
|
+
"kind": "json",
|
|
8002
|
+
"body": true
|
|
8003
|
+
}
|
|
8004
|
+
},
|
|
8005
|
+
{
|
|
8006
|
+
"key": "messageSend",
|
|
8007
|
+
"method": "POST",
|
|
8008
|
+
"path": "/send",
|
|
8009
|
+
"fullPath": "/internal/agent-api/send",
|
|
8010
|
+
"client": {
|
|
8011
|
+
"resource": "messages",
|
|
8012
|
+
"method": "send"
|
|
8013
|
+
},
|
|
8014
|
+
"capability": "send",
|
|
8015
|
+
"description": "Send a message as the bound agent credential.",
|
|
8016
|
+
"sideEffect": "write",
|
|
8017
|
+
"idempotency": "key",
|
|
8018
|
+
"destructive": false,
|
|
8019
|
+
"audience": "both",
|
|
8020
|
+
"request": {
|
|
8021
|
+
"params": false,
|
|
8022
|
+
"query": false,
|
|
8023
|
+
"body": true
|
|
8024
|
+
},
|
|
8025
|
+
"response": {
|
|
8026
|
+
"kind": "json",
|
|
8027
|
+
"body": true
|
|
8028
|
+
}
|
|
8029
|
+
},
|
|
8030
|
+
{
|
|
8031
|
+
"key": "messageSendV2",
|
|
8032
|
+
"method": "POST",
|
|
8033
|
+
"path": "/v2/send",
|
|
8034
|
+
"fullPath": "/internal/agent-api/v2/send",
|
|
8035
|
+
"client": {
|
|
8036
|
+
"resource": "messages",
|
|
8037
|
+
"method": "sendV2"
|
|
8038
|
+
},
|
|
8039
|
+
"capability": "send",
|
|
8040
|
+
"description": "Send a message through the versioned typed-mention contract.",
|
|
8041
|
+
"sideEffect": "write",
|
|
8042
|
+
"idempotency": "key",
|
|
8043
|
+
"destructive": false,
|
|
8044
|
+
"audience": "both",
|
|
8045
|
+
"request": {
|
|
8046
|
+
"params": false,
|
|
8047
|
+
"query": false,
|
|
8048
|
+
"body": true
|
|
8049
|
+
},
|
|
8050
|
+
"response": {
|
|
8051
|
+
"kind": "json",
|
|
8052
|
+
"body": true
|
|
8053
|
+
}
|
|
8054
|
+
},
|
|
8055
|
+
{
|
|
8056
|
+
"key": "messageResolve",
|
|
8057
|
+
"method": "GET",
|
|
8058
|
+
"path": "/messages/:msgId/resolve",
|
|
8059
|
+
"fullPath": "/internal/agent-api/messages/:msgId/resolve",
|
|
8060
|
+
"client": {
|
|
8061
|
+
"resource": "messages",
|
|
8062
|
+
"method": "resolve"
|
|
8063
|
+
},
|
|
8064
|
+
"capability": "read",
|
|
8065
|
+
"description": "Resolve a message id exactly and return the canonical visible message.",
|
|
8066
|
+
"sideEffect": "read",
|
|
8067
|
+
"idempotency": "natural",
|
|
8068
|
+
"destructive": false,
|
|
8069
|
+
"audience": "both",
|
|
8070
|
+
"request": {
|
|
8071
|
+
"params": true,
|
|
8072
|
+
"query": false,
|
|
8073
|
+
"body": false
|
|
8074
|
+
},
|
|
8075
|
+
"response": {
|
|
8076
|
+
"kind": "json",
|
|
8077
|
+
"body": true
|
|
8078
|
+
}
|
|
8079
|
+
},
|
|
8080
|
+
{
|
|
8081
|
+
"key": "messageSearch",
|
|
8082
|
+
"method": "GET",
|
|
8083
|
+
"path": "/search",
|
|
8084
|
+
"fullPath": "/internal/agent-api/search",
|
|
8085
|
+
"client": {
|
|
8086
|
+
"resource": "messages",
|
|
8087
|
+
"method": "search"
|
|
8088
|
+
},
|
|
8089
|
+
"capability": "read",
|
|
8090
|
+
"description": "Search messages visible to the bound agent credential.",
|
|
8091
|
+
"sideEffect": "read",
|
|
8092
|
+
"idempotency": "natural",
|
|
8093
|
+
"destructive": false,
|
|
8094
|
+
"audience": "both",
|
|
8095
|
+
"request": {
|
|
8096
|
+
"params": false,
|
|
8097
|
+
"query": true,
|
|
8098
|
+
"body": false
|
|
8099
|
+
},
|
|
8100
|
+
"response": {
|
|
8101
|
+
"kind": "json",
|
|
8102
|
+
"body": true
|
|
8103
|
+
}
|
|
8104
|
+
},
|
|
8105
|
+
{
|
|
8106
|
+
"key": "messageReactionAdd",
|
|
8107
|
+
"method": "POST",
|
|
8108
|
+
"path": "/messages/:msgId/reactions",
|
|
8109
|
+
"fullPath": "/internal/agent-api/messages/:msgId/reactions",
|
|
8110
|
+
"client": {
|
|
8111
|
+
"resource": "messages",
|
|
8112
|
+
"method": "addReaction"
|
|
8113
|
+
},
|
|
8114
|
+
"capability": "reactions",
|
|
8115
|
+
"description": "Add a reaction to a visible message as the bound agent credential.",
|
|
8116
|
+
"sideEffect": "write",
|
|
8117
|
+
"idempotency": "natural",
|
|
8118
|
+
"destructive": false,
|
|
8119
|
+
"audience": "both",
|
|
8120
|
+
"request": {
|
|
8121
|
+
"params": true,
|
|
8122
|
+
"query": false,
|
|
8123
|
+
"body": true
|
|
8124
|
+
},
|
|
8125
|
+
"response": {
|
|
8126
|
+
"kind": "json",
|
|
8127
|
+
"body": true
|
|
8128
|
+
}
|
|
8129
|
+
},
|
|
8130
|
+
{
|
|
8131
|
+
"key": "messageReactionRemove",
|
|
8132
|
+
"method": "DELETE",
|
|
8133
|
+
"path": "/messages/:msgId/reactions",
|
|
8134
|
+
"fullPath": "/internal/agent-api/messages/:msgId/reactions",
|
|
8135
|
+
"client": {
|
|
8136
|
+
"resource": "messages",
|
|
8137
|
+
"method": "removeReaction"
|
|
8138
|
+
},
|
|
8139
|
+
"capability": "reactions",
|
|
8140
|
+
"description": "Remove a reaction from a visible message as the bound agent credential.",
|
|
8141
|
+
"sideEffect": "write",
|
|
8142
|
+
"idempotency": "natural",
|
|
8143
|
+
"destructive": true,
|
|
8144
|
+
"audience": "both",
|
|
8145
|
+
"request": {
|
|
8146
|
+
"params": true,
|
|
8147
|
+
"query": false,
|
|
8148
|
+
"body": true
|
|
8149
|
+
},
|
|
8150
|
+
"response": {
|
|
8151
|
+
"kind": "json",
|
|
8152
|
+
"body": true
|
|
8153
|
+
}
|
|
8154
|
+
},
|
|
8155
|
+
{
|
|
8156
|
+
"key": "channelJoin",
|
|
8157
|
+
"method": "POST",
|
|
8158
|
+
"path": "/channels/:channelId/join",
|
|
8159
|
+
"fullPath": "/internal/agent-api/channels/:channelId/join",
|
|
8160
|
+
"client": {
|
|
8161
|
+
"resource": "channels",
|
|
8162
|
+
"method": "join"
|
|
8163
|
+
},
|
|
8164
|
+
"capability": "channels",
|
|
8165
|
+
"description": "Join a visible public channel as the bound agent credential.",
|
|
8166
|
+
"sideEffect": "write",
|
|
8167
|
+
"idempotency": "natural",
|
|
8168
|
+
"destructive": false,
|
|
8169
|
+
"audience": "both",
|
|
8170
|
+
"request": {
|
|
8171
|
+
"params": true,
|
|
8172
|
+
"query": false,
|
|
8173
|
+
"body": false
|
|
8174
|
+
},
|
|
8175
|
+
"response": {
|
|
8176
|
+
"kind": "json",
|
|
8177
|
+
"body": true
|
|
8178
|
+
}
|
|
8179
|
+
},
|
|
8180
|
+
{
|
|
8181
|
+
"key": "channelLeave",
|
|
8182
|
+
"method": "POST",
|
|
8183
|
+
"path": "/channels/:channelId/leave",
|
|
8184
|
+
"fullPath": "/internal/agent-api/channels/:channelId/leave",
|
|
8185
|
+
"client": {
|
|
8186
|
+
"resource": "channels",
|
|
8187
|
+
"method": "leave"
|
|
8188
|
+
},
|
|
8189
|
+
"capability": "channels",
|
|
8190
|
+
"description": "Leave a joined regular channel as the bound agent credential.",
|
|
8191
|
+
"sideEffect": "write",
|
|
8192
|
+
"idempotency": "natural",
|
|
8193
|
+
"destructive": true,
|
|
8194
|
+
"audience": "both",
|
|
8195
|
+
"request": {
|
|
8196
|
+
"params": true,
|
|
8197
|
+
"query": false,
|
|
8198
|
+
"body": false
|
|
8199
|
+
},
|
|
8200
|
+
"response": {
|
|
8201
|
+
"kind": "json",
|
|
8202
|
+
"body": true
|
|
8203
|
+
}
|
|
8204
|
+
},
|
|
8205
|
+
{
|
|
8206
|
+
"key": "channelMute",
|
|
8207
|
+
"method": "POST",
|
|
8208
|
+
"path": "/channels/:channelId/mute",
|
|
8209
|
+
"fullPath": "/internal/agent-api/channels/:channelId/mute",
|
|
8210
|
+
"client": {
|
|
8211
|
+
"resource": "channels",
|
|
8212
|
+
"method": "mute"
|
|
8213
|
+
},
|
|
8214
|
+
"capability": "channels",
|
|
8215
|
+
"description": "Mute ordinary activity delivery for a visible regular channel as the bound agent credential.",
|
|
8216
|
+
"sideEffect": "write",
|
|
8217
|
+
"idempotency": "natural",
|
|
8218
|
+
"destructive": false,
|
|
8219
|
+
"audience": "both",
|
|
8220
|
+
"request": {
|
|
8221
|
+
"params": true,
|
|
8222
|
+
"query": false,
|
|
8223
|
+
"body": true
|
|
8224
|
+
},
|
|
8225
|
+
"response": {
|
|
8226
|
+
"kind": "json",
|
|
8227
|
+
"body": true
|
|
8228
|
+
}
|
|
8229
|
+
},
|
|
8230
|
+
{
|
|
8231
|
+
"key": "channelUnmute",
|
|
8232
|
+
"method": "POST",
|
|
8233
|
+
"path": "/channels/:channelId/unmute",
|
|
8234
|
+
"fullPath": "/internal/agent-api/channels/:channelId/unmute",
|
|
8235
|
+
"client": {
|
|
8236
|
+
"resource": "channels",
|
|
8237
|
+
"method": "unmute"
|
|
8238
|
+
},
|
|
8239
|
+
"capability": "channels",
|
|
8240
|
+
"description": "Unmute ordinary activity delivery for a visible regular channel as the bound agent credential.",
|
|
8241
|
+
"sideEffect": "write",
|
|
8242
|
+
"idempotency": "natural",
|
|
8243
|
+
"destructive": false,
|
|
8244
|
+
"audience": "both",
|
|
8245
|
+
"request": {
|
|
8246
|
+
"params": true,
|
|
8247
|
+
"query": false,
|
|
8248
|
+
"body": false
|
|
8249
|
+
},
|
|
8250
|
+
"response": {
|
|
8251
|
+
"kind": "json",
|
|
8252
|
+
"body": true
|
|
8253
|
+
}
|
|
8254
|
+
},
|
|
8255
|
+
{
|
|
8256
|
+
"key": "channelArchive",
|
|
8257
|
+
"method": "POST",
|
|
8258
|
+
"path": "/channels/archive",
|
|
8259
|
+
"fullPath": "/internal/agent-api/channels/archive",
|
|
8260
|
+
"client": {
|
|
8261
|
+
"resource": "channels",
|
|
8262
|
+
"method": "archive"
|
|
8263
|
+
},
|
|
8264
|
+
"capability": "channels",
|
|
8265
|
+
"description": "Archive a regular channel when the bound agent has server channel-management authority.",
|
|
8266
|
+
"sideEffect": "write",
|
|
8267
|
+
"idempotency": "natural",
|
|
8268
|
+
"destructive": true,
|
|
8269
|
+
"audience": "both",
|
|
8270
|
+
"request": {
|
|
8271
|
+
"params": false,
|
|
8272
|
+
"query": false,
|
|
8273
|
+
"body": true
|
|
8274
|
+
},
|
|
8275
|
+
"response": {
|
|
8276
|
+
"kind": "json",
|
|
8277
|
+
"body": true
|
|
8278
|
+
}
|
|
8279
|
+
},
|
|
8280
|
+
{
|
|
8281
|
+
"key": "channelUnarchive",
|
|
8282
|
+
"method": "POST",
|
|
8283
|
+
"path": "/channels/unarchive",
|
|
8284
|
+
"fullPath": "/internal/agent-api/channels/unarchive",
|
|
8285
|
+
"client": {
|
|
8286
|
+
"resource": "channels",
|
|
8287
|
+
"method": "unarchive"
|
|
8288
|
+
},
|
|
8289
|
+
"capability": "channels",
|
|
8290
|
+
"description": "Unarchive a regular channel when the bound agent has server channel-management authority.",
|
|
8291
|
+
"sideEffect": "write",
|
|
8292
|
+
"idempotency": "natural",
|
|
8293
|
+
"destructive": false,
|
|
8294
|
+
"audience": "both",
|
|
8295
|
+
"request": {
|
|
8296
|
+
"params": false,
|
|
8297
|
+
"query": false,
|
|
8298
|
+
"body": true
|
|
8299
|
+
},
|
|
8300
|
+
"response": {
|
|
8301
|
+
"kind": "json",
|
|
8302
|
+
"body": true
|
|
8303
|
+
}
|
|
8304
|
+
},
|
|
8305
|
+
{
|
|
8306
|
+
"key": "channelMembers",
|
|
8307
|
+
"method": "GET",
|
|
8308
|
+
"path": "/channel-members",
|
|
8309
|
+
"fullPath": "/internal/agent-api/channel-members",
|
|
8310
|
+
"client": {
|
|
8311
|
+
"resource": "channels",
|
|
8312
|
+
"method": "members"
|
|
8313
|
+
},
|
|
8314
|
+
"capability": "channels",
|
|
8315
|
+
"description": "List agents and humans in a visible channel, DM, or thread.",
|
|
8316
|
+
"sideEffect": "read",
|
|
8317
|
+
"idempotency": "natural",
|
|
8318
|
+
"destructive": false,
|
|
8319
|
+
"audience": "both",
|
|
8320
|
+
"request": {
|
|
8321
|
+
"params": false,
|
|
8322
|
+
"query": true,
|
|
8323
|
+
"body": false
|
|
8324
|
+
},
|
|
8325
|
+
"response": {
|
|
8326
|
+
"kind": "json",
|
|
8327
|
+
"body": true
|
|
8328
|
+
}
|
|
8329
|
+
},
|
|
8330
|
+
{
|
|
8331
|
+
"key": "resolveChannel",
|
|
8332
|
+
"method": "POST",
|
|
8333
|
+
"path": "/resolve-channel",
|
|
8334
|
+
"fullPath": "/internal/agent-api/resolve-channel",
|
|
8335
|
+
"client": {
|
|
8336
|
+
"resource": "channels",
|
|
8337
|
+
"method": "resolve"
|
|
8338
|
+
},
|
|
8339
|
+
"capability": "send",
|
|
8340
|
+
"description": "Resolve a writable channel, DM, or thread target for the bound agent credential.",
|
|
8341
|
+
"sideEffect": "read",
|
|
8342
|
+
"idempotency": "natural",
|
|
8343
|
+
"destructive": false,
|
|
8344
|
+
"audience": "both",
|
|
8345
|
+
"request": {
|
|
8346
|
+
"params": false,
|
|
8347
|
+
"query": false,
|
|
8348
|
+
"body": true
|
|
8349
|
+
},
|
|
8350
|
+
"response": {
|
|
8351
|
+
"kind": "json",
|
|
8352
|
+
"body": true
|
|
8353
|
+
}
|
|
8354
|
+
},
|
|
8355
|
+
{
|
|
8356
|
+
"key": "threadUnfollow",
|
|
8357
|
+
"method": "POST",
|
|
8358
|
+
"path": "/threads/unfollow",
|
|
8359
|
+
"fullPath": "/internal/agent-api/threads/unfollow",
|
|
8360
|
+
"client": {
|
|
8361
|
+
"resource": "threads",
|
|
8362
|
+
"method": "unfollow"
|
|
8363
|
+
},
|
|
8364
|
+
"capability": "channels",
|
|
8365
|
+
"description": "Stop ordinary delivery for a followed thread as the bound agent credential.",
|
|
8366
|
+
"sideEffect": "write",
|
|
8367
|
+
"idempotency": "natural",
|
|
8368
|
+
"destructive": true,
|
|
8369
|
+
"audience": "both",
|
|
8370
|
+
"request": {
|
|
8371
|
+
"params": false,
|
|
8372
|
+
"query": false,
|
|
8373
|
+
"body": true
|
|
8374
|
+
},
|
|
8375
|
+
"response": {
|
|
8376
|
+
"kind": "json",
|
|
8377
|
+
"body": true
|
|
8378
|
+
}
|
|
8379
|
+
},
|
|
8380
|
+
{
|
|
8381
|
+
"key": "threadList",
|
|
8382
|
+
"method": "GET",
|
|
8383
|
+
"path": "/threads",
|
|
8384
|
+
"fullPath": "/internal/agent-api/threads",
|
|
8385
|
+
"client": {
|
|
8386
|
+
"resource": "threads",
|
|
8387
|
+
"method": "list"
|
|
8388
|
+
},
|
|
8389
|
+
"capability": "channels",
|
|
8390
|
+
"description": "List active threads the bound agent is following.",
|
|
8391
|
+
"sideEffect": "read",
|
|
8392
|
+
"idempotency": "natural",
|
|
8393
|
+
"destructive": false,
|
|
8394
|
+
"audience": "both",
|
|
8395
|
+
"request": {
|
|
8396
|
+
"params": false,
|
|
8397
|
+
"query": false,
|
|
8398
|
+
"body": false
|
|
8399
|
+
},
|
|
8400
|
+
"response": {
|
|
8401
|
+
"kind": "json",
|
|
8402
|
+
"body": true
|
|
8403
|
+
}
|
|
8404
|
+
},
|
|
8405
|
+
{
|
|
8406
|
+
"key": "inboxList",
|
|
8407
|
+
"method": "GET",
|
|
8408
|
+
"path": "/inbox/conversations",
|
|
8409
|
+
"fullPath": "/internal/agent-api/inbox/conversations",
|
|
8410
|
+
"client": {
|
|
8411
|
+
"resource": "inbox",
|
|
8412
|
+
"method": "list"
|
|
8413
|
+
},
|
|
8414
|
+
"capability": "read",
|
|
8415
|
+
"description": "List the bound agent's durable unread conversations, newest activity first.",
|
|
8416
|
+
"sideEffect": "read",
|
|
8417
|
+
"idempotency": "natural",
|
|
8418
|
+
"destructive": false,
|
|
8419
|
+
"audience": "both",
|
|
8420
|
+
"request": {
|
|
8421
|
+
"params": false,
|
|
8422
|
+
"query": true,
|
|
8423
|
+
"body": false
|
|
8424
|
+
},
|
|
8425
|
+
"response": {
|
|
8426
|
+
"kind": "json",
|
|
8427
|
+
"body": true
|
|
8428
|
+
}
|
|
8429
|
+
},
|
|
8430
|
+
{
|
|
8431
|
+
"key": "agentContext",
|
|
8432
|
+
"method": "GET",
|
|
8433
|
+
"path": "/context",
|
|
8434
|
+
"fullPath": "/internal/agent-api/context",
|
|
8435
|
+
"client": {
|
|
8436
|
+
"resource": "agent",
|
|
8437
|
+
"method": "context"
|
|
8438
|
+
},
|
|
8439
|
+
"capability": "read",
|
|
8440
|
+
"description": "Identity bootstrap: the bound agent's identity, server, credential capabilities, and (external agents) the rendered operating guide.",
|
|
8441
|
+
"sideEffect": "read",
|
|
8442
|
+
"idempotency": "natural",
|
|
8443
|
+
"destructive": false,
|
|
8444
|
+
"audience": "both",
|
|
8445
|
+
"request": {
|
|
8446
|
+
"params": false,
|
|
8447
|
+
"query": false,
|
|
8448
|
+
"body": false
|
|
8449
|
+
},
|
|
8450
|
+
"response": {
|
|
8451
|
+
"kind": "json",
|
|
8452
|
+
"body": true
|
|
8453
|
+
}
|
|
8454
|
+
},
|
|
8455
|
+
{
|
|
8456
|
+
"key": "serverInfo",
|
|
8457
|
+
"method": "GET",
|
|
8458
|
+
"path": "/server",
|
|
8459
|
+
"fullPath": "/internal/agent-api/server",
|
|
8460
|
+
"client": {
|
|
8461
|
+
"resource": "server",
|
|
8462
|
+
"method": "info"
|
|
8463
|
+
},
|
|
8464
|
+
"capability": "read",
|
|
8465
|
+
"description": "List channels, agents, humans, and runtime context visible to the bound agent.",
|
|
8466
|
+
"sideEffect": "read",
|
|
8467
|
+
"idempotency": "natural",
|
|
8468
|
+
"destructive": false,
|
|
8469
|
+
"audience": "both",
|
|
8470
|
+
"request": {
|
|
8471
|
+
"params": false,
|
|
8472
|
+
"query": false,
|
|
8473
|
+
"body": false
|
|
8474
|
+
},
|
|
8475
|
+
"response": {
|
|
8476
|
+
"kind": "json",
|
|
8477
|
+
"body": true
|
|
8478
|
+
}
|
|
8479
|
+
},
|
|
8480
|
+
{
|
|
8481
|
+
"key": "serverUpdate",
|
|
8482
|
+
"method": "PATCH",
|
|
8483
|
+
"path": "/server",
|
|
8484
|
+
"fullPath": "/internal/agent-api/server",
|
|
8485
|
+
"client": {
|
|
8486
|
+
"resource": "server",
|
|
8487
|
+
"method": "update"
|
|
8488
|
+
},
|
|
8489
|
+
"capability": "server",
|
|
8490
|
+
"description": "Update the bound agent's server profile name or member-visibility setting.",
|
|
8491
|
+
"sideEffect": "write",
|
|
8492
|
+
"idempotency": "natural",
|
|
8493
|
+
"destructive": true,
|
|
8494
|
+
"audience": "both",
|
|
8495
|
+
"request": {
|
|
8496
|
+
"params": false,
|
|
8497
|
+
"query": false,
|
|
8498
|
+
"body": true
|
|
8499
|
+
},
|
|
8500
|
+
"response": {
|
|
8501
|
+
"kind": "json",
|
|
8502
|
+
"body": true
|
|
8503
|
+
}
|
|
8504
|
+
},
|
|
8505
|
+
{
|
|
8506
|
+
"key": "mentionActionsPending",
|
|
8507
|
+
"method": "GET",
|
|
8508
|
+
"path": "/mention-actions/pending",
|
|
8509
|
+
"fullPath": "/internal/agent-api/mention-actions/pending",
|
|
8510
|
+
"client": {
|
|
8511
|
+
"resource": "mentions",
|
|
8512
|
+
"method": "pendingActions"
|
|
8513
|
+
},
|
|
8514
|
+
"capability": "mentions",
|
|
8515
|
+
"description": "List sender-side pending mention actions for unresolved outsider mentions.",
|
|
8516
|
+
"sideEffect": "read",
|
|
8517
|
+
"idempotency": "natural",
|
|
8518
|
+
"destructive": false,
|
|
8519
|
+
"audience": "both",
|
|
8520
|
+
"request": {
|
|
8521
|
+
"params": false,
|
|
8522
|
+
"query": true,
|
|
8523
|
+
"body": false
|
|
8524
|
+
},
|
|
8525
|
+
"response": {
|
|
8526
|
+
"kind": "json",
|
|
8527
|
+
"body": true
|
|
8528
|
+
}
|
|
8529
|
+
},
|
|
8530
|
+
{
|
|
8531
|
+
"key": "senderMentionDeliveries",
|
|
8532
|
+
"method": "GET",
|
|
8533
|
+
"path": "/messages/:messageId/mention-deliveries",
|
|
8534
|
+
"fullPath": "/internal/agent-api/messages/:messageId/mention-deliveries",
|
|
8535
|
+
"client": {
|
|
8536
|
+
"resource": "mentions",
|
|
8537
|
+
"method": "senderDeliveries"
|
|
8538
|
+
},
|
|
8539
|
+
"capability": "mentions",
|
|
8540
|
+
"description": "Per-target delivery outcome for a message the bound agent sent.",
|
|
8541
|
+
"sideEffect": "read",
|
|
8542
|
+
"idempotency": "natural",
|
|
8543
|
+
"destructive": false,
|
|
8544
|
+
"audience": "both",
|
|
8545
|
+
"request": {
|
|
8546
|
+
"params": true,
|
|
8547
|
+
"query": false,
|
|
8548
|
+
"body": false
|
|
8549
|
+
},
|
|
8550
|
+
"response": {
|
|
8551
|
+
"kind": "json",
|
|
8552
|
+
"body": true
|
|
8553
|
+
}
|
|
8554
|
+
},
|
|
8555
|
+
{
|
|
8556
|
+
"key": "mentionActionsExecute",
|
|
8557
|
+
"method": "POST",
|
|
8558
|
+
"path": "/mention-actions/execute",
|
|
8559
|
+
"fullPath": "/internal/agent-api/mention-actions/execute",
|
|
8560
|
+
"client": {
|
|
8561
|
+
"resource": "mentions",
|
|
8562
|
+
"method": "executeAction"
|
|
8563
|
+
},
|
|
8564
|
+
"capability": "mentions",
|
|
8565
|
+
"description": "Execute sender-side mention resolution actions.",
|
|
8566
|
+
"sideEffect": "write",
|
|
8567
|
+
"idempotency": "none",
|
|
8568
|
+
"destructive": false,
|
|
8569
|
+
"audience": "both",
|
|
8570
|
+
"request": {
|
|
8571
|
+
"params": false,
|
|
8572
|
+
"query": false,
|
|
8573
|
+
"body": true
|
|
8574
|
+
},
|
|
8575
|
+
"response": {
|
|
8576
|
+
"kind": "json",
|
|
8577
|
+
"body": true
|
|
8578
|
+
}
|
|
8579
|
+
},
|
|
8580
|
+
{
|
|
8581
|
+
"key": "taskClaim",
|
|
8582
|
+
"method": "POST",
|
|
8583
|
+
"path": "/tasks/claim",
|
|
8584
|
+
"fullPath": "/internal/agent-api/tasks/claim",
|
|
8585
|
+
"client": {
|
|
8586
|
+
"resource": "tasks",
|
|
8587
|
+
"method": "claim"
|
|
8588
|
+
},
|
|
8589
|
+
"capability": "tasks",
|
|
8590
|
+
"description": "Claim one or more tasks by task number or message id.",
|
|
8591
|
+
"sideEffect": "write",
|
|
8592
|
+
"idempotency": "none",
|
|
8593
|
+
"destructive": false,
|
|
8594
|
+
"audience": "both",
|
|
8595
|
+
"request": {
|
|
8596
|
+
"params": false,
|
|
8597
|
+
"query": false,
|
|
8598
|
+
"body": true
|
|
8599
|
+
},
|
|
8600
|
+
"response": {
|
|
8601
|
+
"kind": "json",
|
|
8602
|
+
"body": true
|
|
8603
|
+
}
|
|
8604
|
+
},
|
|
8605
|
+
{
|
|
8606
|
+
"key": "taskList",
|
|
8607
|
+
"method": "GET",
|
|
8608
|
+
"path": "/tasks",
|
|
8609
|
+
"fullPath": "/internal/agent-api/tasks",
|
|
8610
|
+
"client": {
|
|
8611
|
+
"resource": "tasks",
|
|
8612
|
+
"method": "list"
|
|
8613
|
+
},
|
|
8614
|
+
"capability": "tasks",
|
|
8615
|
+
"description": "List tasks in a channel.",
|
|
8616
|
+
"sideEffect": "read",
|
|
8617
|
+
"idempotency": "natural",
|
|
8618
|
+
"destructive": false,
|
|
8619
|
+
"audience": "both",
|
|
8620
|
+
"request": {
|
|
8621
|
+
"params": false,
|
|
8622
|
+
"query": true,
|
|
8623
|
+
"body": false
|
|
8624
|
+
},
|
|
8625
|
+
"response": {
|
|
8626
|
+
"kind": "json",
|
|
8627
|
+
"body": true
|
|
8628
|
+
}
|
|
8629
|
+
},
|
|
8630
|
+
{
|
|
8631
|
+
"key": "taskCreate",
|
|
8632
|
+
"method": "POST",
|
|
8633
|
+
"path": "/tasks",
|
|
8634
|
+
"fullPath": "/internal/agent-api/tasks",
|
|
8635
|
+
"client": {
|
|
8636
|
+
"resource": "tasks",
|
|
8637
|
+
"method": "create"
|
|
8638
|
+
},
|
|
8639
|
+
"capability": "tasks",
|
|
8640
|
+
"description": "Create one or more tasks in a channel.",
|
|
8641
|
+
"sideEffect": "write",
|
|
8642
|
+
"idempotency": "none",
|
|
8643
|
+
"destructive": false,
|
|
8644
|
+
"audience": "both",
|
|
8645
|
+
"request": {
|
|
8646
|
+
"params": false,
|
|
8647
|
+
"query": false,
|
|
8648
|
+
"body": true
|
|
8649
|
+
},
|
|
8650
|
+
"response": {
|
|
8651
|
+
"kind": "json",
|
|
8652
|
+
"body": true
|
|
8653
|
+
}
|
|
8654
|
+
},
|
|
8655
|
+
{
|
|
8656
|
+
"key": "taskUnclaim",
|
|
8657
|
+
"method": "POST",
|
|
8658
|
+
"path": "/tasks/unclaim",
|
|
8659
|
+
"fullPath": "/internal/agent-api/tasks/unclaim",
|
|
8660
|
+
"client": {
|
|
8661
|
+
"resource": "tasks",
|
|
8662
|
+
"method": "unclaim"
|
|
8663
|
+
},
|
|
8664
|
+
"capability": "tasks",
|
|
8665
|
+
"description": "Release a previously claimed task.",
|
|
8666
|
+
"sideEffect": "write",
|
|
8667
|
+
"idempotency": "none",
|
|
8668
|
+
"destructive": true,
|
|
8669
|
+
"audience": "both",
|
|
8670
|
+
"request": {
|
|
8671
|
+
"params": false,
|
|
8672
|
+
"query": false,
|
|
8673
|
+
"body": true
|
|
8674
|
+
},
|
|
8675
|
+
"response": {
|
|
8676
|
+
"kind": "json",
|
|
8677
|
+
"body": true
|
|
8678
|
+
}
|
|
8679
|
+
},
|
|
8680
|
+
{
|
|
8681
|
+
"key": "taskAssign",
|
|
8682
|
+
"method": "POST",
|
|
8683
|
+
"path": "/tasks/assign",
|
|
8684
|
+
"fullPath": "/internal/agent-api/tasks/assign",
|
|
8685
|
+
"client": {
|
|
8686
|
+
"resource": "tasks",
|
|
8687
|
+
"method": "assign"
|
|
8688
|
+
},
|
|
8689
|
+
"capability": "tasks",
|
|
8690
|
+
"description": "Set or clear a task's assignee (pass null to unassign).",
|
|
8691
|
+
"sideEffect": "write",
|
|
8692
|
+
"idempotency": "natural",
|
|
8693
|
+
"destructive": true,
|
|
8694
|
+
"audience": "both",
|
|
8695
|
+
"request": {
|
|
8696
|
+
"params": false,
|
|
8697
|
+
"query": false,
|
|
8698
|
+
"body": true
|
|
8699
|
+
},
|
|
8700
|
+
"response": {
|
|
8701
|
+
"kind": "json",
|
|
8702
|
+
"body": true
|
|
8703
|
+
}
|
|
8704
|
+
},
|
|
8705
|
+
{
|
|
8706
|
+
"key": "taskUpdateStatus",
|
|
8707
|
+
"method": "POST",
|
|
8708
|
+
"path": "/tasks/update-status",
|
|
8709
|
+
"fullPath": "/internal/agent-api/tasks/update-status",
|
|
8710
|
+
"client": {
|
|
8711
|
+
"resource": "tasks",
|
|
8712
|
+
"method": "updateStatus"
|
|
8713
|
+
},
|
|
8714
|
+
"capability": "tasks",
|
|
8715
|
+
"description": "Update a task status.",
|
|
8716
|
+
"sideEffect": "write",
|
|
8717
|
+
"idempotency": "natural",
|
|
8718
|
+
"destructive": true,
|
|
8719
|
+
"audience": "both",
|
|
8720
|
+
"request": {
|
|
8721
|
+
"params": false,
|
|
8722
|
+
"query": false,
|
|
8723
|
+
"body": true
|
|
8724
|
+
},
|
|
8725
|
+
"response": {
|
|
8726
|
+
"kind": "json",
|
|
8727
|
+
"body": true
|
|
8728
|
+
}
|
|
8729
|
+
},
|
|
8730
|
+
{
|
|
8731
|
+
"key": "taskResourceReceipt",
|
|
8732
|
+
"method": "POST",
|
|
8733
|
+
"path": "/tasks/resource-receipt",
|
|
8734
|
+
"fullPath": "/internal/agent-api/tasks/resource-receipt",
|
|
8735
|
+
"client": {
|
|
8736
|
+
"resource": "tasks",
|
|
8737
|
+
"method": "recordResourceReceipt"
|
|
8738
|
+
},
|
|
8739
|
+
"capability": "tasks",
|
|
8740
|
+
"description": "Record the structured receipt and expiry follow-up for a resource-creating task.",
|
|
8741
|
+
"sideEffect": "write",
|
|
8742
|
+
"idempotency": "none",
|
|
8743
|
+
"destructive": false,
|
|
8744
|
+
"audience": "both",
|
|
8745
|
+
"request": {
|
|
8746
|
+
"params": false,
|
|
8747
|
+
"query": false,
|
|
8748
|
+
"body": true
|
|
8749
|
+
},
|
|
8750
|
+
"response": {
|
|
8751
|
+
"kind": "json",
|
|
8752
|
+
"body": true
|
|
8753
|
+
}
|
|
8754
|
+
},
|
|
8755
|
+
{
|
|
8756
|
+
"key": "taskDelete",
|
|
8757
|
+
"method": "POST",
|
|
8758
|
+
"path": "/tasks/delete",
|
|
8759
|
+
"fullPath": "/internal/agent-api/tasks/delete",
|
|
8760
|
+
"client": {
|
|
8761
|
+
"resource": "tasks",
|
|
8762
|
+
"method": "delete"
|
|
8763
|
+
},
|
|
8764
|
+
"capability": "tasks",
|
|
8765
|
+
"description": "Delete a task (creator or server admin).",
|
|
8766
|
+
"sideEffect": "write",
|
|
8767
|
+
"idempotency": "none",
|
|
8768
|
+
"destructive": true,
|
|
8769
|
+
"audience": "both",
|
|
8770
|
+
"request": {
|
|
8771
|
+
"params": false,
|
|
8772
|
+
"query": false,
|
|
8773
|
+
"body": true
|
|
8774
|
+
},
|
|
8775
|
+
"response": {
|
|
8776
|
+
"kind": "json",
|
|
8777
|
+
"body": true
|
|
8778
|
+
}
|
|
8779
|
+
},
|
|
8780
|
+
{
|
|
8781
|
+
"key": "taskConvert",
|
|
8782
|
+
"method": "POST",
|
|
8783
|
+
"path": "/tasks/convert",
|
|
8784
|
+
"fullPath": "/internal/agent-api/tasks/convert",
|
|
8785
|
+
"client": {
|
|
8786
|
+
"resource": "tasks",
|
|
8787
|
+
"method": "convert"
|
|
8788
|
+
},
|
|
8789
|
+
"capability": "tasks",
|
|
8790
|
+
"description": "Convert a message into a task without claiming it.",
|
|
8791
|
+
"sideEffect": "write",
|
|
8792
|
+
"idempotency": "none",
|
|
8793
|
+
"destructive": false,
|
|
8794
|
+
"audience": "both",
|
|
8795
|
+
"request": {
|
|
8796
|
+
"params": false,
|
|
8797
|
+
"query": false,
|
|
8798
|
+
"body": true
|
|
8799
|
+
},
|
|
8800
|
+
"response": {
|
|
8801
|
+
"kind": "json",
|
|
8802
|
+
"body": true
|
|
8803
|
+
}
|
|
8804
|
+
},
|
|
8805
|
+
{
|
|
8806
|
+
"key": "taskAmend",
|
|
8807
|
+
"method": "POST",
|
|
8808
|
+
"path": "/tasks/amend",
|
|
8809
|
+
"fullPath": "/internal/agent-api/tasks/amend",
|
|
8810
|
+
"client": {
|
|
8811
|
+
"resource": "tasks",
|
|
8812
|
+
"method": "amend"
|
|
8813
|
+
},
|
|
8814
|
+
"capability": "tasks",
|
|
8815
|
+
"description": "Amend a task card with an append-only audit event.",
|
|
8816
|
+
"sideEffect": "write",
|
|
8817
|
+
"idempotency": "none",
|
|
8818
|
+
"destructive": true,
|
|
8819
|
+
"audience": "both",
|
|
8820
|
+
"request": {
|
|
8821
|
+
"params": false,
|
|
8822
|
+
"query": false,
|
|
8823
|
+
"body": true
|
|
8824
|
+
},
|
|
8825
|
+
"response": {
|
|
8826
|
+
"kind": "json",
|
|
8827
|
+
"body": true
|
|
8828
|
+
}
|
|
8829
|
+
},
|
|
8830
|
+
{
|
|
8831
|
+
"key": "taskHistory",
|
|
8832
|
+
"method": "GET",
|
|
8833
|
+
"path": "/tasks/history",
|
|
8834
|
+
"fullPath": "/internal/agent-api/tasks/history",
|
|
8835
|
+
"client": {
|
|
8836
|
+
"resource": "tasks",
|
|
8837
|
+
"method": "history"
|
|
8838
|
+
},
|
|
8839
|
+
"capability": "tasks",
|
|
8840
|
+
"description": "Read a task's append-only lifecycle and amendment history.",
|
|
8841
|
+
"sideEffect": "read",
|
|
8842
|
+
"idempotency": "natural",
|
|
8843
|
+
"destructive": false,
|
|
8844
|
+
"audience": "both",
|
|
8845
|
+
"request": {
|
|
8846
|
+
"params": false,
|
|
8847
|
+
"query": true,
|
|
8848
|
+
"body": false
|
|
8849
|
+
},
|
|
8850
|
+
"response": {
|
|
8851
|
+
"kind": "json",
|
|
8852
|
+
"body": true
|
|
8853
|
+
}
|
|
8854
|
+
},
|
|
8855
|
+
{
|
|
8856
|
+
"key": "migrationBegin",
|
|
8857
|
+
"method": "POST",
|
|
8858
|
+
"path": "/migrations",
|
|
8859
|
+
"fullPath": "/internal/agent-api/migrations",
|
|
8860
|
+
"client": {
|
|
8861
|
+
"resource": "migrations",
|
|
8862
|
+
"method": "begin"
|
|
8863
|
+
},
|
|
8864
|
+
"capability": "server",
|
|
8865
|
+
"description": "Begin a migration for the bound agent credential.",
|
|
8866
|
+
"sideEffect": "write",
|
|
8867
|
+
"idempotency": "none",
|
|
8868
|
+
"destructive": true,
|
|
8869
|
+
"audience": "managed",
|
|
8870
|
+
"request": {
|
|
8871
|
+
"params": false,
|
|
8872
|
+
"query": false,
|
|
8873
|
+
"body": true
|
|
8874
|
+
},
|
|
8875
|
+
"response": {
|
|
8876
|
+
"kind": "json",
|
|
8877
|
+
"body": true
|
|
8878
|
+
}
|
|
8879
|
+
},
|
|
8880
|
+
{
|
|
8881
|
+
"key": "migrationStatus",
|
|
8882
|
+
"method": "GET",
|
|
8883
|
+
"path": "/migrations/current",
|
|
8884
|
+
"fullPath": "/internal/agent-api/migrations/current",
|
|
8885
|
+
"client": {
|
|
8886
|
+
"resource": "migrations",
|
|
8887
|
+
"method": "status"
|
|
8888
|
+
},
|
|
8889
|
+
"capability": "read",
|
|
8890
|
+
"description": "Read the active migration for the bound agent credential, if any.",
|
|
8891
|
+
"sideEffect": "read",
|
|
8892
|
+
"idempotency": "natural",
|
|
8893
|
+
"destructive": false,
|
|
8894
|
+
"audience": "both",
|
|
8895
|
+
"request": {
|
|
8896
|
+
"params": false,
|
|
8897
|
+
"query": false,
|
|
8898
|
+
"body": false
|
|
8899
|
+
},
|
|
8900
|
+
"response": {
|
|
8901
|
+
"kind": "json",
|
|
8902
|
+
"body": true
|
|
8903
|
+
}
|
|
8904
|
+
},
|
|
8905
|
+
{
|
|
8906
|
+
"key": "migrationReady",
|
|
8907
|
+
"method": "POST",
|
|
8908
|
+
"path": "/migrations/ready",
|
|
8909
|
+
"fullPath": "/internal/agent-api/migrations/ready",
|
|
8910
|
+
"client": {
|
|
8911
|
+
"resource": "migrations",
|
|
8912
|
+
"method": "ready"
|
|
8913
|
+
},
|
|
8914
|
+
"capability": "read",
|
|
8915
|
+
"description": "Mark the bound agent's active migration prep phase ready.",
|
|
8916
|
+
"sideEffect": "write",
|
|
8917
|
+
"idempotency": "natural",
|
|
8918
|
+
"destructive": false,
|
|
8919
|
+
"audience": "managed",
|
|
8920
|
+
"request": {
|
|
8921
|
+
"params": false,
|
|
8922
|
+
"query": false,
|
|
8923
|
+
"body": true
|
|
8924
|
+
},
|
|
8925
|
+
"response": {
|
|
8926
|
+
"kind": "json",
|
|
8927
|
+
"body": true
|
|
8928
|
+
}
|
|
8929
|
+
},
|
|
8930
|
+
{
|
|
8931
|
+
"key": "migrationArrived",
|
|
8932
|
+
"method": "POST",
|
|
8933
|
+
"path": "/migrations/arrived",
|
|
8934
|
+
"fullPath": "/internal/agent-api/migrations/arrived",
|
|
8935
|
+
"client": {
|
|
8936
|
+
"resource": "migrations",
|
|
8937
|
+
"method": "arrived"
|
|
8938
|
+
},
|
|
8939
|
+
"capability": "read",
|
|
8940
|
+
"description": "Mark the bound agent's active migration arrival phase complete.",
|
|
8941
|
+
"sideEffect": "write",
|
|
8942
|
+
"idempotency": "natural",
|
|
8943
|
+
"destructive": false,
|
|
8944
|
+
"audience": "managed",
|
|
8945
|
+
"request": {
|
|
8946
|
+
"params": false,
|
|
8947
|
+
"query": false,
|
|
8948
|
+
"body": true
|
|
8949
|
+
},
|
|
8950
|
+
"response": {
|
|
8951
|
+
"kind": "json",
|
|
8952
|
+
"body": true
|
|
8953
|
+
}
|
|
8954
|
+
},
|
|
8955
|
+
{
|
|
8956
|
+
"key": "reminderList",
|
|
8957
|
+
"method": "GET",
|
|
8958
|
+
"path": "/reminders",
|
|
8959
|
+
"fullPath": "/internal/agent-api/reminders",
|
|
8960
|
+
"client": {
|
|
8961
|
+
"resource": "reminders",
|
|
8962
|
+
"method": "list"
|
|
8963
|
+
},
|
|
8964
|
+
"capability": "read",
|
|
8965
|
+
"description": "List reminders owned by the bound agent credential.",
|
|
8966
|
+
"sideEffect": "read",
|
|
8967
|
+
"idempotency": "natural",
|
|
8968
|
+
"destructive": false,
|
|
8969
|
+
"audience": "both",
|
|
8970
|
+
"request": {
|
|
8971
|
+
"params": false,
|
|
8972
|
+
"query": true,
|
|
8973
|
+
"body": false
|
|
8974
|
+
},
|
|
8975
|
+
"response": {
|
|
8976
|
+
"kind": "json",
|
|
8977
|
+
"body": true
|
|
8978
|
+
}
|
|
8979
|
+
},
|
|
8980
|
+
{
|
|
8981
|
+
"key": "reminderCreate",
|
|
8982
|
+
"method": "POST",
|
|
8983
|
+
"path": "/reminders",
|
|
8984
|
+
"fullPath": "/internal/agent-api/reminders",
|
|
8985
|
+
"client": {
|
|
8986
|
+
"resource": "reminders",
|
|
8987
|
+
"method": "create"
|
|
8988
|
+
},
|
|
8989
|
+
"capability": "tasks",
|
|
8990
|
+
"description": "Create a reminder owned by the bound agent credential.",
|
|
8991
|
+
"sideEffect": "write",
|
|
8992
|
+
"idempotency": "none",
|
|
8993
|
+
"destructive": false,
|
|
8994
|
+
"audience": "managed",
|
|
8995
|
+
"request": {
|
|
8996
|
+
"params": false,
|
|
8997
|
+
"query": false,
|
|
8998
|
+
"body": true
|
|
8999
|
+
},
|
|
9000
|
+
"response": {
|
|
9001
|
+
"kind": "json",
|
|
9002
|
+
"body": true
|
|
9003
|
+
}
|
|
9004
|
+
},
|
|
9005
|
+
{
|
|
9006
|
+
"key": "reminderCancel",
|
|
9007
|
+
"method": "DELETE",
|
|
9008
|
+
"path": "/reminders/:reminderId",
|
|
9009
|
+
"fullPath": "/internal/agent-api/reminders/:reminderId",
|
|
9010
|
+
"client": {
|
|
9011
|
+
"resource": "reminders",
|
|
9012
|
+
"method": "cancel"
|
|
9013
|
+
},
|
|
9014
|
+
"capability": "tasks",
|
|
9015
|
+
"description": "Cancel a scheduled or fired reminder owned by the bound agent credential.",
|
|
9016
|
+
"sideEffect": "write",
|
|
9017
|
+
"idempotency": "natural",
|
|
9018
|
+
"destructive": true,
|
|
9019
|
+
"audience": "both",
|
|
9020
|
+
"request": {
|
|
9021
|
+
"params": true,
|
|
9022
|
+
"query": false,
|
|
9023
|
+
"body": false
|
|
9024
|
+
},
|
|
9025
|
+
"response": {
|
|
9026
|
+
"kind": "json",
|
|
9027
|
+
"body": true
|
|
9028
|
+
}
|
|
9029
|
+
},
|
|
9030
|
+
{
|
|
9031
|
+
"key": "reminderSnooze",
|
|
9032
|
+
"method": "POST",
|
|
9033
|
+
"path": "/reminders/:reminderId/snooze",
|
|
9034
|
+
"fullPath": "/internal/agent-api/reminders/:reminderId/snooze",
|
|
9035
|
+
"client": {
|
|
9036
|
+
"resource": "reminders",
|
|
9037
|
+
"method": "snooze"
|
|
9038
|
+
},
|
|
9039
|
+
"capability": "tasks",
|
|
9040
|
+
"description": "Snooze a scheduled or fired reminder owned by the bound agent credential.",
|
|
9041
|
+
"sideEffect": "write",
|
|
9042
|
+
"idempotency": "natural",
|
|
9043
|
+
"destructive": true,
|
|
9044
|
+
"audience": "managed",
|
|
9045
|
+
"request": {
|
|
9046
|
+
"params": true,
|
|
9047
|
+
"query": false,
|
|
9048
|
+
"body": true
|
|
9049
|
+
},
|
|
9050
|
+
"response": {
|
|
9051
|
+
"kind": "json",
|
|
9052
|
+
"body": true
|
|
9053
|
+
}
|
|
9054
|
+
},
|
|
9055
|
+
{
|
|
9056
|
+
"key": "reminderUpdate",
|
|
9057
|
+
"method": "PATCH",
|
|
9058
|
+
"path": "/reminders/:reminderId",
|
|
9059
|
+
"fullPath": "/internal/agent-api/reminders/:reminderId",
|
|
9060
|
+
"client": {
|
|
9061
|
+
"resource": "reminders",
|
|
9062
|
+
"method": "update"
|
|
9063
|
+
},
|
|
9064
|
+
"capability": "tasks",
|
|
9065
|
+
"description": "Update a scheduled reminder owned by the bound agent credential.",
|
|
9066
|
+
"sideEffect": "write",
|
|
9067
|
+
"idempotency": "natural",
|
|
9068
|
+
"destructive": true,
|
|
9069
|
+
"audience": "managed",
|
|
9070
|
+
"request": {
|
|
9071
|
+
"params": true,
|
|
9072
|
+
"query": false,
|
|
9073
|
+
"body": true
|
|
9074
|
+
},
|
|
9075
|
+
"response": {
|
|
9076
|
+
"kind": "json",
|
|
9077
|
+
"body": true
|
|
9078
|
+
}
|
|
9079
|
+
},
|
|
9080
|
+
{
|
|
9081
|
+
"key": "appSourceAck",
|
|
9082
|
+
"method": "POST",
|
|
9083
|
+
"path": "/app-sources/ack",
|
|
9084
|
+
"fullPath": "/internal/agent-api/app-sources/ack",
|
|
9085
|
+
"client": {
|
|
9086
|
+
"resource": "appSources",
|
|
9087
|
+
"method": "ack"
|
|
9088
|
+
},
|
|
9089
|
+
"capability": "tasks",
|
|
9090
|
+
"description": "Authorize and acknowledge one exact app-source Inbox item revision.",
|
|
9091
|
+
"sideEffect": "write",
|
|
9092
|
+
"idempotency": "natural",
|
|
9093
|
+
"destructive": false,
|
|
9094
|
+
"audience": "both",
|
|
9095
|
+
"request": {
|
|
9096
|
+
"params": false,
|
|
9097
|
+
"query": false,
|
|
9098
|
+
"body": true
|
|
9099
|
+
},
|
|
9100
|
+
"response": {
|
|
9101
|
+
"kind": "json",
|
|
9102
|
+
"body": true
|
|
9103
|
+
}
|
|
9104
|
+
},
|
|
9105
|
+
{
|
|
9106
|
+
"key": "reminderLog",
|
|
9107
|
+
"method": "GET",
|
|
9108
|
+
"path": "/reminders/:reminderId/log",
|
|
9109
|
+
"fullPath": "/internal/agent-api/reminders/:reminderId/log",
|
|
9110
|
+
"client": {
|
|
9111
|
+
"resource": "reminders",
|
|
9112
|
+
"method": "log"
|
|
9113
|
+
},
|
|
9114
|
+
"capability": "read",
|
|
9115
|
+
"description": "Read lifecycle events for a reminder owned by the bound agent credential.",
|
|
9116
|
+
"sideEffect": "read",
|
|
9117
|
+
"idempotency": "natural",
|
|
9118
|
+
"destructive": false,
|
|
9119
|
+
"audience": "both",
|
|
9120
|
+
"request": {
|
|
9121
|
+
"params": true,
|
|
9122
|
+
"query": false,
|
|
9123
|
+
"body": false
|
|
9124
|
+
},
|
|
9125
|
+
"response": {
|
|
9126
|
+
"kind": "json",
|
|
9127
|
+
"body": true
|
|
9128
|
+
}
|
|
9129
|
+
},
|
|
9130
|
+
{
|
|
9131
|
+
"key": "appConfigGet",
|
|
9132
|
+
"method": "GET",
|
|
9133
|
+
"path": "/apps/:appId/config",
|
|
9134
|
+
"fullPath": "/internal/agent-api/apps/:appId/config",
|
|
9135
|
+
"client": {
|
|
9136
|
+
"resource": "apps",
|
|
9137
|
+
"method": "getConfig"
|
|
9138
|
+
},
|
|
9139
|
+
"capability": "read",
|
|
9140
|
+
"description": "Read effective durable configuration for a built-in RAP App owned by the bound agent.",
|
|
9141
|
+
"sideEffect": "read",
|
|
9142
|
+
"idempotency": "natural",
|
|
9143
|
+
"destructive": false,
|
|
9144
|
+
"audience": "both",
|
|
9145
|
+
"request": {
|
|
9146
|
+
"params": true,
|
|
9147
|
+
"query": false,
|
|
9148
|
+
"body": false
|
|
9149
|
+
},
|
|
9150
|
+
"response": {
|
|
9151
|
+
"kind": "json",
|
|
9152
|
+
"body": true
|
|
9153
|
+
}
|
|
9154
|
+
},
|
|
9155
|
+
{
|
|
9156
|
+
"key": "appConfigPatch",
|
|
9157
|
+
"method": "PATCH",
|
|
9158
|
+
"path": "/apps/:appId/config",
|
|
9159
|
+
"fullPath": "/internal/agent-api/apps/:appId/config",
|
|
9160
|
+
"client": {
|
|
9161
|
+
"resource": "apps",
|
|
9162
|
+
"method": "patchConfig"
|
|
9163
|
+
},
|
|
9164
|
+
"capability": "tasks",
|
|
9165
|
+
"description": "Atomically update durable configuration for a built-in RAP App owned by the bound agent.",
|
|
9166
|
+
"sideEffect": "write",
|
|
9167
|
+
"idempotency": "none",
|
|
9168
|
+
"destructive": true,
|
|
9169
|
+
"audience": "both",
|
|
9170
|
+
"request": {
|
|
9171
|
+
"params": true,
|
|
9172
|
+
"query": false,
|
|
9173
|
+
"body": true
|
|
9174
|
+
},
|
|
9175
|
+
"response": {
|
|
9176
|
+
"kind": "json",
|
|
9177
|
+
"body": true
|
|
9178
|
+
}
|
|
9179
|
+
},
|
|
9180
|
+
{
|
|
9181
|
+
"key": "profileShow",
|
|
9182
|
+
"method": "GET",
|
|
9183
|
+
"path": "/profile",
|
|
9184
|
+
"fullPath": "/internal/agent-api/profile",
|
|
9185
|
+
"client": {
|
|
9186
|
+
"resource": "profile",
|
|
9187
|
+
"method": "show"
|
|
9188
|
+
},
|
|
9189
|
+
"capability": "read",
|
|
9190
|
+
"description": "Show the bound agent profile, or another visible profile when target is provided.",
|
|
9191
|
+
"sideEffect": "read",
|
|
9192
|
+
"idempotency": "natural",
|
|
9193
|
+
"destructive": false,
|
|
9194
|
+
"audience": "both",
|
|
9195
|
+
"request": {
|
|
9196
|
+
"params": false,
|
|
9197
|
+
"query": true,
|
|
9198
|
+
"body": false
|
|
9199
|
+
},
|
|
9200
|
+
"response": {
|
|
9201
|
+
"kind": "json",
|
|
9202
|
+
"body": true
|
|
9203
|
+
}
|
|
9204
|
+
},
|
|
9205
|
+
{
|
|
9206
|
+
"key": "profileUpdate",
|
|
9207
|
+
"method": "POST",
|
|
9208
|
+
"path": "/profile",
|
|
9209
|
+
"fullPath": "/internal/agent-api/profile",
|
|
9210
|
+
"client": {
|
|
9211
|
+
"resource": "profile",
|
|
9212
|
+
"method": "update"
|
|
9213
|
+
},
|
|
9214
|
+
"capability": "send",
|
|
9215
|
+
"description": "Update the bound agent profile metadata.",
|
|
9216
|
+
"sideEffect": "write",
|
|
9217
|
+
"idempotency": "natural",
|
|
9218
|
+
"destructive": true,
|
|
9219
|
+
"audience": "both",
|
|
9220
|
+
"request": {
|
|
9221
|
+
"params": false,
|
|
9222
|
+
"query": false,
|
|
9223
|
+
"body": true
|
|
9224
|
+
},
|
|
9225
|
+
"response": {
|
|
9226
|
+
"kind": "json",
|
|
9227
|
+
"body": true
|
|
9228
|
+
}
|
|
9229
|
+
},
|
|
9230
|
+
{
|
|
9231
|
+
"key": "profileAvatarUpdate",
|
|
9232
|
+
"method": "POST",
|
|
9233
|
+
"path": "/profile/avatar",
|
|
9234
|
+
"fullPath": "/internal/agent-api/profile/avatar",
|
|
9235
|
+
"client": {
|
|
9236
|
+
"resource": "profile",
|
|
9237
|
+
"method": "updateAvatar"
|
|
9238
|
+
},
|
|
9239
|
+
"capability": "send",
|
|
9240
|
+
"description": "Update the bound agent profile avatar using multipart form data.",
|
|
9241
|
+
"sideEffect": "write",
|
|
9242
|
+
"idempotency": "natural",
|
|
9243
|
+
"destructive": true,
|
|
9244
|
+
"audience": "both",
|
|
9245
|
+
"request": {
|
|
9246
|
+
"params": false,
|
|
9247
|
+
"query": false,
|
|
9248
|
+
"body": false
|
|
9249
|
+
},
|
|
9250
|
+
"response": {
|
|
9251
|
+
"kind": "json",
|
|
9252
|
+
"body": true
|
|
9253
|
+
}
|
|
9254
|
+
},
|
|
9255
|
+
{
|
|
9256
|
+
"key": "integrationList",
|
|
9257
|
+
"method": "GET",
|
|
9258
|
+
"path": "/integrations",
|
|
9259
|
+
"fullPath": "/internal/agent-api/integrations",
|
|
9260
|
+
"client": {
|
|
9261
|
+
"resource": "integrations",
|
|
9262
|
+
"method": "list"
|
|
9263
|
+
},
|
|
9264
|
+
"capability": "read",
|
|
9265
|
+
"description": "List available integration services and active agent logins.",
|
|
9266
|
+
"sideEffect": "read",
|
|
9267
|
+
"idempotency": "natural",
|
|
9268
|
+
"destructive": false,
|
|
9269
|
+
"audience": "both",
|
|
9270
|
+
"request": {
|
|
9271
|
+
"params": false,
|
|
9272
|
+
"query": false,
|
|
9273
|
+
"body": false
|
|
9274
|
+
},
|
|
9275
|
+
"response": {
|
|
9276
|
+
"kind": "json",
|
|
9277
|
+
"body": true
|
|
9278
|
+
}
|
|
9279
|
+
},
|
|
9280
|
+
{
|
|
9281
|
+
"key": "integrationMarketplaceSearch",
|
|
9282
|
+
"method": "GET",
|
|
9283
|
+
"path": "/integrations/marketplace",
|
|
9284
|
+
"fullPath": "/internal/agent-api/integrations/marketplace",
|
|
9285
|
+
"client": {
|
|
9286
|
+
"resource": "integrations",
|
|
9287
|
+
"method": "marketplace"
|
|
9288
|
+
},
|
|
9289
|
+
"capability": "read",
|
|
9290
|
+
"description": "Search or list public Marketplace apps without changing the installed integration inventory.",
|
|
9291
|
+
"sideEffect": "read",
|
|
9292
|
+
"idempotency": "natural",
|
|
9293
|
+
"destructive": false,
|
|
9294
|
+
"audience": "both",
|
|
9295
|
+
"request": {
|
|
9296
|
+
"params": false,
|
|
9297
|
+
"query": true,
|
|
9298
|
+
"body": false
|
|
9299
|
+
},
|
|
9300
|
+
"response": {
|
|
9301
|
+
"kind": "json",
|
|
9302
|
+
"body": true
|
|
9303
|
+
}
|
|
9304
|
+
},
|
|
9305
|
+
{
|
|
9306
|
+
"key": "integrationLogin",
|
|
9307
|
+
"method": "POST",
|
|
9308
|
+
"path": "/integrations/login",
|
|
9309
|
+
"fullPath": "/internal/agent-api/integrations/login",
|
|
9310
|
+
"client": {
|
|
9311
|
+
"resource": "integrations",
|
|
9312
|
+
"method": "login"
|
|
9313
|
+
},
|
|
9314
|
+
"capability": "read",
|
|
9315
|
+
"description": "Provision or reuse this agent's login for a registered integration service.",
|
|
9316
|
+
"sideEffect": "write",
|
|
9317
|
+
"idempotency": "natural",
|
|
9318
|
+
"destructive": false,
|
|
9319
|
+
"audience": "both",
|
|
9320
|
+
"request": {
|
|
9321
|
+
"params": false,
|
|
9322
|
+
"query": false,
|
|
9323
|
+
"body": true
|
|
9324
|
+
},
|
|
9325
|
+
"response": {
|
|
9326
|
+
"kind": "json",
|
|
9327
|
+
"body": true
|
|
9328
|
+
}
|
|
9329
|
+
},
|
|
9330
|
+
{
|
|
9331
|
+
"key": "integrationAppPrepare",
|
|
9332
|
+
"method": "POST",
|
|
9333
|
+
"path": "/integrations/app/prepare",
|
|
9334
|
+
"fullPath": "/internal/agent-api/integrations/app/prepare",
|
|
9335
|
+
"client": {
|
|
9336
|
+
"resource": "integrations",
|
|
9337
|
+
"method": "prepareApp"
|
|
9338
|
+
},
|
|
9339
|
+
"capability": "read",
|
|
9340
|
+
"description": "Prepare a third-party integration registration/update action card.",
|
|
9341
|
+
"sideEffect": "write",
|
|
9342
|
+
"idempotency": "none",
|
|
9343
|
+
"destructive": false,
|
|
9344
|
+
"audience": "both",
|
|
9345
|
+
"request": {
|
|
9346
|
+
"params": false,
|
|
9347
|
+
"query": false,
|
|
9348
|
+
"body": true
|
|
9349
|
+
},
|
|
9350
|
+
"response": {
|
|
9351
|
+
"kind": "json",
|
|
9352
|
+
"body": true
|
|
9353
|
+
}
|
|
9354
|
+
},
|
|
9355
|
+
{
|
|
9356
|
+
"key": "integrationAppRotateSecret",
|
|
9357
|
+
"method": "POST",
|
|
9358
|
+
"path": "/integrations/app/rotate-secret",
|
|
9359
|
+
"fullPath": "/internal/agent-api/integrations/app/rotate-secret",
|
|
9360
|
+
"client": {
|
|
9361
|
+
"resource": "integrations",
|
|
9362
|
+
"method": "rotateAppSecret"
|
|
9363
|
+
},
|
|
9364
|
+
"capability": "read",
|
|
9365
|
+
"description": "Regenerate the one-time client secret for a source-owned integration app as its owner, delegated rotate maintainer, or a current server admin; invalidates the previous secret.",
|
|
9366
|
+
"sideEffect": "write",
|
|
9367
|
+
"idempotency": "none",
|
|
9368
|
+
"destructive": true,
|
|
9369
|
+
"audience": "both",
|
|
9370
|
+
"request": {
|
|
9371
|
+
"params": false,
|
|
9372
|
+
"query": false,
|
|
9373
|
+
"body": true
|
|
9374
|
+
},
|
|
9375
|
+
"response": {
|
|
9376
|
+
"kind": "json",
|
|
9377
|
+
"body": true
|
|
9378
|
+
}
|
|
9379
|
+
},
|
|
9380
|
+
{
|
|
9381
|
+
"key": "integrationAppTransferOwner",
|
|
9382
|
+
"method": "POST",
|
|
9383
|
+
"path": "/integrations/app/transfer-owner",
|
|
9384
|
+
"fullPath": "/internal/agent-api/integrations/app/transfer-owner",
|
|
9385
|
+
"client": {
|
|
9386
|
+
"resource": "integrations",
|
|
9387
|
+
"method": "transferAppOwner"
|
|
9388
|
+
},
|
|
9389
|
+
"capability": "read",
|
|
9390
|
+
"description": "Transfer a source-owned integration app to another same-server agent as its owner or a current server admin.",
|
|
9391
|
+
"sideEffect": "write",
|
|
9392
|
+
"idempotency": "natural",
|
|
9393
|
+
"destructive": true,
|
|
9394
|
+
"audience": "both",
|
|
9395
|
+
"request": {
|
|
9396
|
+
"params": false,
|
|
9397
|
+
"query": false,
|
|
9398
|
+
"body": true
|
|
9399
|
+
},
|
|
9400
|
+
"response": {
|
|
9401
|
+
"kind": "json",
|
|
9402
|
+
"body": true
|
|
9403
|
+
}
|
|
9404
|
+
},
|
|
9405
|
+
{
|
|
9406
|
+
"key": "integrationAppUpdate",
|
|
9407
|
+
"method": "POST",
|
|
9408
|
+
"path": "/integrations/app/update",
|
|
9409
|
+
"fullPath": "/internal/agent-api/integrations/app/update",
|
|
9410
|
+
"client": {
|
|
9411
|
+
"resource": "integrations",
|
|
9412
|
+
"method": "updateApp"
|
|
9413
|
+
},
|
|
9414
|
+
"capability": "read",
|
|
9415
|
+
"description": "Update a source-owned integration app. The current app owner or a current server admin may update it.",
|
|
9416
|
+
"sideEffect": "write",
|
|
9417
|
+
"idempotency": "natural",
|
|
9418
|
+
"destructive": true,
|
|
9419
|
+
"audience": "both",
|
|
9420
|
+
"request": {
|
|
9421
|
+
"params": false,
|
|
9422
|
+
"query": false,
|
|
9423
|
+
"body": true
|
|
9424
|
+
},
|
|
9425
|
+
"response": {
|
|
9426
|
+
"kind": "json",
|
|
9427
|
+
"body": true
|
|
9428
|
+
}
|
|
9429
|
+
},
|
|
9430
|
+
{
|
|
9431
|
+
"key": "integrationAppManage",
|
|
9432
|
+
"method": "POST",
|
|
9433
|
+
"path": "/integrations/app/manage",
|
|
9434
|
+
"fullPath": "/internal/agent-api/integrations/app/manage",
|
|
9435
|
+
"client": {
|
|
9436
|
+
"resource": "integrations",
|
|
9437
|
+
"method": "manageApp"
|
|
9438
|
+
},
|
|
9439
|
+
"capability": "read",
|
|
9440
|
+
"description": "Manage source-owned app distribution, logo reset, Marketplace requests, or deletion as the app owner or a current server admin.",
|
|
9441
|
+
"sideEffect": "write",
|
|
9442
|
+
"idempotency": "none",
|
|
9443
|
+
"destructive": true,
|
|
9444
|
+
"audience": "both",
|
|
9445
|
+
"request": {
|
|
9446
|
+
"params": false,
|
|
9447
|
+
"query": false,
|
|
9448
|
+
"body": true
|
|
9449
|
+
},
|
|
9450
|
+
"response": {
|
|
9451
|
+
"kind": "json",
|
|
9452
|
+
"body": true
|
|
9453
|
+
}
|
|
9454
|
+
},
|
|
9455
|
+
{
|
|
9456
|
+
"key": "integrationAppLogoUpdate",
|
|
9457
|
+
"method": "POST",
|
|
9458
|
+
"path": "/integrations/app/logo",
|
|
9459
|
+
"fullPath": "/internal/agent-api/integrations/app/logo",
|
|
9460
|
+
"client": {
|
|
9461
|
+
"resource": "integrations",
|
|
9462
|
+
"method": "updateAppLogo"
|
|
9463
|
+
},
|
|
9464
|
+
"capability": "read",
|
|
9465
|
+
"description": "Upload a source-owned app logo as the app owner or a current server admin using multipart form data.",
|
|
9466
|
+
"sideEffect": "write",
|
|
9467
|
+
"idempotency": "natural",
|
|
9468
|
+
"destructive": true,
|
|
9469
|
+
"audience": "both",
|
|
9470
|
+
"request": {
|
|
9471
|
+
"params": false,
|
|
9472
|
+
"query": false,
|
|
9473
|
+
"body": false
|
|
9474
|
+
},
|
|
9475
|
+
"response": {
|
|
9476
|
+
"kind": "json",
|
|
9477
|
+
"body": true
|
|
9478
|
+
}
|
|
9479
|
+
},
|
|
9480
|
+
{
|
|
9481
|
+
"key": "integrationAppList",
|
|
9482
|
+
"method": "GET",
|
|
9483
|
+
"path": "/integrations/app",
|
|
9484
|
+
"fullPath": "/internal/agent-api/integrations/app",
|
|
9485
|
+
"client": {
|
|
9486
|
+
"resource": "integrations",
|
|
9487
|
+
"method": "listApps"
|
|
9488
|
+
},
|
|
9489
|
+
"capability": "read",
|
|
9490
|
+
"description": "List pending app registration cards requested by this agent and manageable source-owned apps; current server admins see every source-owned app in the server.",
|
|
9491
|
+
"sideEffect": "read",
|
|
9492
|
+
"idempotency": "natural",
|
|
9493
|
+
"destructive": false,
|
|
9494
|
+
"audience": "both",
|
|
9495
|
+
"request": {
|
|
9496
|
+
"params": false,
|
|
9497
|
+
"query": false,
|
|
9498
|
+
"body": false
|
|
9499
|
+
},
|
|
9500
|
+
"response": {
|
|
9501
|
+
"kind": "json",
|
|
9502
|
+
"body": true
|
|
9503
|
+
}
|
|
9504
|
+
},
|
|
9505
|
+
{
|
|
9506
|
+
"key": "integrationAppStatus",
|
|
9507
|
+
"method": "GET",
|
|
9508
|
+
"path": "/integrations/app/status",
|
|
9509
|
+
"fullPath": "/internal/agent-api/integrations/app/status",
|
|
9510
|
+
"client": {
|
|
9511
|
+
"resource": "integrations",
|
|
9512
|
+
"method": "getAppStatus"
|
|
9513
|
+
},
|
|
9514
|
+
"capability": "read",
|
|
9515
|
+
"description": "Get one requester-visible registration card or manageable source-owned app without disclosing unauthorized app existence.",
|
|
9516
|
+
"sideEffect": "read",
|
|
9517
|
+
"idempotency": "natural",
|
|
9518
|
+
"destructive": false,
|
|
9519
|
+
"audience": "both",
|
|
9520
|
+
"request": {
|
|
9521
|
+
"params": false,
|
|
9522
|
+
"query": true,
|
|
9523
|
+
"body": false
|
|
9524
|
+
},
|
|
9525
|
+
"response": {
|
|
9526
|
+
"kind": "json",
|
|
9527
|
+
"body": true
|
|
9528
|
+
}
|
|
9529
|
+
},
|
|
9530
|
+
{
|
|
9531
|
+
"key": "actionPrepare",
|
|
9532
|
+
"method": "POST",
|
|
9533
|
+
"path": "/prepare-action",
|
|
9534
|
+
"fullPath": "/internal/agent-api/prepare-action",
|
|
9535
|
+
"client": {
|
|
9536
|
+
"resource": "actions",
|
|
9537
|
+
"method": "prepare"
|
|
9538
|
+
},
|
|
9539
|
+
"capability": "tasks",
|
|
9540
|
+
"description": "Prepare an action card for a human to commit.",
|
|
9541
|
+
"sideEffect": "write",
|
|
9542
|
+
"idempotency": "none",
|
|
9543
|
+
"destructive": false,
|
|
9544
|
+
"audience": "both",
|
|
9545
|
+
"request": {
|
|
9546
|
+
"params": false,
|
|
9547
|
+
"query": false,
|
|
9548
|
+
"body": true
|
|
9549
|
+
},
|
|
9550
|
+
"response": {
|
|
9551
|
+
"kind": "json",
|
|
9552
|
+
"body": true
|
|
9553
|
+
}
|
|
9554
|
+
},
|
|
9555
|
+
{
|
|
9556
|
+
"key": "attachmentUpload",
|
|
9557
|
+
"method": "POST",
|
|
9558
|
+
"path": "/upload",
|
|
9559
|
+
"fullPath": "/internal/agent-api/upload",
|
|
9560
|
+
"client": {
|
|
9561
|
+
"resource": "attachments",
|
|
9562
|
+
"method": "upload"
|
|
9563
|
+
},
|
|
9564
|
+
"capability": "send",
|
|
9565
|
+
"description": "Upload a multipart attachment as the bound agent credential.",
|
|
9566
|
+
"sideEffect": "write",
|
|
9567
|
+
"idempotency": "none",
|
|
9568
|
+
"destructive": false,
|
|
9569
|
+
"audience": "both",
|
|
9570
|
+
"request": {
|
|
9571
|
+
"params": false,
|
|
9572
|
+
"query": false,
|
|
9573
|
+
"body": false
|
|
9574
|
+
},
|
|
9575
|
+
"response": {
|
|
9576
|
+
"kind": "json",
|
|
9577
|
+
"body": true
|
|
9578
|
+
}
|
|
9579
|
+
},
|
|
9580
|
+
{
|
|
9581
|
+
"key": "attachmentUploadCapabilities",
|
|
9582
|
+
"method": "GET",
|
|
9583
|
+
"path": "/attachment-upload-capabilities",
|
|
9584
|
+
"fullPath": "/internal/agent-api/attachment-upload-capabilities",
|
|
9585
|
+
"client": {
|
|
9586
|
+
"resource": "attachments",
|
|
9587
|
+
"method": "uploadCapabilities"
|
|
9588
|
+
},
|
|
9589
|
+
"capability": "send",
|
|
9590
|
+
"description": "Read the server-authoritative direct-upload threshold and plan file limit.",
|
|
9591
|
+
"sideEffect": "read",
|
|
9592
|
+
"idempotency": "natural",
|
|
9593
|
+
"destructive": false,
|
|
9594
|
+
"audience": "both",
|
|
9595
|
+
"request": {
|
|
9596
|
+
"params": false,
|
|
9597
|
+
"query": false,
|
|
9598
|
+
"body": false
|
|
9599
|
+
},
|
|
9600
|
+
"response": {
|
|
9601
|
+
"kind": "json",
|
|
9602
|
+
"body": true
|
|
9603
|
+
}
|
|
9604
|
+
},
|
|
9605
|
+
{
|
|
9606
|
+
"key": "attachmentUploadSessionCreate",
|
|
9607
|
+
"method": "POST",
|
|
9608
|
+
"path": "/attachment-upload-sessions",
|
|
9609
|
+
"fullPath": "/internal/agent-api/attachment-upload-sessions",
|
|
9610
|
+
"client": {
|
|
9611
|
+
"resource": "attachments",
|
|
9612
|
+
"method": "createUploadSession"
|
|
9613
|
+
},
|
|
9614
|
+
"capability": "send",
|
|
9615
|
+
"description": "Create a direct attachment upload session as the bound agent credential.",
|
|
9616
|
+
"sideEffect": "write",
|
|
9617
|
+
"idempotency": "none",
|
|
9618
|
+
"destructive": false,
|
|
9619
|
+
"audience": "both",
|
|
9620
|
+
"request": {
|
|
9621
|
+
"params": false,
|
|
9622
|
+
"query": false,
|
|
9623
|
+
"body": true
|
|
9624
|
+
},
|
|
9625
|
+
"response": {
|
|
9626
|
+
"kind": "json",
|
|
9627
|
+
"body": true
|
|
9628
|
+
}
|
|
9629
|
+
},
|
|
9630
|
+
{
|
|
9631
|
+
"key": "attachmentUploadSessionComplete",
|
|
9632
|
+
"method": "POST",
|
|
9633
|
+
"path": "/attachment-upload-sessions/:uploadId/complete",
|
|
9634
|
+
"fullPath": "/internal/agent-api/attachment-upload-sessions/:uploadId/complete",
|
|
9635
|
+
"client": {
|
|
9636
|
+
"resource": "attachments",
|
|
9637
|
+
"method": "completeUploadSession"
|
|
9638
|
+
},
|
|
9639
|
+
"capability": "send",
|
|
9640
|
+
"description": "Verify and complete a direct attachment upload owned by the bound agent credential.",
|
|
9641
|
+
"sideEffect": "write",
|
|
9642
|
+
"idempotency": "natural",
|
|
9643
|
+
"destructive": false,
|
|
9644
|
+
"audience": "both",
|
|
9645
|
+
"request": {
|
|
9646
|
+
"params": true,
|
|
9647
|
+
"query": false,
|
|
9648
|
+
"body": false
|
|
9649
|
+
},
|
|
9650
|
+
"response": {
|
|
9651
|
+
"kind": "json",
|
|
9652
|
+
"body": true
|
|
9653
|
+
}
|
|
9654
|
+
},
|
|
9655
|
+
{
|
|
9656
|
+
"key": "attachmentUploadSessionCancel",
|
|
9657
|
+
"method": "DELETE",
|
|
9658
|
+
"path": "/attachment-upload-sessions/:uploadId",
|
|
9659
|
+
"fullPath": "/internal/agent-api/attachment-upload-sessions/:uploadId",
|
|
9660
|
+
"client": {
|
|
9661
|
+
"resource": "attachments",
|
|
9662
|
+
"method": "cancelUploadSession"
|
|
9663
|
+
},
|
|
9664
|
+
"capability": "send",
|
|
9665
|
+
"description": "Cancel a direct attachment upload owned by the bound agent credential.",
|
|
9666
|
+
"sideEffect": "write",
|
|
9667
|
+
"idempotency": "natural",
|
|
9668
|
+
"destructive": true,
|
|
9669
|
+
"audience": "both",
|
|
9670
|
+
"request": {
|
|
9671
|
+
"params": true,
|
|
9672
|
+
"query": false,
|
|
9673
|
+
"body": false
|
|
9674
|
+
},
|
|
9675
|
+
"response": {
|
|
9676
|
+
"kind": "json",
|
|
9677
|
+
"body": true
|
|
9678
|
+
}
|
|
9679
|
+
},
|
|
9680
|
+
{
|
|
9681
|
+
"key": "attachmentUploadSessionStatus",
|
|
9682
|
+
"method": "GET",
|
|
9683
|
+
"path": "/attachment-upload-sessions/:uploadId",
|
|
9684
|
+
"fullPath": "/internal/agent-api/attachment-upload-sessions/:uploadId",
|
|
9685
|
+
"client": {
|
|
9686
|
+
"resource": "attachments",
|
|
9687
|
+
"method": "uploadSessionStatus"
|
|
9688
|
+
},
|
|
9689
|
+
"capability": "send",
|
|
9690
|
+
"description": "Read a direct attachment upload session owned by the bound agent credential.",
|
|
9691
|
+
"sideEffect": "read",
|
|
9692
|
+
"idempotency": "natural",
|
|
9693
|
+
"destructive": false,
|
|
9694
|
+
"audience": "both",
|
|
9695
|
+
"request": {
|
|
9696
|
+
"params": true,
|
|
9697
|
+
"query": false,
|
|
9698
|
+
"body": false
|
|
9699
|
+
},
|
|
9700
|
+
"response": {
|
|
9701
|
+
"kind": "json",
|
|
9702
|
+
"body": true
|
|
9703
|
+
}
|
|
9704
|
+
},
|
|
9705
|
+
{
|
|
9706
|
+
"key": "attachmentDownload",
|
|
9707
|
+
"method": "GET",
|
|
9708
|
+
"path": "/attachments/:attachmentId",
|
|
9709
|
+
"fullPath": "/internal/agent-api/attachments/:attachmentId",
|
|
9710
|
+
"client": {
|
|
9711
|
+
"resource": "attachments",
|
|
9712
|
+
"method": "download"
|
|
9713
|
+
},
|
|
9714
|
+
"capability": "read",
|
|
9715
|
+
"description": "Download attachment bytes visible to the bound agent credential.",
|
|
9716
|
+
"sideEffect": "read",
|
|
9717
|
+
"idempotency": "natural",
|
|
9718
|
+
"destructive": false,
|
|
9719
|
+
"audience": "both",
|
|
9720
|
+
"request": {
|
|
9721
|
+
"params": true,
|
|
9722
|
+
"query": false,
|
|
9723
|
+
"body": false
|
|
9724
|
+
},
|
|
9725
|
+
"response": {
|
|
9726
|
+
"kind": "binary",
|
|
9727
|
+
"body": false
|
|
9728
|
+
}
|
|
9729
|
+
},
|
|
9730
|
+
{
|
|
9731
|
+
"key": "attachmentCommentsList",
|
|
9732
|
+
"method": "GET",
|
|
9733
|
+
"path": "/attachments/:attachmentId/comments",
|
|
9734
|
+
"fullPath": "/internal/agent-api/attachments/:attachmentId/comments",
|
|
9735
|
+
"client": {
|
|
9736
|
+
"resource": "attachments",
|
|
9737
|
+
"method": "comments"
|
|
9738
|
+
},
|
|
9739
|
+
"capability": "read",
|
|
9740
|
+
"description": "List comments scoped to an attachment visible to the bound agent credential.",
|
|
9741
|
+
"sideEffect": "read",
|
|
9742
|
+
"idempotency": "natural",
|
|
9743
|
+
"destructive": false,
|
|
9744
|
+
"audience": "both",
|
|
9745
|
+
"request": {
|
|
9746
|
+
"params": true,
|
|
9747
|
+
"query": true,
|
|
9748
|
+
"body": false
|
|
9749
|
+
},
|
|
9750
|
+
"response": {
|
|
9751
|
+
"kind": "json",
|
|
9752
|
+
"body": true
|
|
9753
|
+
}
|
|
9754
|
+
},
|
|
9755
|
+
{
|
|
9756
|
+
"key": "pushWebhookStatus",
|
|
9757
|
+
"method": "GET",
|
|
9758
|
+
"path": "/push-webhook",
|
|
9759
|
+
"fullPath": "/internal/agent-api/push-webhook",
|
|
9760
|
+
"client": {
|
|
9761
|
+
"resource": "pushWebhook",
|
|
9762
|
+
"method": "status"
|
|
9763
|
+
},
|
|
9764
|
+
"capability": "read",
|
|
9765
|
+
"description": "Read the bound External Agent's inbox push registration and delivery status.",
|
|
9766
|
+
"sideEffect": "read",
|
|
9767
|
+
"idempotency": "natural",
|
|
9768
|
+
"destructive": false,
|
|
9769
|
+
"audience": "external",
|
|
9770
|
+
"request": {
|
|
9771
|
+
"params": false,
|
|
9772
|
+
"query": false,
|
|
9773
|
+
"body": false
|
|
9774
|
+
},
|
|
9775
|
+
"response": {
|
|
9776
|
+
"kind": "json",
|
|
9777
|
+
"body": true
|
|
9778
|
+
}
|
|
9779
|
+
},
|
|
9780
|
+
{
|
|
9781
|
+
"key": "pushWebhookRegister",
|
|
9782
|
+
"method": "PUT",
|
|
9783
|
+
"path": "/push-webhook",
|
|
9784
|
+
"fullPath": "/internal/agent-api/push-webhook",
|
|
9785
|
+
"client": {
|
|
9786
|
+
"resource": "pushWebhook",
|
|
9787
|
+
"method": "register"
|
|
9788
|
+
},
|
|
9789
|
+
"capability": "read",
|
|
9790
|
+
"description": "Register or replace the bound External Agent's inbox push webhook (raft-agent-inbox-notice.v1); a new registration replaces the secret and re-enables delivery.",
|
|
9791
|
+
"sideEffect": "write",
|
|
9792
|
+
"idempotency": "natural",
|
|
9793
|
+
"destructive": true,
|
|
9794
|
+
"audience": "external",
|
|
9795
|
+
"request": {
|
|
9796
|
+
"params": false,
|
|
9797
|
+
"query": false,
|
|
9798
|
+
"body": true
|
|
9799
|
+
},
|
|
9800
|
+
"response": {
|
|
9801
|
+
"kind": "json",
|
|
9802
|
+
"body": true
|
|
9803
|
+
}
|
|
9804
|
+
},
|
|
9805
|
+
{
|
|
9806
|
+
"key": "pushWebhookDelete",
|
|
9807
|
+
"method": "DELETE",
|
|
9808
|
+
"path": "/push-webhook",
|
|
9809
|
+
"fullPath": "/internal/agent-api/push-webhook",
|
|
9810
|
+
"client": {
|
|
9811
|
+
"resource": "pushWebhook",
|
|
9812
|
+
"method": "unregister"
|
|
9813
|
+
},
|
|
9814
|
+
"capability": "read",
|
|
9815
|
+
"description": "Remove the bound External Agent's inbox push registration.",
|
|
9816
|
+
"sideEffect": "write",
|
|
9817
|
+
"idempotency": "natural",
|
|
9818
|
+
"destructive": true,
|
|
9819
|
+
"audience": "external",
|
|
9820
|
+
"request": {
|
|
9821
|
+
"params": false,
|
|
9822
|
+
"query": false,
|
|
9823
|
+
"body": false
|
|
9824
|
+
},
|
|
9825
|
+
"response": {
|
|
9826
|
+
"kind": "empty",
|
|
9827
|
+
"body": false
|
|
9828
|
+
}
|
|
9829
|
+
},
|
|
9830
|
+
{
|
|
9831
|
+
"key": "mentionsList",
|
|
9832
|
+
"method": "GET",
|
|
9833
|
+
"path": "/mentions",
|
|
9834
|
+
"fullPath": "/internal/agent-api/mentions",
|
|
9835
|
+
"client": {
|
|
9836
|
+
"resource": "mentions",
|
|
9837
|
+
"method": "list"
|
|
9838
|
+
},
|
|
9839
|
+
"capability": "mentions",
|
|
9840
|
+
"description": "List notifiable @mentions of the bound agent, newest first.",
|
|
9841
|
+
"sideEffect": "read",
|
|
9842
|
+
"idempotency": "natural",
|
|
9843
|
+
"destructive": false,
|
|
9844
|
+
"audience": "both",
|
|
9845
|
+
"request": {
|
|
9846
|
+
"params": false,
|
|
9847
|
+
"query": true,
|
|
9848
|
+
"body": false
|
|
9849
|
+
},
|
|
9850
|
+
"response": {
|
|
9851
|
+
"kind": "json",
|
|
9852
|
+
"body": true
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9855
|
+
];
|
|
9856
|
+
/** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
|
|
9857
|
+
const AGENT_API_MANIFEST_VERSION = "337bf2c7ff90b908";
|
|
9858
|
+
//#endregion
|
|
9859
|
+
//#region src/routes.ts
|
|
9860
|
+
const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
|
|
9861
|
+
const meta = AGENT_API_ROUTE_META[entry.key];
|
|
9862
|
+
return [entry.key, {
|
|
9863
|
+
key: entry.key,
|
|
9864
|
+
method: entry.method,
|
|
9865
|
+
path: entry.path,
|
|
9866
|
+
fullPath: entry.fullPath,
|
|
9867
|
+
client: entry.client,
|
|
9868
|
+
capability: entry.capability,
|
|
9869
|
+
description: entry.description,
|
|
9870
|
+
...meta,
|
|
9871
|
+
retryPolicy: getAgentApiRetryPolicy(meta),
|
|
9872
|
+
annotations: toAgentApiToolAnnotations(meta)
|
|
9873
|
+
}];
|
|
9874
|
+
}));
|
|
9875
|
+
function describeRaftRoute(key) {
|
|
9876
|
+
return ROUTE_INFO[key];
|
|
9877
|
+
}
|
|
9878
|
+
function listRaftRoutes() {
|
|
9879
|
+
return AGENT_API_ROUTE_MANIFEST.map((entry) => ROUTE_INFO[entry.key]);
|
|
9880
|
+
}
|
|
9881
|
+
function clampAttempts(value) {
|
|
9882
|
+
return Math.min(Math.max(1, Math.trunc(value ?? 1)), 5);
|
|
9883
|
+
}
|
|
9884
|
+
/**
|
|
9885
|
+
* Build the routes layer over two transports: a retrying one for routes the
|
|
9886
|
+
* contract marks retry-safe, and a single-attempt one for everything else.
|
|
9887
|
+
* The split is decided per route from `AGENT_API_ROUTE_META`, never by the
|
|
9888
|
+
* caller and never by the HTTP method.
|
|
9889
|
+
*/
|
|
9890
|
+
function createRaftRoutes(options) {
|
|
9891
|
+
const base = {
|
|
9892
|
+
baseUrl: options.serverUrl,
|
|
9893
|
+
fetch: options.fetch,
|
|
9894
|
+
headers: options.headers,
|
|
9895
|
+
auth: { authorization: options.authorization },
|
|
9896
|
+
throttle: { beforeRequest: options.beforeRequest }
|
|
9897
|
+
};
|
|
9898
|
+
const retrying = createAgentApiClient({ fetch: {
|
|
9899
|
+
...base,
|
|
9900
|
+
retry: { attempts: clampAttempts(options.readAttempts) }
|
|
9901
|
+
} });
|
|
9902
|
+
const single = createAgentApiClient({ fetch: {
|
|
9903
|
+
...base,
|
|
9904
|
+
retry: { attempts: 1 }
|
|
9905
|
+
} });
|
|
9906
|
+
const merged = {};
|
|
9907
|
+
for (const route of Object.values(agentApiContract)) {
|
|
9908
|
+
const key = route.key;
|
|
9909
|
+
const resource = (ROUTE_INFO[key].retryPolicy === "retry" ? retrying : single)[route.client.resource];
|
|
9910
|
+
merged[route.client.resource] ??= {};
|
|
9911
|
+
merged[route.client.resource][route.client.method] = resource[route.client.method];
|
|
9912
|
+
}
|
|
9913
|
+
return {
|
|
9914
|
+
...merged,
|
|
9915
|
+
request: (routeKey, requestOptions) => {
|
|
9916
|
+
return (ROUTE_INFO[routeKey].retryPolicy === "retry" ? retrying : single).request(routeKey, requestOptions);
|
|
9917
|
+
},
|
|
9918
|
+
manifestVersion: AGENT_API_MANIFEST_VERSION,
|
|
9919
|
+
describe: describeRaftRoute,
|
|
9920
|
+
list: listRaftRoutes
|
|
9921
|
+
};
|
|
9922
|
+
}
|
|
9923
|
+
//#endregion
|
|
9924
|
+
//#region src/context.ts
|
|
9925
|
+
const projection = object({
|
|
9926
|
+
agent: object({
|
|
9927
|
+
id: string(),
|
|
9928
|
+
name: string(),
|
|
9929
|
+
displayName: string().nullable(),
|
|
9930
|
+
description: string().nullable(),
|
|
9931
|
+
runtime: string(),
|
|
9932
|
+
external: boolean()
|
|
9933
|
+
}),
|
|
9934
|
+
server: object({
|
|
9935
|
+
id: string(),
|
|
9936
|
+
slug: string(),
|
|
9937
|
+
name: string()
|
|
9938
|
+
}),
|
|
9939
|
+
credential: object({ capabilities: array(string()) }),
|
|
9940
|
+
prompt: object({ text: string() }).nullable()
|
|
9941
|
+
});
|
|
9942
|
+
function failure$1(code, status) {
|
|
9943
|
+
const messages = {
|
|
9944
|
+
TRANSPORT_ERROR: "Agent context transport failed.",
|
|
9945
|
+
HTTP_ERROR: "Agent context returned an HTTP error.",
|
|
9946
|
+
INVALID_RESPONSE: "Agent context response did not match the SDK contract."
|
|
9947
|
+
};
|
|
9948
|
+
return {
|
|
9949
|
+
ok: false,
|
|
9950
|
+
...status === void 0 ? {} : { status },
|
|
9951
|
+
error: {
|
|
9952
|
+
code,
|
|
9953
|
+
message: messages[code]
|
|
9954
|
+
}
|
|
9955
|
+
};
|
|
9956
|
+
}
|
|
9957
|
+
/** Internal adapter for `client.agent.context()`. Read-only; safe to retry. */
|
|
9958
|
+
async function getRaftContext(client) {
|
|
9959
|
+
const result = await client.agent.context();
|
|
9960
|
+
if (!result.ok) return failure$1(result.error.kind === "transport" ? "TRANSPORT_ERROR" : result.error.kind === "http" ? "HTTP_ERROR" : "INVALID_RESPONSE", result.status);
|
|
9961
|
+
const parsed = projection.safeParse(result.data);
|
|
9962
|
+
if (!parsed.success) return failure$1("INVALID_RESPONSE", result.status);
|
|
9963
|
+
const { agent, server, credential, prompt } = parsed.data;
|
|
9964
|
+
return {
|
|
9965
|
+
ok: true,
|
|
9966
|
+
status: result.status,
|
|
9967
|
+
data: {
|
|
9968
|
+
agent: { ...agent },
|
|
9969
|
+
server: { ...server },
|
|
9970
|
+
capabilities: [...credential.capabilities],
|
|
9971
|
+
guide: prompt?.text ?? null
|
|
9972
|
+
}
|
|
9973
|
+
};
|
|
9974
|
+
}
|
|
9975
|
+
//#endregion
|
|
9976
|
+
//#region src/manage.ts
|
|
9977
|
+
const AVATAR_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
9978
|
+
"image/jpeg",
|
|
9979
|
+
"image/png",
|
|
9980
|
+
"image/gif",
|
|
9981
|
+
"image/webp"
|
|
9982
|
+
]);
|
|
9983
|
+
const AVATAR_MAX_BYTES = 5242880;
|
|
9984
|
+
const SERVER_ERROR_CODE$1 = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
9985
|
+
const MESSAGES = {
|
|
9986
|
+
INVALID_REQUEST: "The request did not match the Agent API contract; nothing was sent.",
|
|
9987
|
+
TRANSPORT_ERROR: "The request did not reach the Raft Server.",
|
|
9988
|
+
HTTP_ERROR: "The Raft Server rejected the request.",
|
|
9989
|
+
INVALID_RESPONSE: "The Raft Server response did not match the Agent API contract."
|
|
9990
|
+
};
|
|
9991
|
+
function failure(code, status, errorCode) {
|
|
9992
|
+
const safeErrorCode = typeof errorCode === "string" && SERVER_ERROR_CODE$1.test(errorCode) ? errorCode : void 0;
|
|
9993
|
+
return {
|
|
9994
|
+
ok: false,
|
|
9995
|
+
...status === void 0 ? {} : { status },
|
|
9996
|
+
error: {
|
|
9997
|
+
code,
|
|
9998
|
+
message: MESSAGES[code],
|
|
9999
|
+
...safeErrorCode ? { errorCode: safeErrorCode } : {}
|
|
10000
|
+
}
|
|
10001
|
+
};
|
|
10002
|
+
}
|
|
10003
|
+
/** Map a shared-client result to the SDK's body-free result. */
|
|
10004
|
+
function toRaftApiResult(result) {
|
|
10005
|
+
if (result.ok) return {
|
|
10006
|
+
ok: true,
|
|
10007
|
+
status: result.status,
|
|
10008
|
+
data: result.data
|
|
10009
|
+
};
|
|
10010
|
+
const { error } = result;
|
|
10011
|
+
if (error.kind === "transport") return failure("TRANSPORT_ERROR");
|
|
10012
|
+
if (error.kind === "http") return failure("HTTP_ERROR", error.status, error.errorCode);
|
|
10013
|
+
return failure(error.reason === "request_contract_mismatch" || error.reason === "missing_path_param" || error.reason === "missing_route" ? "INVALID_REQUEST" : "INVALID_RESPONSE", result.status);
|
|
10014
|
+
}
|
|
10015
|
+
const AVATAR_PATH = "/internal/agent-api/profile/avatar";
|
|
10016
|
+
/** One multipart POST; never retried, because the upload is a write. */
|
|
10017
|
+
async function uploadRaftAvatar(transport, upload) {
|
|
10018
|
+
if (!upload || !AVATAR_MIME_TYPES.has(upload.mimeType) || typeof upload.filename !== "string" || !upload.filename.trim()) return failure("INVALID_REQUEST");
|
|
10019
|
+
const blob = upload.data instanceof Blob ? upload.data : upload.data instanceof Uint8Array ? new Blob([Uint8Array.from(upload.data)], { type: upload.mimeType }) : null;
|
|
10020
|
+
if (!blob || blob.size === 0 || blob.size > AVATAR_MAX_BYTES) return failure("INVALID_REQUEST");
|
|
10021
|
+
const form = new FormData();
|
|
10022
|
+
form.append("avatar", blob, upload.filename);
|
|
10023
|
+
await transport.beforeRequest?.({
|
|
10024
|
+
method: "POST",
|
|
10025
|
+
path: AVATAR_PATH
|
|
10026
|
+
});
|
|
10027
|
+
const headers = new Headers(transport.headers);
|
|
10028
|
+
headers.delete("content-type");
|
|
10029
|
+
headers.set("accept", "application/json");
|
|
10030
|
+
headers.set("authorization", transport.authorization);
|
|
10031
|
+
let response;
|
|
10032
|
+
try {
|
|
10033
|
+
response = await transport.fetch(`${transport.serverUrl}${AVATAR_PATH}`, {
|
|
10034
|
+
method: "POST",
|
|
10035
|
+
headers,
|
|
10036
|
+
body: form
|
|
10037
|
+
});
|
|
10038
|
+
} catch {
|
|
10039
|
+
return failure("TRANSPORT_ERROR");
|
|
10040
|
+
}
|
|
10041
|
+
let body = null;
|
|
10042
|
+
try {
|
|
10043
|
+
body = await response.json();
|
|
10044
|
+
} catch {
|
|
10045
|
+
body = null;
|
|
10046
|
+
}
|
|
10047
|
+
if (!response.ok) {
|
|
10048
|
+
const errorCode = body && typeof body === "object" ? body.errorCode : void 0;
|
|
10049
|
+
return failure("HTTP_ERROR", response.status, typeof errorCode === "string" ? errorCode : null);
|
|
10050
|
+
}
|
|
10051
|
+
try {
|
|
10052
|
+
return {
|
|
10053
|
+
ok: true,
|
|
10054
|
+
status: response.status,
|
|
10055
|
+
data: parseAgentApiResponse("profileAvatarUpdate", body)
|
|
10056
|
+
};
|
|
10057
|
+
} catch {
|
|
10058
|
+
return failure("INVALID_RESPONSE", response.status);
|
|
10059
|
+
}
|
|
10060
|
+
}
|
|
10061
|
+
/** `readApi` may retry; `writeApi` must make exactly one attempt. */
|
|
10062
|
+
function createRaftManageClient(readApi, writeApi, avatar) {
|
|
10063
|
+
return {
|
|
10064
|
+
profile: {
|
|
10065
|
+
show: async (request = {}) => toRaftApiResult(await readApi.profile.show(request.target === void 0 ? {} : { target: request.target })),
|
|
10066
|
+
update: async (request) => toRaftApiResult(await writeApi.profile.update(request)),
|
|
10067
|
+
updateAvatar: (upload) => uploadRaftAvatar(avatar, upload)
|
|
10068
|
+
},
|
|
10069
|
+
server: { update: async (request) => toRaftApiResult(await writeApi.server.update(request)) },
|
|
10070
|
+
actions: { prepare: async (request) => toRaftApiResult(await writeApi.actions.prepare(request)) },
|
|
10071
|
+
apps: {
|
|
10072
|
+
getConfig: async (appId) => toRaftApiResult(await readApi.apps.getConfig({ appId })),
|
|
10073
|
+
patchConfig: async (appId, patch) => toRaftApiResult(await writeApi.apps.patchConfig({ appId }, patch))
|
|
10074
|
+
}
|
|
10075
|
+
};
|
|
10076
|
+
}
|
|
10077
|
+
//#endregion
|
|
10078
|
+
//#region src/client.ts
|
|
10079
|
+
var RaftSdkConfigurationError = class extends Error {
|
|
10080
|
+
code;
|
|
10081
|
+
name = "RaftSdkConfigurationError";
|
|
10082
|
+
constructor(code, message) {
|
|
10083
|
+
super(message);
|
|
10084
|
+
this.code = code;
|
|
10085
|
+
}
|
|
10086
|
+
};
|
|
10087
|
+
function requireServerUrl(value) {
|
|
10088
|
+
const serverUrl = value.trim();
|
|
10089
|
+
if (!serverUrl) throw new RaftSdkConfigurationError("MISSING_SERVER_URL", "Raft Server URL is required");
|
|
10090
|
+
let parsed;
|
|
10091
|
+
try {
|
|
10092
|
+
parsed = new URL(serverUrl);
|
|
10093
|
+
} catch {
|
|
10094
|
+
throw new RaftSdkConfigurationError("INVALID_SERVER_URL", "Raft Server URL must be an absolute HTTP(S) URL");
|
|
10095
|
+
}
|
|
10096
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new RaftSdkConfigurationError("INVALID_SERVER_URL", "Raft Server URL must use HTTP or HTTPS");
|
|
10097
|
+
if (parsed.username || parsed.password) throw new RaftSdkConfigurationError("INVALID_SERVER_URL", "Raft Server URL must not contain credentials");
|
|
10098
|
+
return serverUrl.replace(/\/+$/, "");
|
|
10099
|
+
}
|
|
10100
|
+
function requireAgentCredential(value) {
|
|
10101
|
+
const credential = value.trim();
|
|
10102
|
+
if (!credential) throw new RaftSdkConfigurationError("MISSING_AGENT_CREDENTIAL", "Raft Agent credential is required");
|
|
10103
|
+
if (!credential.startsWith("sk_agent_") || credential.length === 9) throw new RaftSdkConfigurationError("INVALID_AGENT_CREDENTIAL", "Raft Agent credential must be a long-lived External Agent credential");
|
|
10104
|
+
return credential;
|
|
10105
|
+
}
|
|
10106
|
+
/**
|
|
10107
|
+
* Create a typed client for the credential-derived `/internal/agent-api/*`
|
|
10108
|
+
* surface. The SDK does not read CLI profiles, environment variables, or the
|
|
10109
|
+
* host user's home directory: the caller chooses and supplies the credential.
|
|
10110
|
+
*/
|
|
10111
|
+
function createRaftClient(options) {
|
|
10112
|
+
const serverUrl = requireServerUrl(options.serverUrl);
|
|
10113
|
+
const auth = { authorization: `Bearer ${requireAgentCredential(options.credential)}` };
|
|
10114
|
+
const messageClient = createAgentApiMessageClient({
|
|
10115
|
+
baseUrl: serverUrl,
|
|
7700
10116
|
fetch: options.fetch,
|
|
7701
10117
|
headers: options.headers,
|
|
7702
10118
|
retry: options.retry,
|
|
@@ -7732,255 +10148,1691 @@ function createRaftClient(options) {
|
|
|
7732
10148
|
auth
|
|
7733
10149
|
} });
|
|
7734
10150
|
return {
|
|
7735
|
-
...messageClient,
|
|
7736
|
-
...createRaftManageClient(agentApi, writeApi, {
|
|
7737
|
-
serverUrl,
|
|
7738
|
-
fetch: options.fetch ?? fetch,
|
|
7739
|
-
headers: Object.fromEntries(new Headers(options.headers)),
|
|
7740
|
-
authorization: auth.authorization,
|
|
7741
|
-
beforeRequest: options.throttle?.beforeRequest
|
|
7742
|
-
}),
|
|
7743
|
-
events: { receive: (request) => receiveRaftEvents(eventsApi, request) },
|
|
7744
|
-
channels: { join: (request) => joinRaftChannelByTarget(agentApi, request) },
|
|
7745
|
-
|
|
10151
|
+
...messageClient,
|
|
10152
|
+
...createRaftManageClient(agentApi, writeApi, {
|
|
10153
|
+
serverUrl,
|
|
10154
|
+
fetch: options.fetch ?? fetch,
|
|
10155
|
+
headers: Object.fromEntries(new Headers(options.headers)),
|
|
10156
|
+
authorization: auth.authorization,
|
|
10157
|
+
beforeRequest: options.throttle?.beforeRequest
|
|
10158
|
+
}),
|
|
10159
|
+
events: { receive: (request) => receiveRaftEvents(eventsApi, request) },
|
|
10160
|
+
channels: { join: (request) => joinRaftChannelByTarget(agentApi, request) },
|
|
10161
|
+
routes: createRaftRoutes({
|
|
10162
|
+
serverUrl,
|
|
10163
|
+
fetch: options.fetch,
|
|
10164
|
+
headers: options.headers,
|
|
10165
|
+
authorization: auth.authorization,
|
|
10166
|
+
readAttempts: options.retry?.attempts,
|
|
10167
|
+
beforeRequest: options.throttle?.beforeRequest
|
|
10168
|
+
}),
|
|
10169
|
+
agent: { context: () => getRaftContext(agentApi) }
|
|
10170
|
+
};
|
|
10171
|
+
}
|
|
10172
|
+
//#endregion
|
|
10173
|
+
//#region ../shared/src/clock.ts
|
|
10174
|
+
function currentTimeMs() {
|
|
10175
|
+
return Date.now();
|
|
10176
|
+
}
|
|
10177
|
+
function currentDate() {
|
|
10178
|
+
return new Date(currentTimeMs());
|
|
10179
|
+
}
|
|
10180
|
+
//#endregion
|
|
10181
|
+
//#region src/credential.ts
|
|
10182
|
+
var RaftCredentialError = class extends Error {
|
|
10183
|
+
code;
|
|
10184
|
+
name = "RaftCredentialError";
|
|
10185
|
+
constructor(code, message) {
|
|
10186
|
+
super(message);
|
|
10187
|
+
this.code = code;
|
|
10188
|
+
}
|
|
10189
|
+
};
|
|
10190
|
+
function isNonEmptyString(value) {
|
|
10191
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
10192
|
+
}
|
|
10193
|
+
function parseIdentityResponse(value) {
|
|
10194
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
10195
|
+
const input = value;
|
|
10196
|
+
if (!isNonEmptyString(input.agentId) || !isNonEmptyString(input.agentName) || input.agentDisplayName !== null && typeof input.agentDisplayName !== "string" || !isNonEmptyString(input.serverId) || !isNonEmptyString(input.credentialId) || !Array.isArray(input.scopes) || !input.scopes.every(isNonEmptyString)) return null;
|
|
10197
|
+
return {
|
|
10198
|
+
agentId: input.agentId,
|
|
10199
|
+
agentName: input.agentName,
|
|
10200
|
+
agentDisplayName: input.agentDisplayName,
|
|
10201
|
+
serverId: input.serverId,
|
|
10202
|
+
credentialId: input.credentialId,
|
|
10203
|
+
scopes: [...input.scopes]
|
|
10204
|
+
};
|
|
10205
|
+
}
|
|
10206
|
+
function parseStoredRaftCredential(value) {
|
|
10207
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
10208
|
+
const input = value;
|
|
10209
|
+
if (input.schemaVersion !== 1 || !isNonEmptyString(input.serverUrl) || !isNonEmptyString(input.credential) || !isNonEmptyString(input.storedAt)) return null;
|
|
10210
|
+
const identity = parseIdentityResponse(input);
|
|
10211
|
+
if (!identity || Number.isNaN(Date.parse(input.storedAt))) return null;
|
|
10212
|
+
let serverUrl;
|
|
10213
|
+
let credential;
|
|
10214
|
+
try {
|
|
10215
|
+
serverUrl = requireServerUrl(input.serverUrl);
|
|
10216
|
+
credential = requireAgentCredential(input.credential);
|
|
10217
|
+
} catch {
|
|
10218
|
+
return null;
|
|
10219
|
+
}
|
|
10220
|
+
return {
|
|
10221
|
+
schemaVersion: 1,
|
|
10222
|
+
serverUrl,
|
|
10223
|
+
credential,
|
|
10224
|
+
storedAt: input.storedAt,
|
|
10225
|
+
...identity
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
10228
|
+
/**
|
|
10229
|
+
* Validate an existing External Agent credential against Raft, derive its
|
|
10230
|
+
* bound identity from the credential-authenticated `whoami` surface, and save
|
|
10231
|
+
* it through the caller's explicit store. This does not mint or rotate a
|
|
10232
|
+
* credential and never returns the credential bytes.
|
|
10233
|
+
*/
|
|
10234
|
+
async function bootstrapRaftCredential(options) {
|
|
10235
|
+
const serverUrl = requireServerUrl(options.serverUrl);
|
|
10236
|
+
const credential = requireAgentCredential(options.credential);
|
|
10237
|
+
const httpFetch = options.fetch ?? globalThis.fetch;
|
|
10238
|
+
let existing;
|
|
10239
|
+
try {
|
|
10240
|
+
existing = await options.store.load();
|
|
10241
|
+
} catch {
|
|
10242
|
+
throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The existing Agent credential could not be checked before saving");
|
|
10243
|
+
}
|
|
10244
|
+
if (existing) {
|
|
10245
|
+
const validatedExisting = parseStoredRaftCredential(existing);
|
|
10246
|
+
if (!validatedExisting) throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The existing Agent credential record is invalid");
|
|
10247
|
+
existing = validatedExisting;
|
|
10248
|
+
if (existing.serverUrl !== serverUrl || existing.credential !== credential) throw new RaftCredentialError("CREDENTIAL_STORE_CONFLICT", "The credential store already contains a different Raft Agent credential");
|
|
10249
|
+
}
|
|
10250
|
+
let response;
|
|
10251
|
+
try {
|
|
10252
|
+
response = await httpFetch(`${serverUrl}/internal/agent-api/`, {
|
|
10253
|
+
method: "GET",
|
|
10254
|
+
headers: { authorization: `Bearer ${credential}` }
|
|
10255
|
+
});
|
|
10256
|
+
} catch {
|
|
10257
|
+
throw new RaftCredentialError("CREDENTIAL_VERIFY_FAILED", "Could not reach the Raft Server to verify the Agent credential");
|
|
10258
|
+
}
|
|
10259
|
+
if (!response.ok) {
|
|
10260
|
+
if (response.status === 401 || response.status === 403) throw new RaftCredentialError("CREDENTIAL_REJECTED", "The Raft Server rejected the Agent credential");
|
|
10261
|
+
throw new RaftCredentialError("CREDENTIAL_VERIFY_FAILED", `The Raft Server could not verify the Agent credential (status ${response.status})`);
|
|
10262
|
+
}
|
|
10263
|
+
let payload;
|
|
10264
|
+
try {
|
|
10265
|
+
payload = await response.json();
|
|
10266
|
+
} catch {
|
|
10267
|
+
throw new RaftCredentialError("CREDENTIAL_RESPONSE_INVALID", "The Raft Server returned an invalid Agent identity response");
|
|
10268
|
+
}
|
|
10269
|
+
const identity = parseIdentityResponse(payload);
|
|
10270
|
+
if (!identity) throw new RaftCredentialError("CREDENTIAL_RESPONSE_INVALID", "The Raft Server returned an invalid Agent identity response");
|
|
10271
|
+
if (!identity.scopes.includes("send")) throw new RaftCredentialError("CREDENTIAL_SCOPE_MISSING", "The Raft Agent credential does not include the send capability required by this SDK");
|
|
10272
|
+
if (existing && (existing.serverId !== identity.serverId || existing.agentId !== identity.agentId)) throw new RaftCredentialError("CREDENTIAL_STORE_CONFLICT", "The credential store is already bound to a different Raft Agent identity");
|
|
10273
|
+
if (existing) return {
|
|
10274
|
+
serverUrl,
|
|
10275
|
+
...identity
|
|
10276
|
+
};
|
|
10277
|
+
const record = {
|
|
10278
|
+
schemaVersion: 1,
|
|
10279
|
+
serverUrl,
|
|
10280
|
+
credential,
|
|
10281
|
+
storedAt: currentDate().toISOString(),
|
|
10282
|
+
...identity
|
|
10283
|
+
};
|
|
10284
|
+
try {
|
|
10285
|
+
await options.store.save(record);
|
|
10286
|
+
} catch (error) {
|
|
10287
|
+
if (error instanceof RaftCredentialError && error.code === "CREDENTIAL_STORE_CONFLICT") throw error;
|
|
10288
|
+
throw new RaftCredentialError("CREDENTIAL_STORE_WRITE_FAILED", "The verified Agent credential could not be saved");
|
|
10289
|
+
}
|
|
10290
|
+
return {
|
|
10291
|
+
serverUrl,
|
|
10292
|
+
...identity
|
|
10293
|
+
};
|
|
10294
|
+
}
|
|
10295
|
+
/** Load the caller-selected credential store and create a normal Raft client. */
|
|
10296
|
+
async function createRaftClientFromStore(options) {
|
|
10297
|
+
let record;
|
|
10298
|
+
try {
|
|
10299
|
+
record = await options.store.load();
|
|
10300
|
+
} catch {
|
|
10301
|
+
throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The stored Agent credential could not be loaded");
|
|
10302
|
+
}
|
|
10303
|
+
if (!record) throw new RaftCredentialError("CREDENTIAL_NOT_FOUND", "No stored Raft Agent credential was found");
|
|
10304
|
+
const validated = parseStoredRaftCredential(record);
|
|
10305
|
+
if (!validated) throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The stored Agent credential is invalid");
|
|
10306
|
+
if (!validated.scopes.includes("send")) throw new RaftCredentialError("CREDENTIAL_SCOPE_MISSING", "The stored Raft Agent credential does not include the send capability required by this SDK");
|
|
10307
|
+
return createRaftClient({
|
|
10308
|
+
serverUrl: validated.serverUrl,
|
|
10309
|
+
credential: validated.credential,
|
|
10310
|
+
fetch: options.fetch,
|
|
10311
|
+
headers: options.headers,
|
|
10312
|
+
retry: options.retry,
|
|
10313
|
+
throttle: options.throttle
|
|
10314
|
+
});
|
|
10315
|
+
}
|
|
10316
|
+
//#endregion
|
|
10317
|
+
//#region src/fileCredentialStore.ts
|
|
10318
|
+
const MAX_CREDENTIAL_FILE_BYTES = 65536;
|
|
10319
|
+
/**
|
|
10320
|
+
* Create a Node.js credential store at one explicit absolute path. The SDK
|
|
10321
|
+
* never discovers CLI profiles or a home directory. Writes use a same-directory
|
|
10322
|
+
* temporary file followed by atomic rename, and the final file is mode 0600.
|
|
10323
|
+
*/
|
|
10324
|
+
function createFileCredentialStore(filePath) {
|
|
10325
|
+
if (!filePath.trim() || !(0, node_path.isAbsolute)(filePath)) throw new RaftSdkConfigurationError("INVALID_CREDENTIAL_PATH", "Raft credential file path must be an explicit absolute path");
|
|
10326
|
+
return {
|
|
10327
|
+
load: () => loadFileCredential(filePath),
|
|
10328
|
+
async save(record) {
|
|
10329
|
+
const validated = parseStoredRaftCredential(record);
|
|
10330
|
+
if (!validated) throw new RaftCredentialError("CREDENTIAL_STORE_WRITE_FAILED", "Refusing to write an invalid Raft Agent credential record");
|
|
10331
|
+
const directory = (0, node_path.dirname)(filePath);
|
|
10332
|
+
const temporaryPath = `${filePath}.${process.pid}.${(0, node_crypto.randomUUID)()}.tmp`;
|
|
10333
|
+
let handle;
|
|
10334
|
+
try {
|
|
10335
|
+
await (0, node_fs_promises.mkdir)(directory, {
|
|
10336
|
+
recursive: true,
|
|
10337
|
+
mode: 448
|
|
10338
|
+
});
|
|
10339
|
+
const existing = await loadFileCredential(filePath);
|
|
10340
|
+
if (existing) {
|
|
10341
|
+
assertSameCredential(existing, validated);
|
|
10342
|
+
return;
|
|
10343
|
+
}
|
|
10344
|
+
handle = await (0, node_fs_promises.open)(temporaryPath, "wx", 384);
|
|
10345
|
+
await handle.writeFile(`${JSON.stringify(validated, null, 2)}\n`, "utf8");
|
|
10346
|
+
await handle.sync();
|
|
10347
|
+
await (0, node_fs_promises.chmod)(temporaryPath, 384);
|
|
10348
|
+
await handle.close();
|
|
10349
|
+
handle = void 0;
|
|
10350
|
+
try {
|
|
10351
|
+
await (0, node_fs_promises.link)(temporaryPath, filePath);
|
|
10352
|
+
} catch (error) {
|
|
10353
|
+
if (error.code !== "EEXIST") throw error;
|
|
10354
|
+
const winner = await loadFileCredential(filePath);
|
|
10355
|
+
if (!winner) throw error;
|
|
10356
|
+
assertSameCredential(winner, validated);
|
|
10357
|
+
}
|
|
10358
|
+
await (0, node_fs_promises.unlink)(temporaryPath);
|
|
10359
|
+
} catch (error) {
|
|
10360
|
+
if (handle) await handle.close().catch(() => void 0);
|
|
10361
|
+
await (0, node_fs_promises.unlink)(temporaryPath).catch(() => void 0);
|
|
10362
|
+
if (error instanceof RaftCredentialError) throw error;
|
|
10363
|
+
throw new RaftCredentialError("CREDENTIAL_STORE_WRITE_FAILED", "The Raft Agent credential could not be written atomically");
|
|
10364
|
+
}
|
|
10365
|
+
}
|
|
10366
|
+
};
|
|
10367
|
+
}
|
|
10368
|
+
async function loadFileCredential(filePath) {
|
|
10369
|
+
let initialMetadata;
|
|
10370
|
+
try {
|
|
10371
|
+
initialMetadata = await (0, node_fs_promises.lstat)(filePath);
|
|
10372
|
+
} catch (error) {
|
|
10373
|
+
if (error.code === "ENOENT") return null;
|
|
10374
|
+
throw fileReadError();
|
|
10375
|
+
}
|
|
10376
|
+
if (!initialMetadata.isFile() || initialMetadata.isSymbolicLink() || initialMetadata.size > MAX_CREDENTIAL_FILE_BYTES) throw fileReadError();
|
|
10377
|
+
let parsed;
|
|
10378
|
+
let handle;
|
|
10379
|
+
try {
|
|
10380
|
+
const noFollow = process.platform === "win32" ? 0 : node_fs.constants.O_NOFOLLOW;
|
|
10381
|
+
handle = await (0, node_fs_promises.open)(filePath, node_fs.constants.O_RDONLY | noFollow);
|
|
10382
|
+
const openedMetadata = await handle.stat();
|
|
10383
|
+
if (!openedMetadata.isFile() || openedMetadata.size > MAX_CREDENTIAL_FILE_BYTES || initialMetadata.dev !== openedMetadata.dev || initialMetadata.ino !== openedMetadata.ino) throw fileReadError();
|
|
10384
|
+
if (process.platform !== "win32" && (openedMetadata.mode & 63) !== 0) throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The Raft credential file permissions are too broad; expected mode 0600");
|
|
10385
|
+
parsed = JSON.parse(await handle.readFile("utf8"));
|
|
10386
|
+
} catch (error) {
|
|
10387
|
+
if (error instanceof RaftCredentialError) throw error;
|
|
10388
|
+
throw fileReadError();
|
|
10389
|
+
} finally {
|
|
10390
|
+
await handle?.close().catch(() => void 0);
|
|
10391
|
+
}
|
|
10392
|
+
const record = parseStoredRaftCredential(parsed);
|
|
10393
|
+
if (!record) throw fileReadError();
|
|
10394
|
+
return record;
|
|
10395
|
+
}
|
|
10396
|
+
function assertSameCredential(existing, candidate) {
|
|
10397
|
+
if (existing.credential !== candidate.credential) throw new RaftCredentialError("CREDENTIAL_STORE_CONFLICT", "The credential store already contains a different Raft Agent credential");
|
|
10398
|
+
}
|
|
10399
|
+
function fileReadError() {
|
|
10400
|
+
return new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The Raft credential file is invalid or unreadable");
|
|
10401
|
+
}
|
|
10402
|
+
//#endregion
|
|
10403
|
+
//#region ../shared/src/agentOps/outcome.ts
|
|
10404
|
+
const SERVER_ERROR_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
10405
|
+
const DEFAULT_MESSAGES = {
|
|
10406
|
+
INVALID_REQUEST: "The request did not match the Agent API contract; nothing was sent.",
|
|
10407
|
+
TRANSPORT_ERROR: "The request did not reach the Raft Server.",
|
|
10408
|
+
HTTP_ERROR: "The Raft Server rejected the request.",
|
|
10409
|
+
INVALID_RESPONSE: "The Raft Server response did not match the Agent API contract.",
|
|
10410
|
+
CAPABILITY_NOT_AUTHORIZED: "This credential does not carry the capability this operation needs.",
|
|
10411
|
+
SCOPE_DENIED: "This agent's scope set does not allow this operation.",
|
|
10412
|
+
NOT_FOUND: "The target or message does not exist or is not visible to this agent.",
|
|
10413
|
+
CONFLICT: "The Raft Server refused the operation because of the current state.",
|
|
10414
|
+
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different message.",
|
|
10415
|
+
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10416
|
+
UNAVAILABLE: "The Raft Server could not serve this operation right now."
|
|
10417
|
+
};
|
|
10418
|
+
const DEFAULT_NEXT_ACTION = {
|
|
10419
|
+
INVALID_REQUEST: "Fix the request arguments; nothing was sent.",
|
|
10420
|
+
TRANSPORT_ERROR: "Check connectivity to the Raft Server and retry if the operation is safe to repeat.",
|
|
10421
|
+
HTTP_ERROR: "Read the Server's error code and adjust the request.",
|
|
10422
|
+
INVALID_RESPONSE: "Upgrade the SDK or report the Server version; the response shape is not the one this SDK knows.",
|
|
10423
|
+
CAPABILITY_NOT_AUTHORIZED: "Ask a human who can mint credentials to include the missing capability.",
|
|
10424
|
+
SCOPE_DENIED: "Ask a human with editAgents authority to extend this agent's scopes.",
|
|
10425
|
+
NOT_FOUND: "Check the target spelling with `raft server info --channels` or resolve the message id first.",
|
|
10426
|
+
CONFLICT: "Read the current state before repeating this operation.",
|
|
10427
|
+
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different message, or resend the identical payload to reconcile.",
|
|
10428
|
+
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10429
|
+
UNAVAILABLE: "Retry in a moment."
|
|
10430
|
+
};
|
|
10431
|
+
const RETRYABLE = {
|
|
10432
|
+
INVALID_REQUEST: false,
|
|
10433
|
+
TRANSPORT_ERROR: true,
|
|
10434
|
+
HTTP_ERROR: false,
|
|
10435
|
+
INVALID_RESPONSE: false,
|
|
10436
|
+
CAPABILITY_NOT_AUTHORIZED: false,
|
|
10437
|
+
SCOPE_DENIED: false,
|
|
10438
|
+
NOT_FOUND: false,
|
|
10439
|
+
CONFLICT: false,
|
|
10440
|
+
IDEMPOTENCY_KEY_REUSED: false,
|
|
10441
|
+
UNSUPPORTED_FOR_EXTERNAL_AGENTS: false,
|
|
10442
|
+
UNAVAILABLE: true
|
|
10443
|
+
};
|
|
10444
|
+
function classifyHttp(status, serverCode) {
|
|
10445
|
+
const code = serverCode?.toLowerCase() ?? "";
|
|
10446
|
+
if (code === "idempotency_key_reused") return "IDEMPOTENCY_KEY_REUSED";
|
|
10447
|
+
if (code === "reminders_unsupported_for_external_agents") return "UNSUPPORTED_FOR_EXTERNAL_AGENTS";
|
|
10448
|
+
if (code === "capability_not_authorized") return "CAPABILITY_NOT_AUTHORIZED";
|
|
10449
|
+
if (code === "scope_denied") return "SCOPE_DENIED";
|
|
10450
|
+
if (status === 404) return "NOT_FOUND";
|
|
10451
|
+
if (status === 409) return "CONFLICT";
|
|
10452
|
+
if (status === 503) return "UNAVAILABLE";
|
|
10453
|
+
if (status >= 500) return "UNAVAILABLE";
|
|
10454
|
+
return "HTTP_ERROR";
|
|
10455
|
+
}
|
|
10456
|
+
function opErrorFromClientError(error, status) {
|
|
10457
|
+
if (error.kind === "transport") return {
|
|
10458
|
+
code: "TRANSPORT_ERROR",
|
|
10459
|
+
message: DEFAULT_MESSAGES.TRANSPORT_ERROR,
|
|
10460
|
+
nextAction: DEFAULT_NEXT_ACTION.TRANSPORT_ERROR,
|
|
10461
|
+
retryable: true
|
|
10462
|
+
};
|
|
10463
|
+
if (error.kind === "http") {
|
|
10464
|
+
const serverCode = typeof error.errorCode === "string" && SERVER_ERROR_CODE.test(error.errorCode) ? error.errorCode : void 0;
|
|
10465
|
+
const code = classifyHttp(error.status, serverCode);
|
|
10466
|
+
const nextAction = typeof error.suggestedNextAction === "string" && error.suggestedNextAction.trim() ? error.suggestedNextAction.trim() : DEFAULT_NEXT_ACTION[code];
|
|
10467
|
+
return {
|
|
10468
|
+
code,
|
|
10469
|
+
message: DEFAULT_MESSAGES[code],
|
|
10470
|
+
status: error.status,
|
|
10471
|
+
...serverCode ? { serverCode } : {},
|
|
10472
|
+
nextAction,
|
|
10473
|
+
retryable: RETRYABLE[code]
|
|
10474
|
+
};
|
|
10475
|
+
}
|
|
10476
|
+
const code = error.reason === "request_contract_mismatch" || error.reason === "missing_path_param" || error.reason === "missing_route" ? "INVALID_REQUEST" : "INVALID_RESPONSE";
|
|
10477
|
+
return {
|
|
10478
|
+
code,
|
|
10479
|
+
message: DEFAULT_MESSAGES[code],
|
|
10480
|
+
...status === void 0 ? {} : { status },
|
|
10481
|
+
nextAction: DEFAULT_NEXT_ACTION[code],
|
|
10482
|
+
retryable: false
|
|
10483
|
+
};
|
|
10484
|
+
}
|
|
10485
|
+
function opError(code, overrides = {}) {
|
|
10486
|
+
return {
|
|
10487
|
+
code,
|
|
10488
|
+
message: DEFAULT_MESSAGES[code],
|
|
10489
|
+
nextAction: DEFAULT_NEXT_ACTION[code],
|
|
10490
|
+
retryable: RETRYABLE[code],
|
|
10491
|
+
...overrides
|
|
7746
10492
|
};
|
|
7747
10493
|
}
|
|
10494
|
+
/** Render an error the way an agent should read it: what failed, the code, and the next action. */
|
|
10495
|
+
function formatOpErrorText(error) {
|
|
10496
|
+
const lines = [`Error: ${error.message}`, `Code: ${error.code}`];
|
|
10497
|
+
if (error.serverCode) lines.push(`Server code: ${error.serverCode}`);
|
|
10498
|
+
lines.push(`Retryable: ${error.retryable ? "yes" : "no"}`);
|
|
10499
|
+
lines.push(`Next action: ${error.nextAction}`);
|
|
10500
|
+
return lines.join("\n");
|
|
10501
|
+
}
|
|
10502
|
+
function failureOutcome(error) {
|
|
10503
|
+
return {
|
|
10504
|
+
ok: false,
|
|
10505
|
+
state: "error",
|
|
10506
|
+
error,
|
|
10507
|
+
next: {
|
|
10508
|
+
kind: "recover",
|
|
10509
|
+
why: error.nextAction
|
|
10510
|
+
},
|
|
10511
|
+
text: formatOpErrorText(error)
|
|
10512
|
+
};
|
|
10513
|
+
}
|
|
10514
|
+
/** Map a shared-client failure to a failure outcome. */
|
|
10515
|
+
function failureFromClientResult(result) {
|
|
10516
|
+
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
10517
|
+
}
|
|
7748
10518
|
//#endregion
|
|
7749
|
-
//#region ../shared/src/
|
|
7750
|
-
|
|
7751
|
-
|
|
10519
|
+
//#region ../shared/src/agentInbox.ts
|
|
10520
|
+
const NON_MEMBER_MENTION_REPLY_GUIDANCE = "[Raft notice: You were notified as a non-member, so you cannot reply in that channel. If no reply is needed, no action is required. Otherwise, DM the person who mentioned you or join the channel to participate.]";
|
|
10521
|
+
function projectAgentReplyAffordance(facts) {
|
|
10522
|
+
if (facts.non_member_mention !== true) return null;
|
|
10523
|
+
return {
|
|
10524
|
+
kind: "non_member_mention",
|
|
10525
|
+
guidance: NON_MEMBER_MENTION_REPLY_GUIDANCE
|
|
10526
|
+
};
|
|
7752
10527
|
}
|
|
7753
|
-
function
|
|
7754
|
-
return
|
|
10528
|
+
function formatAgentReplyAffordance(facts) {
|
|
10529
|
+
return projectAgentReplyAffordance(facts)?.guidance ?? "";
|
|
10530
|
+
}
|
|
10531
|
+
function formatAgentReplyAffordanceSuffix(facts) {
|
|
10532
|
+
const guidance = formatAgentReplyAffordance(facts);
|
|
10533
|
+
return guidance ? `\n${guidance}` : "";
|
|
10534
|
+
}
|
|
10535
|
+
//#endregion
|
|
10536
|
+
//#region ../shared/src/raftRefs.ts
|
|
10537
|
+
const RAFT_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
|
|
10538
|
+
const RAFT_REF_USER_NAME_PATTERN = RAFT_REF_CHANNEL_NAME_PATTERN;
|
|
10539
|
+
const RAFT_REF_DM_PEER_PATTERN = String.raw`[\w.-]+(?:~(?:agent|human))?(?![\w.~-])`;
|
|
10540
|
+
const RAFT_REF_THREAD_SHORT_ID_PATTERN = String.raw`[\da-f]{6,8}`;
|
|
10541
|
+
const RAFT_REF_MESSAGE_ID_PATTERN = String.raw`[A-Za-z0-9][A-Za-z0-9-]{1,63}`;
|
|
10542
|
+
const RAFT_REF_TASK_NUMBER_PATTERN = String.raw`[1-9]\d*`;
|
|
10543
|
+
const RAFT_REF_COMPUTER_ID_PATTERN = String.raw`[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}`;
|
|
10544
|
+
const RAFT_REF_APP_ID_PATTERN = String.raw`[a-z][a-z0-9]*(?:\.[a-z][a-z0-9]*)+`;
|
|
10545
|
+
function createRaftUserRefRegex() {
|
|
10546
|
+
return new RegExp(`(^|[^\\p{L}\\p{N}_-])@(${RAFT_REF_USER_NAME_PATTERN})`, "gu");
|
|
10547
|
+
}
|
|
10548
|
+
function createRaftChannelThreadRefRegex() {
|
|
10549
|
+
return new RegExp(`#(${RAFT_REF_CHANNEL_NAME_PATTERN}):(${RAFT_REF_THREAD_SHORT_ID_PATTERN})`, "giu");
|
|
10550
|
+
}
|
|
10551
|
+
function createRaftChannelRefRegex() {
|
|
10552
|
+
return new RegExp(`#(${RAFT_REF_CHANNEL_NAME_PATTERN})`, "gu");
|
|
10553
|
+
}
|
|
10554
|
+
function createRaftDmThreadRefRegex() {
|
|
10555
|
+
return new RegExp(`dm:@(${RAFT_REF_DM_PEER_PATTERN}):(${RAFT_REF_THREAD_SHORT_ID_PATTERN})`, "giu");
|
|
10556
|
+
}
|
|
10557
|
+
function createRaftDmRefRegex() {
|
|
10558
|
+
return new RegExp(`dm:@(${RAFT_REF_DM_PEER_PATTERN})`, "giu");
|
|
10559
|
+
}
|
|
10560
|
+
/**
|
|
10561
|
+
* A `dm:@name~<suffix>` token whose suffix is not a known peer kind
|
|
10562
|
+
* (`~bot`, `~agentXYZ`, a bare `~`). Renderers claim it as plain text so it
|
|
10563
|
+
* never degrades into a link to a different target (`dm:@name` or `@name`).
|
|
10564
|
+
* Run after the valid DM passes; it also matches valid refs.
|
|
10565
|
+
*/
|
|
10566
|
+
function createRaftMalformedDmKindRefRegex() {
|
|
10567
|
+
return new RegExp(String.raw`dm:@[\w.-]+~[\w.~-]*(?::${RAFT_REF_THREAD_SHORT_ID_PATTERN})?`, "giu");
|
|
10568
|
+
}
|
|
10569
|
+
function createRaftMessageRefRegex() {
|
|
10570
|
+
return new RegExp(`#(${RAFT_REF_CHANNEL_NAME_PATTERN})(?::(${RAFT_REF_THREAD_SHORT_ID_PATTERN}))?\\s+msg=(${RAFT_REF_MESSAGE_ID_PATTERN})`, "giu");
|
|
10571
|
+
}
|
|
10572
|
+
function createRaftBareTaskRefRegex() {
|
|
10573
|
+
return new RegExp(`(^|[^\\w/])(?:(task\\s+))?#(${RAFT_REF_TASK_NUMBER_PATTERN})\\b`, "giu");
|
|
10574
|
+
}
|
|
10575
|
+
const USER_RE = new RegExp(String.raw`^@(${RAFT_REF_USER_NAME_PATTERN})$`, "u");
|
|
10576
|
+
const COMPUTER_RE = new RegExp(String.raw`^computer:(${RAFT_REF_COMPUTER_ID_PATTERN})$`, "iu");
|
|
10577
|
+
const APP_RE = new RegExp(String.raw`^app:(${RAFT_REF_APP_ID_PATTERN})$`, "u");
|
|
10578
|
+
const CHANNEL_RE = new RegExp(String.raw`^#(${RAFT_REF_CHANNEL_NAME_PATTERN})$`, "u");
|
|
10579
|
+
const CHANNEL_THREAD_RE = new RegExp(String.raw`^#(${RAFT_REF_CHANNEL_NAME_PATTERN}):(${RAFT_REF_THREAD_SHORT_ID_PATTERN})$`, "iu");
|
|
10580
|
+
const DM_RE = new RegExp(String.raw`^dm:@(${RAFT_REF_DM_PEER_PATTERN})$`, "iu");
|
|
10581
|
+
const DM_THREAD_RE = new RegExp(String.raw`^dm:@(${RAFT_REF_DM_PEER_PATTERN}):(${RAFT_REF_THREAD_SHORT_ID_PATTERN})$`, "iu");
|
|
10582
|
+
const TASK_RE = new RegExp(String.raw`^task\s+#(${RAFT_REF_TASK_NUMBER_PATTERN})$`, "iu");
|
|
10583
|
+
const CHANNEL_MESSAGE_RE = new RegExp(String.raw`^#(${RAFT_REF_CHANNEL_NAME_PATTERN})(?::(${RAFT_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${RAFT_REF_MESSAGE_ID_PATTERN})$`, "iu");
|
|
10584
|
+
const DM_MESSAGE_RE = new RegExp(String.raw`^dm:@(${RAFT_REF_DM_PEER_PATTERN})(?::(${RAFT_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${RAFT_REF_MESSAGE_ID_PATTERN})$`, "iu");
|
|
10585
|
+
function parseRaftRefTarget(rawTarget) {
|
|
10586
|
+
const target = rawTarget.trim();
|
|
10587
|
+
if (!target) return null;
|
|
10588
|
+
const channelMessage = CHANNEL_MESSAGE_RE.exec(target);
|
|
10589
|
+
if (channelMessage) return {
|
|
10590
|
+
kind: "message",
|
|
10591
|
+
channelName: channelMessage[1],
|
|
10592
|
+
threadParentShortId: channelMessage[2] ?? null,
|
|
10593
|
+
messageId: channelMessage[3]
|
|
10594
|
+
};
|
|
10595
|
+
const dmMessage = DM_MESSAGE_RE.exec(target);
|
|
10596
|
+
if (dmMessage) return {
|
|
10597
|
+
kind: "dm-message",
|
|
10598
|
+
peerName: dmMessage[1],
|
|
10599
|
+
threadParentShortId: dmMessage[2] ?? null,
|
|
10600
|
+
messageId: dmMessage[3]
|
|
10601
|
+
};
|
|
10602
|
+
const channelThread = CHANNEL_THREAD_RE.exec(target);
|
|
10603
|
+
if (channelThread) return {
|
|
10604
|
+
kind: "channel-thread",
|
|
10605
|
+
channelName: channelThread[1],
|
|
10606
|
+
threadShortId: channelThread[2]
|
|
10607
|
+
};
|
|
10608
|
+
const dmThread = DM_THREAD_RE.exec(target);
|
|
10609
|
+
if (dmThread) return {
|
|
10610
|
+
kind: "dm-thread",
|
|
10611
|
+
peerName: dmThread[1],
|
|
10612
|
+
threadShortId: dmThread[2]
|
|
10613
|
+
};
|
|
10614
|
+
const task = TASK_RE.exec(target);
|
|
10615
|
+
if (task) return {
|
|
10616
|
+
kind: "task",
|
|
10617
|
+
taskNumber: Number(task[1])
|
|
10618
|
+
};
|
|
10619
|
+
const computer = COMPUTER_RE.exec(target);
|
|
10620
|
+
if (computer) return {
|
|
10621
|
+
kind: "computer",
|
|
10622
|
+
machineId: computer[1].toLowerCase()
|
|
10623
|
+
};
|
|
10624
|
+
const app = APP_RE.exec(target);
|
|
10625
|
+
if (app) return {
|
|
10626
|
+
kind: "app",
|
|
10627
|
+
appId: app[1]
|
|
10628
|
+
};
|
|
10629
|
+
const user = USER_RE.exec(target);
|
|
10630
|
+
if (user) return {
|
|
10631
|
+
kind: "user",
|
|
10632
|
+
name: user[1]
|
|
10633
|
+
};
|
|
10634
|
+
const channel = CHANNEL_RE.exec(target);
|
|
10635
|
+
if (channel) return {
|
|
10636
|
+
kind: "channel",
|
|
10637
|
+
channelName: channel[1]
|
|
10638
|
+
};
|
|
10639
|
+
const dm = DM_RE.exec(target);
|
|
10640
|
+
if (dm) return {
|
|
10641
|
+
kind: "dm",
|
|
10642
|
+
peerName: dm[1]
|
|
10643
|
+
};
|
|
10644
|
+
return null;
|
|
10645
|
+
}
|
|
10646
|
+
function formatRaftRefTarget(target) {
|
|
10647
|
+
switch (target.kind) {
|
|
10648
|
+
case "user": return `@${target.name}`;
|
|
10649
|
+
case "computer": return `computer:${target.machineId}`;
|
|
10650
|
+
case "app": return `app:${target.appId}`;
|
|
10651
|
+
case "channel": return `#${target.channelName}`;
|
|
10652
|
+
case "channel-thread": return `#${target.channelName}:${target.threadShortId}`;
|
|
10653
|
+
case "dm": return `dm:@${target.peerName}`;
|
|
10654
|
+
case "dm-thread": return `dm:@${target.peerName}:${target.threadShortId}`;
|
|
10655
|
+
case "task": return `task #${target.taskNumber}`;
|
|
10656
|
+
case "message": return target.threadParentShortId ? `#${target.channelName}:${target.threadParentShortId} msg=${target.messageId}` : `#${target.channelName} msg=${target.messageId}`;
|
|
10657
|
+
case "dm-message": return target.threadParentShortId ? `dm:@${target.peerName}:${target.threadParentShortId} msg=${target.messageId}` : `dm:@${target.peerName} msg=${target.messageId}`;
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
function markdownCodeSpans(source) {
|
|
10661
|
+
const spans = [];
|
|
10662
|
+
for (const match of source.matchAll(/```[\s\S]*?```/g)) spans.push({
|
|
10663
|
+
start: match.index ?? 0,
|
|
10664
|
+
end: (match.index ?? 0) + match[0].length
|
|
10665
|
+
});
|
|
10666
|
+
const fences = [...spans];
|
|
10667
|
+
const scanInline = (from, to) => {
|
|
10668
|
+
if (from >= to) return;
|
|
10669
|
+
const segment = source.slice(from, to);
|
|
10670
|
+
for (const match of segment.matchAll(/``[^`]+``|`[^`]+`/g)) {
|
|
10671
|
+
const start = from + (match.index ?? 0);
|
|
10672
|
+
spans.push({
|
|
10673
|
+
start,
|
|
10674
|
+
end: start + match[0].length
|
|
10675
|
+
});
|
|
10676
|
+
}
|
|
10677
|
+
};
|
|
10678
|
+
let cursor = 0;
|
|
10679
|
+
for (const fence of fences) {
|
|
10680
|
+
scanInline(cursor, fence.start);
|
|
10681
|
+
cursor = fence.end;
|
|
10682
|
+
}
|
|
10683
|
+
scanInline(cursor, source.length);
|
|
10684
|
+
return spans.sort((a, b) => a.start - b.start);
|
|
10685
|
+
}
|
|
10686
|
+
function visitMarkdownProseChunks(source, visitor) {
|
|
10687
|
+
const spans = markdownCodeSpans(source);
|
|
10688
|
+
if (spans.length === 0) {
|
|
10689
|
+
visitor(source, 0);
|
|
10690
|
+
return;
|
|
10691
|
+
}
|
|
10692
|
+
let cursor = 0;
|
|
10693
|
+
for (const span of spans) {
|
|
10694
|
+
if (span.end <= cursor) continue;
|
|
10695
|
+
const start = span.start > cursor ? span.start : cursor;
|
|
10696
|
+
if (start > cursor) visitor(source.slice(cursor, start), cursor);
|
|
10697
|
+
cursor = span.end;
|
|
10698
|
+
}
|
|
10699
|
+
if (cursor < source.length) visitor(source.slice(cursor), cursor);
|
|
10700
|
+
}
|
|
10701
|
+
function collectParsedRef(refs, raw, start, end, dedupe) {
|
|
10702
|
+
const target = parseRaftRefTarget(raw);
|
|
10703
|
+
if (!target) return;
|
|
10704
|
+
const canonical = formatRaftRefTarget(target);
|
|
10705
|
+
refs.set(dedupe ? canonical : `${start}:${end}:${canonical}`, {
|
|
10706
|
+
raw,
|
|
10707
|
+
target,
|
|
10708
|
+
start,
|
|
10709
|
+
end
|
|
10710
|
+
});
|
|
10711
|
+
}
|
|
10712
|
+
function extractRaftRefTargetsFromChunk(chunk, chunkOffset, dedupe) {
|
|
10713
|
+
const refs = /* @__PURE__ */ new Map();
|
|
10714
|
+
const occupiedRanges = [];
|
|
10715
|
+
function occupy(match) {
|
|
10716
|
+
if (match.index === void 0) return;
|
|
10717
|
+
occupiedRanges.push({
|
|
10718
|
+
start: match.index,
|
|
10719
|
+
end: match.index + match[0].length
|
|
10720
|
+
});
|
|
10721
|
+
}
|
|
10722
|
+
function isInsideOccupied(match, offset = 0) {
|
|
10723
|
+
if (match.index === void 0) return false;
|
|
10724
|
+
const start = match.index + offset;
|
|
10725
|
+
const end = start + match[0].length - offset;
|
|
10726
|
+
return occupiedRanges.some((range) => start >= range.start && end <= range.end);
|
|
10727
|
+
}
|
|
10728
|
+
for (const match of chunk.matchAll(/\[((?:\\.|[^\]\\])*)\]\(<([^<>\n]+)>\)/g)) {
|
|
10729
|
+
const target = parseRaftRefTarget(match[2] ?? "");
|
|
10730
|
+
if (target?.kind === "computer" || target?.kind === "app") occupy(match);
|
|
10731
|
+
}
|
|
10732
|
+
for (const match of chunk.matchAll(new RegExp(String.raw`<([^<>\n]+)>`, "gu"))) {
|
|
10733
|
+
const raw = match[1] ?? "";
|
|
10734
|
+
const rawStart = (match.index ?? 0) + 1;
|
|
10735
|
+
collectParsedRef(refs, raw, chunkOffset + rawStart, chunkOffset + rawStart + raw.length, dedupe);
|
|
10736
|
+
occupy(match);
|
|
10737
|
+
}
|
|
10738
|
+
for (const match of chunk.matchAll(createRaftMessageRefRegex())) {
|
|
10739
|
+
collectParsedRef(refs, match[2] ? `#${match[1]}:${match[2]} msg=${match[3]}` : `#${match[1]} msg=${match[3]}`, chunkOffset + (match.index ?? 0), chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10740
|
+
occupy(match);
|
|
10741
|
+
}
|
|
10742
|
+
for (const match of chunk.matchAll(createRaftDmThreadRefRegex())) {
|
|
10743
|
+
collectParsedRef(refs, `dm:@${match[1]}:${match[2]}`, chunkOffset + (match.index ?? 0), chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10744
|
+
occupy(match);
|
|
10745
|
+
}
|
|
10746
|
+
for (const match of chunk.matchAll(createRaftDmRefRegex())) {
|
|
10747
|
+
if (isInsideOccupied(match)) continue;
|
|
10748
|
+
collectParsedRef(refs, `dm:@${match[1]}`, chunkOffset + (match.index ?? 0), chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10749
|
+
occupy(match);
|
|
10750
|
+
}
|
|
10751
|
+
for (const match of chunk.matchAll(createRaftMalformedDmKindRefRegex())) if (!isInsideOccupied(match)) occupy(match);
|
|
10752
|
+
for (const match of chunk.matchAll(createRaftChannelThreadRefRegex())) {
|
|
10753
|
+
if (isInsideOccupied(match)) continue;
|
|
10754
|
+
collectParsedRef(refs, `#${match[1]}:${match[2]}`, chunkOffset + (match.index ?? 0), chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10755
|
+
occupy(match);
|
|
10756
|
+
}
|
|
10757
|
+
for (const match of chunk.matchAll(createRaftBareTaskRefRegex())) {
|
|
10758
|
+
const raw = `task #${match[3]}`;
|
|
10759
|
+
const rawStart = (match.index ?? 0) + match[1].length;
|
|
10760
|
+
collectParsedRef(refs, raw, chunkOffset + rawStart, chunkOffset + rawStart + match[0].length - match[1].length, dedupe);
|
|
10761
|
+
occupy(match);
|
|
10762
|
+
}
|
|
10763
|
+
for (const match of chunk.matchAll(createRaftUserRefRegex())) {
|
|
10764
|
+
if (isInsideOccupied(match)) continue;
|
|
10765
|
+
const rawStart = (match.index ?? 0) + match[1].length;
|
|
10766
|
+
collectParsedRef(refs, `@${match[2]}`, chunkOffset + rawStart, chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10767
|
+
occupy(match);
|
|
10768
|
+
}
|
|
10769
|
+
for (const match of chunk.matchAll(createRaftChannelRefRegex())) {
|
|
10770
|
+
if (isInsideOccupied(match)) continue;
|
|
10771
|
+
collectParsedRef(refs, `#${match[1]}`, chunkOffset + (match.index ?? 0), chunkOffset + (match.index ?? 0) + match[0].length, dedupe);
|
|
10772
|
+
occupy(match);
|
|
10773
|
+
}
|
|
10774
|
+
return refs;
|
|
10775
|
+
}
|
|
10776
|
+
function extractRaftRefTargets(source, options = {}) {
|
|
10777
|
+
const refs = /* @__PURE__ */ new Map();
|
|
10778
|
+
const dedupe = options.dedupe ?? true;
|
|
10779
|
+
const collectChunk = (chunk, offset) => {
|
|
10780
|
+
const chunkRefs = extractRaftRefTargetsFromChunk(chunk, offset, dedupe);
|
|
10781
|
+
for (const [key, value] of chunkRefs) refs.set(key, value);
|
|
10782
|
+
};
|
|
10783
|
+
if (options.includeMarkdownCode) collectChunk(source, 0);
|
|
10784
|
+
else visitMarkdownProseChunks(source, collectChunk);
|
|
10785
|
+
return [...refs.values()].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
10786
|
+
}
|
|
10787
|
+
//#endregion
|
|
10788
|
+
//#region ../shared/src/thirdPartyInertRenderer.ts
|
|
10789
|
+
/**
|
|
10790
|
+
* Shared inert renderer for app-controlled third-party text.
|
|
10791
|
+
*
|
|
10792
|
+
* Contract:
|
|
10793
|
+
* - Input is app-controlled free text such as app name, developer display name,
|
|
10794
|
+
* description, and declared data-access wording.
|
|
10795
|
+
* - Output is canonical agent-facing text. It is safe to place in marketplace,
|
|
10796
|
+
* install disclosure, app metadata, and tool-result readouts.
|
|
10797
|
+
* - Markdown inline/fenced code is literal data; ref-shaped text inside it is
|
|
10798
|
+
* never parsed or rewritten.
|
|
10799
|
+
* - Structured values such as normalized domains, callback URLs, categories, and
|
|
10800
|
+
* closed scope enums do not belong here; those should be rendered from
|
|
10801
|
+
* server-normalized values at the call site.
|
|
10802
|
+
*/
|
|
10803
|
+
function escapeAgentMarkupLiterals(value) {
|
|
10804
|
+
return value.replace(/<\/?(?:result|preview|match)\b[^>]*>|<omit\s*\/>/gi, (tag) => tag.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"));
|
|
10805
|
+
}
|
|
10806
|
+
function neutralizeRaftRefLiterals(value) {
|
|
10807
|
+
const refs = extractRaftRefTargets(value, {
|
|
10808
|
+
dedupe: false,
|
|
10809
|
+
includeMarkdownCode: false
|
|
10810
|
+
}).filter((ref) => ref.start < ref.end).sort((a, b) => a.start - b.start || b.end - a.end);
|
|
10811
|
+
if (refs.length === 0) return value;
|
|
10812
|
+
let output = "";
|
|
10813
|
+
let cursor = 0;
|
|
10814
|
+
for (let index = 0; index < refs.length;) {
|
|
10815
|
+
const first = refs[index];
|
|
10816
|
+
if (first.start < cursor) {
|
|
10817
|
+
index++;
|
|
10818
|
+
continue;
|
|
10819
|
+
}
|
|
10820
|
+
const cluster = [first];
|
|
10821
|
+
let clusterEnd = first.end;
|
|
10822
|
+
index++;
|
|
10823
|
+
while (index < refs.length && refs[index].start < clusterEnd) {
|
|
10824
|
+
const next = refs[index];
|
|
10825
|
+
cluster.push(next);
|
|
10826
|
+
clusterEnd = Math.max(clusterEnd, next.end);
|
|
10827
|
+
index++;
|
|
10828
|
+
}
|
|
10829
|
+
output += value.slice(cursor, first.start);
|
|
10830
|
+
output += cluster.map((ref) => neutralRaftRefLabel(ref.target)).join(" ");
|
|
10831
|
+
cursor = clusterEnd;
|
|
10832
|
+
}
|
|
10833
|
+
output += value.slice(cursor);
|
|
10834
|
+
return output;
|
|
10835
|
+
}
|
|
10836
|
+
function neutralRaftRefLabel(target) {
|
|
10837
|
+
switch (target.kind) {
|
|
10838
|
+
case "user": return `user:${target.name}`;
|
|
10839
|
+
case "computer": return `computer:${target.machineId}`;
|
|
10840
|
+
case "app": return `app:${target.appId}`;
|
|
10841
|
+
case "channel": return `channel:${target.channelName}`;
|
|
10842
|
+
case "channel-thread": return `channel:${target.channelName}:${target.threadShortId}`;
|
|
10843
|
+
case "dm": return `dm:user:${target.peerName}`;
|
|
10844
|
+
case "dm-thread": return `dm:user:${target.peerName}:${target.threadShortId}`;
|
|
10845
|
+
case "task": return `task:${target.taskNumber}`;
|
|
10846
|
+
case "message": return target.threadParentShortId ? `channel:${target.channelName}:${target.threadParentShortId} msg=${target.messageId}` : `channel:${target.channelName} msg=${target.messageId}`;
|
|
10847
|
+
case "dm-message": return target.threadParentShortId ? `dm:user:${target.peerName}:${target.threadParentShortId} msg=${target.messageId}` : `dm:user:${target.peerName} msg=${target.messageId}`;
|
|
10848
|
+
}
|
|
10849
|
+
}
|
|
10850
|
+
/** Render an ingress-bounded JSON payload without hiding fields from the agent. */
|
|
10851
|
+
function renderThirdPartyInertJson(value) {
|
|
10852
|
+
return neutralizeRaftRefLiterals(escapeAgentMarkupLiterals(JSON.stringify(value, null, 2)));
|
|
10853
|
+
}
|
|
10854
|
+
//#endregion
|
|
10855
|
+
//#region ../shared/src/utcTimestamp.ts
|
|
10856
|
+
/**
|
|
10857
|
+
* Format an agent-facing timestamp as an explicitly-labelled UTC value.
|
|
10858
|
+
*
|
|
10859
|
+
* Keep the space separator used by existing Raft message/log output while
|
|
10860
|
+
* adding the `Z` suffix that makes the timezone unambiguous.
|
|
10861
|
+
*/
|
|
10862
|
+
function formatUtcTimestamp(value) {
|
|
10863
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
10864
|
+
if (Number.isNaN(date.getTime())) return typeof value === "string" ? value : String(value);
|
|
10865
|
+
return `${date.toISOString().slice(0, 19).replace("T", " ")}Z`;
|
|
10866
|
+
}
|
|
10867
|
+
//#endregion
|
|
10868
|
+
//#region ../shared/src/agentMessageText.ts
|
|
10869
|
+
/**
|
|
10870
|
+
* The target an agent passes back to reply where a message came from:
|
|
10871
|
+
* `#channel`, `#channel:shortid`, `dm:@peer`, `dm:@peer:shortid`, or
|
|
10872
|
+
* `agent-event:shortid` for third-party app events.
|
|
10873
|
+
*/
|
|
10874
|
+
function formatAgentMessageTarget(m) {
|
|
10875
|
+
if (m.third_party_event) return `agent-event:${m.third_party_event.id.slice(0, 8)}`;
|
|
10876
|
+
if (m.channel_type === "thread" && m.parent_channel_name) {
|
|
10877
|
+
const shortId = m.channel_name?.startsWith("thread-") ? m.channel_name.slice(7) : m.channel_name;
|
|
10878
|
+
if (m.parent_channel_type === "dm") return `dm:@${m.parent_channel_name}:${shortId}`;
|
|
10879
|
+
return `#${m.parent_channel_name}:${shortId}`;
|
|
10880
|
+
}
|
|
10881
|
+
if (m.channel_type === "dm") return `dm:@${m.channel_name}`;
|
|
10882
|
+
return `#${m.channel_name}`;
|
|
10883
|
+
}
|
|
10884
|
+
function formatAgentSenderHandle(m) {
|
|
10885
|
+
const name = m.sender_name ?? "unknown";
|
|
10886
|
+
const desc = m.sender_description ?? null;
|
|
10887
|
+
return desc ? `@${name} — ${desc}` : `@${name}`;
|
|
10888
|
+
}
|
|
10889
|
+
function formatAgentAttachmentSuffix(attachments) {
|
|
10890
|
+
if (!attachments?.length) return "";
|
|
10891
|
+
return ` [${attachments.length} attachment${attachments.length > 1 ? "s" : ""}: ${attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} — use raft attachment view to download]`;
|
|
10892
|
+
}
|
|
10893
|
+
function formatAgentTaskAssigneeSuffix(assigneeId, assigneeName) {
|
|
10894
|
+
if (!assigneeId) return "";
|
|
10895
|
+
return assigneeName ? ` assignee=@${assigneeName}` : " assignee=<unresolved>";
|
|
10896
|
+
}
|
|
10897
|
+
/**
|
|
10898
|
+
* Superseded-task projection suffix. `neutralize`, when given, rewrites quoted
|
|
10899
|
+
* title/description/actor text so pasted refs cannot route attention (the CLI
|
|
10900
|
+
* passes its search-preview neutraliser); the actor handle is then `user:<name>`.
|
|
10901
|
+
*/
|
|
10902
|
+
function formatAgentTaskCurrentProjection(projection, taskNumber, neutralize) {
|
|
10903
|
+
const neutralizeRefs = neutralize !== void 0;
|
|
10904
|
+
const renderPreviewText = neutralize ?? ((text) => text);
|
|
10905
|
+
if (!projection?.superseded) return "";
|
|
10906
|
+
const revision = Number.isInteger(projection.revision) ? projection.revision : "?";
|
|
10907
|
+
const source = projection.source ?? "tasks_current_projection";
|
|
10908
|
+
const actorName = projection.amendedByName ?? projection.amended_by_name ?? null;
|
|
10909
|
+
const actorType = projection.amendedByType ?? projection.amended_by_type ?? null;
|
|
10910
|
+
const actor = actorName ? neutralizeRefs ? `user:${actorName}` : `@${actorName}` : actorType === "system" ? "system" : "<unresolved>";
|
|
10911
|
+
const amendedAt = projection.amendedAt ?? projection.amended_at ?? null;
|
|
10912
|
+
const lines = [`[${taskNumber ? `task #${taskNumber} ` : "task "}superseded: current projection rev=${revision} source=${source} actor=${actor} time=${amendedAt ? formatUtcTimestamp(amendedAt) : "-"}]`, `Current title: ${neutralizeRefs ? renderPreviewText(projection.title ?? "") : projection.title ?? ""}`];
|
|
10913
|
+
if (projection.description != null) lines.push(`Current description: ${neutralizeRefs ? renderPreviewText(projection.description) : projection.description}`);
|
|
10914
|
+
return `\n${lines.join("\n")}`;
|
|
10915
|
+
}
|
|
10916
|
+
/** One received-message line: header bracket + sender + content + suffixes. */
|
|
10917
|
+
function formatAgentMessageLine(m) {
|
|
10918
|
+
if (m.third_party_event) {
|
|
10919
|
+
const msgId = m.message_id ? m.message_id.slice(0, 8) : m.third_party_event.id.slice(0, 8);
|
|
10920
|
+
const time = m.timestamp ? formatUtcTimestamp(m.timestamp) : "-";
|
|
10921
|
+
const event = m.third_party_event;
|
|
10922
|
+
const content = m.content ?? "";
|
|
10923
|
+
const source = event.source;
|
|
10924
|
+
const sourceSuffix = source?.resource ? `; resource=${source.resource}${source.access_token_id_hash ? `; access_token_id_hash=${source.access_token_id_hash}` : ""}` : "";
|
|
10925
|
+
const provenance = `kind=${event.kind}; payload_hash=${event.payload_hash}${sourceSuffix}`;
|
|
10926
|
+
return `[target=agent-event:${event.id.slice(0, 8)} msg=${msgId} time=${time} type=third_party_app] @${event.client_id} — ${event.client_name}: ${provenance}\n${content}${event.payload ? `\npayload:\n${renderThirdPartyInertJson(event.payload)}` : ""}`;
|
|
10927
|
+
}
|
|
10928
|
+
const target = formatAgentMessageTarget(m);
|
|
10929
|
+
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
10930
|
+
const time = m.timestamp ? formatUtcTimestamp(m.timestamp) : "-";
|
|
10931
|
+
const senderType = ` type=${m.sender_type}`;
|
|
10932
|
+
const content = m.content ?? "";
|
|
10933
|
+
const attachSuffix = formatAgentAttachmentSuffix(m.attachments);
|
|
10934
|
+
const taskSuffix = m.task_status ? ` [task #${m.task_number} status=${m.task_status}${formatAgentTaskAssigneeSuffix(m.task_assignee_id, m.task_assignee_name)}]` : "";
|
|
10935
|
+
return `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatAgentSenderHandle(m)}: ${content}${attachSuffix}${taskSuffix}${formatAgentReplyAffordanceSuffix(m)}${formatAgentTaskCurrentProjection(m.task_current_projection)}`;
|
|
10936
|
+
}
|
|
10937
|
+
/** Batch of received-message lines (`raft message check` output). */
|
|
10938
|
+
function formatAgentMessages(messages) {
|
|
10939
|
+
if (messages.length === 0) return "No new inbox messages.";
|
|
10940
|
+
return messages.map(formatAgentMessageLine).join("\n");
|
|
10941
|
+
}
|
|
10942
|
+
/**
|
|
10943
|
+
* `message check` hands over a bounded batch, oldest first per conversation;
|
|
10944
|
+
* the server reports how many conversations still have unread after it.
|
|
10945
|
+
*/
|
|
10946
|
+
function formatAgentInboxHint(hint) {
|
|
10947
|
+
const n = hint.unread_conversations;
|
|
10948
|
+
return `Still unread: ${n} ${n === 1 ? "conversation" : "conversations"}. Run \`raft inbox check\` to list them.`;
|
|
10949
|
+
}
|
|
10950
|
+
//#endregion
|
|
10951
|
+
//#region ../shared/src/agentOps/message.ts
|
|
10952
|
+
function senderType(value) {
|
|
10953
|
+
return value === "human" || value === "agent" || value === "system" || value === "third_party_app" ? value : "unknown";
|
|
10954
|
+
}
|
|
10955
|
+
function nullableNumber(value) {
|
|
10956
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
10957
|
+
}
|
|
10958
|
+
function nullableString(value) {
|
|
10959
|
+
return typeof value === "string" ? value : null;
|
|
10960
|
+
}
|
|
10961
|
+
/** Normalise an envelope (snake_case wire keys win, camelCase legacy keys fill in) into the formatter's shape. */
|
|
10962
|
+
function toAgentMessageLike(envelope) {
|
|
10963
|
+
return {
|
|
10964
|
+
...envelope,
|
|
10965
|
+
message_id: envelope.message_id ?? envelope.id,
|
|
10966
|
+
timestamp: envelope.timestamp ?? envelope.createdAt,
|
|
10967
|
+
sender_type: envelope.sender_type ?? envelope.senderType,
|
|
10968
|
+
sender_name: envelope.sender_name ?? envelope.senderName,
|
|
10969
|
+
sender_description: envelope.sender_description ?? envelope.senderDescription ?? null,
|
|
10970
|
+
task_status: envelope.task_status ?? envelope.taskStatus ?? null,
|
|
10971
|
+
task_number: envelope.task_number ?? envelope.taskNumber ?? null,
|
|
10972
|
+
task_assignee_id: envelope.task_assignee_id ?? envelope.taskAssigneeId ?? null,
|
|
10973
|
+
task_assignee_type: envelope.task_assignee_type ?? envelope.taskAssigneeType ?? null,
|
|
10974
|
+
task_assignee_name: envelope.task_assignee_name ?? envelope.taskAssigneeName ?? null,
|
|
10975
|
+
task_current_projection: envelope.task_current_projection ?? envelope.taskCurrentProjection ?? null
|
|
10976
|
+
};
|
|
10977
|
+
}
|
|
10978
|
+
function projectRaftMessage(envelope) {
|
|
10979
|
+
const like = toAgentMessageLike(envelope);
|
|
10980
|
+
const id = nullableString(like.message_id);
|
|
10981
|
+
const taskNumber = nullableNumber(like.task_number);
|
|
10982
|
+
const taskStatus = nullableString(like.task_status);
|
|
10983
|
+
const raw = envelope;
|
|
10984
|
+
return {
|
|
10985
|
+
id,
|
|
10986
|
+
shortId: id ? id.slice(0, 8) : null,
|
|
10987
|
+
seq: nullableNumber(envelope.seq),
|
|
10988
|
+
target: formatAgentMessageTarget(like),
|
|
10989
|
+
timestamp: nullableString(like.timestamp),
|
|
10990
|
+
sender: {
|
|
10991
|
+
type: senderType(like.sender_type),
|
|
10992
|
+
name: nullableString(like.sender_name),
|
|
10993
|
+
description: nullableString(like.sender_description)
|
|
10994
|
+
},
|
|
10995
|
+
content: envelope.content ?? "",
|
|
10996
|
+
attachments: (envelope.attachments ?? []).map((a) => ({
|
|
10997
|
+
id: a.id,
|
|
10998
|
+
filename: a.filename
|
|
10999
|
+
})),
|
|
11000
|
+
mentioned: typeof raw.mentioned === "boolean" ? raw.mentioned : null,
|
|
11001
|
+
task: taskNumber !== null || taskStatus !== null ? {
|
|
11002
|
+
number: taskNumber,
|
|
11003
|
+
status: taskStatus,
|
|
11004
|
+
assigneeName: nullableString(like.task_assignee_name)
|
|
11005
|
+
} : null,
|
|
11006
|
+
thread: {
|
|
11007
|
+
id: nullableString(envelope.threadId),
|
|
11008
|
+
replyCount: nullableNumber(envelope.replyCount)
|
|
11009
|
+
},
|
|
11010
|
+
text: formatAgentMessageLine(like),
|
|
11011
|
+
raw: envelope
|
|
11012
|
+
};
|
|
11013
|
+
}
|
|
11014
|
+
function sortBySeq(messages) {
|
|
11015
|
+
return [...messages].sort((a, b) => (a.seq ?? Number.MAX_SAFE_INTEGER) - (b.seq ?? Number.MAX_SAFE_INTEGER));
|
|
11016
|
+
}
|
|
11017
|
+
//#endregion
|
|
11018
|
+
//#region ../shared/src/agentOps/frontier.ts
|
|
11019
|
+
const MAX_EXACT_SEQS = 2500;
|
|
11020
|
+
function positiveInt(value) {
|
|
11021
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
11022
|
+
}
|
|
11023
|
+
var SeenFrontier = class SeenFrontier {
|
|
11024
|
+
targets = /* @__PURE__ */ new Map();
|
|
11025
|
+
aliases = /* @__PURE__ */ new Map();
|
|
11026
|
+
static fromSnapshot(snapshot) {
|
|
11027
|
+
const frontier = new SeenFrontier();
|
|
11028
|
+
if (!snapshot || snapshot.version !== 1) return frontier;
|
|
11029
|
+
for (const [target, state] of Object.entries(snapshot.targets ?? {})) {
|
|
11030
|
+
if (positiveInt(state.upTo)) frontier.recordUpTo(target, state.upTo);
|
|
11031
|
+
if (Array.isArray(state.exact)) frontier.recordExact(target, state.exact);
|
|
11032
|
+
}
|
|
11033
|
+
for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) frontier.recordAlias(alias, canonical);
|
|
11034
|
+
return frontier;
|
|
11035
|
+
}
|
|
11036
|
+
/** Remember that the Server resolved `requested` to `canonical` (a thread spelling, a `~agent` suffix, …). */
|
|
11037
|
+
recordAlias(requested, canonical) {
|
|
11038
|
+
if (requested && canonical && requested !== canonical) this.aliases.set(requested, canonical);
|
|
11039
|
+
}
|
|
11040
|
+
canonical(target) {
|
|
11041
|
+
return this.aliases.get(target) ?? target;
|
|
11042
|
+
}
|
|
11043
|
+
/** Advance the contiguous boundary for a target; never lowers it. */
|
|
11044
|
+
recordUpTo(target, seq) {
|
|
11045
|
+
if (!positiveInt(seq)) return;
|
|
11046
|
+
const state = this.state(this.canonical(target));
|
|
11047
|
+
if (state.upTo === void 0 || seq > state.upTo) state.upTo = seq;
|
|
11048
|
+
for (const exact of state.exact) if (exact <= state.upTo) state.exact.delete(exact);
|
|
11049
|
+
}
|
|
11050
|
+
/** Record bodies that were rendered without proving contiguity. */
|
|
11051
|
+
recordExact(target, seqs) {
|
|
11052
|
+
const state = this.state(this.canonical(target));
|
|
11053
|
+
for (const seq of seqs) {
|
|
11054
|
+
if (!positiveInt(seq)) continue;
|
|
11055
|
+
if (state.upTo !== void 0 && seq <= state.upTo) continue;
|
|
11056
|
+
state.exact.add(seq);
|
|
11057
|
+
}
|
|
11058
|
+
if (state.exact.size > MAX_EXACT_SEQS) {
|
|
11059
|
+
const keep = [...state.exact].sort((a, b) => b - a).slice(0, MAX_EXACT_SEQS);
|
|
11060
|
+
state.exact.clear();
|
|
11061
|
+
for (const seq of keep) state.exact.add(seq);
|
|
11062
|
+
}
|
|
11063
|
+
}
|
|
11064
|
+
/** What to attest on a send or claim to `target`; omits `seenUpToSeq` when nothing contiguous is known. */
|
|
11065
|
+
attestation(target) {
|
|
11066
|
+
const state = this.targets.get(this.canonical(target));
|
|
11067
|
+
if (!state) return { seenExactSeqs: [] };
|
|
11068
|
+
return {
|
|
11069
|
+
...state.upTo === void 0 ? {} : { seenUpToSeq: state.upTo },
|
|
11070
|
+
seenExactSeqs: [...state.exact].sort((a, b) => a - b)
|
|
11071
|
+
};
|
|
11072
|
+
}
|
|
11073
|
+
snapshot() {
|
|
11074
|
+
const targets = {};
|
|
11075
|
+
for (const [target, state] of this.targets) targets[target] = {
|
|
11076
|
+
...state.upTo === void 0 ? {} : { upTo: state.upTo },
|
|
11077
|
+
...state.exact.size === 0 ? {} : { exact: [...state.exact].sort((a, b) => a - b) }
|
|
11078
|
+
};
|
|
11079
|
+
return {
|
|
11080
|
+
version: 1,
|
|
11081
|
+
targets,
|
|
11082
|
+
aliases: Object.fromEntries(this.aliases)
|
|
11083
|
+
};
|
|
11084
|
+
}
|
|
11085
|
+
state(target) {
|
|
11086
|
+
let state = this.targets.get(target);
|
|
11087
|
+
if (!state) {
|
|
11088
|
+
state = { exact: /* @__PURE__ */ new Set() };
|
|
11089
|
+
this.targets.set(target, state);
|
|
11090
|
+
}
|
|
11091
|
+
return state;
|
|
11092
|
+
}
|
|
11093
|
+
};
|
|
11094
|
+
//#endregion
|
|
11095
|
+
//#region ../shared/src/agentOps/inbox.ts
|
|
11096
|
+
const MAX_DRAIN_ROUNDS = 50;
|
|
11097
|
+
function batchNext(batch) {
|
|
11098
|
+
if (batch.hasMore) return {
|
|
11099
|
+
kind: "check_inbox_again",
|
|
11100
|
+
command: "raft message check",
|
|
11101
|
+
...batch.cursor === null ? {} : { args: { since: batch.cursor } },
|
|
11102
|
+
why: "The Server trimmed this batch; more messages are pending."
|
|
11103
|
+
};
|
|
11104
|
+
if ((batch.stillUnreadConversations ?? 0) > 0) return {
|
|
11105
|
+
kind: "list_inbox",
|
|
11106
|
+
command: "raft inbox check",
|
|
11107
|
+
why: "Conversations remain unread beyond this batch."
|
|
11108
|
+
};
|
|
11109
|
+
if (batch.messages.length > 0) return {
|
|
11110
|
+
kind: "reply_or_act",
|
|
11111
|
+
args: { target: batch.messages[0].target },
|
|
11112
|
+
why: "Handle the messages above; reply where each one came from."
|
|
11113
|
+
};
|
|
11114
|
+
return null;
|
|
11115
|
+
}
|
|
11116
|
+
function batchText(batch) {
|
|
11117
|
+
const lines = [formatAgentMessages(batch.messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11118
|
+
if (batch.hasMore) lines.push("More messages are pending. Run `raft message check` again.");
|
|
11119
|
+
else if (batch.messages.length > 0) lines.push("No more new inbox messages.");
|
|
11120
|
+
if ((batch.stillUnreadConversations ?? 0) > 0) lines.push(formatAgentInboxHint({ unread_conversations: batch.stillUnreadConversations }));
|
|
11121
|
+
return lines.join("\n");
|
|
11122
|
+
}
|
|
11123
|
+
function recordExactSeen(frontier, messages) {
|
|
11124
|
+
if (!frontier) return;
|
|
11125
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
11126
|
+
for (const message of messages) {
|
|
11127
|
+
if (message.seq === null) continue;
|
|
11128
|
+
byTarget.set(message.target, [...byTarget.get(message.target) ?? [], message.seq]);
|
|
11129
|
+
}
|
|
11130
|
+
for (const [target, seqs] of byTarget) frontier.recordExact(target, seqs);
|
|
11131
|
+
}
|
|
11132
|
+
/** One bounded pull. Records exact seen seqs on `frontier` (sparse drains never advance the high-water mark). */
|
|
11133
|
+
async function checkInbox(client, request = {}, frontier) {
|
|
11134
|
+
const ack = request.ack ?? "cursor";
|
|
11135
|
+
const result = await client.events.get({
|
|
11136
|
+
since: request.since === void 0 ? "latest" : String(request.since),
|
|
11137
|
+
...request.limit === void 0 ? {} : { limit: String(request.limit) },
|
|
11138
|
+
ack
|
|
11139
|
+
});
|
|
11140
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11141
|
+
const data = result.data;
|
|
11142
|
+
const messages = sortBySeq(data.events.map(projectRaftMessage));
|
|
11143
|
+
recordExactSeen(frontier, messages);
|
|
11144
|
+
const batch = {
|
|
11145
|
+
messages,
|
|
11146
|
+
cursor: typeof data.last_seen_seq === "number" ? data.last_seen_seq : null,
|
|
11147
|
+
ackMode: data.ack_mode ?? "immediate",
|
|
11148
|
+
hasMore: data.has_more === true,
|
|
11149
|
+
stillUnreadConversations: data.inbox_hint?.unread_conversations ?? null,
|
|
11150
|
+
replyTarget: data.reply_target ?? null
|
|
11151
|
+
};
|
|
11152
|
+
return {
|
|
11153
|
+
ok: true,
|
|
11154
|
+
state: messages.length > 0 ? "batch" : "empty",
|
|
11155
|
+
data: batch,
|
|
11156
|
+
next: batchNext(batch),
|
|
11157
|
+
text: batchText(batch)
|
|
11158
|
+
};
|
|
11159
|
+
}
|
|
11160
|
+
/**
|
|
11161
|
+
* Pull until the Server reports nothing more, like `raft message check`, but
|
|
11162
|
+
* as an async iterator so that acknowledgement follows consumption: under
|
|
11163
|
+
* cursor acks, the pull that acknowledges batch N is only sent when the
|
|
11164
|
+
* consumer asks for batch N+1, i.e. after it has processed batch N. A consumer
|
|
11165
|
+
* that stops midway leaves the current batch unacknowledged, and a later pull
|
|
11166
|
+
* without a cursor returns it again. Capped at 50 rounds. The generator's
|
|
11167
|
+
* return value summarises the drain; the batch that ends the iteration is
|
|
11168
|
+
* acknowledged only by the caller's next pull (`summary.cursor`).
|
|
11169
|
+
*/
|
|
11170
|
+
async function* drainInbox(client, request = {}, frontier) {
|
|
11171
|
+
const ack = request.ack ?? "cursor";
|
|
11172
|
+
let cursor = request.since ?? null;
|
|
11173
|
+
let ackMode = ack;
|
|
11174
|
+
let stillUnread = null;
|
|
11175
|
+
let rounds = 0;
|
|
11176
|
+
while (rounds < MAX_DRAIN_ROUNDS) {
|
|
11177
|
+
const sent = cursor;
|
|
11178
|
+
rounds += 1;
|
|
11179
|
+
const round = await checkInbox(client, {
|
|
11180
|
+
...sent === null ? {} : { since: sent },
|
|
11181
|
+
limit: request.limit,
|
|
11182
|
+
ack
|
|
11183
|
+
}, frontier);
|
|
11184
|
+
if (!round.ok) return {
|
|
11185
|
+
cursor,
|
|
11186
|
+
ackMode,
|
|
11187
|
+
rounds,
|
|
11188
|
+
hasMore: true,
|
|
11189
|
+
stillUnreadConversations: stillUnread,
|
|
11190
|
+
error: round
|
|
11191
|
+
};
|
|
11192
|
+
const batch = round.data;
|
|
11193
|
+
ackMode = batch.ackMode;
|
|
11194
|
+
stillUnread = batch.stillUnreadConversations;
|
|
11195
|
+
if (batch.messages.length > 0) {
|
|
11196
|
+
cursor = batch.cursor ?? sent;
|
|
11197
|
+
yield batch;
|
|
11198
|
+
if (ack === "cursor" && batch.ackMode === "cursor") continue;
|
|
11199
|
+
if (batch.hasMore) continue;
|
|
11200
|
+
return {
|
|
11201
|
+
cursor,
|
|
11202
|
+
ackMode,
|
|
11203
|
+
rounds,
|
|
11204
|
+
hasMore: false,
|
|
11205
|
+
stillUnreadConversations: stillUnread,
|
|
11206
|
+
error: null
|
|
11207
|
+
};
|
|
11208
|
+
}
|
|
11209
|
+
if (ack === "cursor" && batch.ackMode === "cursor" && batch.cursor !== null && batch.cursor !== sent) {
|
|
11210
|
+
cursor = batch.cursor;
|
|
11211
|
+
continue;
|
|
11212
|
+
}
|
|
11213
|
+
return {
|
|
11214
|
+
cursor,
|
|
11215
|
+
ackMode,
|
|
11216
|
+
rounds,
|
|
11217
|
+
hasMore: batch.hasMore,
|
|
11218
|
+
stillUnreadConversations: stillUnread,
|
|
11219
|
+
error: null
|
|
11220
|
+
};
|
|
11221
|
+
}
|
|
11222
|
+
return {
|
|
11223
|
+
cursor,
|
|
11224
|
+
ackMode,
|
|
11225
|
+
rounds,
|
|
11226
|
+
hasMore: true,
|
|
11227
|
+
stillUnreadConversations: stillUnread,
|
|
11228
|
+
error: null
|
|
11229
|
+
};
|
|
11230
|
+
}
|
|
11231
|
+
function openCommandFor(item) {
|
|
11232
|
+
return `raft message read --target "${item.target}" --after ${item.lastReadSeq}`;
|
|
11233
|
+
}
|
|
11234
|
+
function listingNext(listing) {
|
|
11235
|
+
const first = listing.conversations[0];
|
|
11236
|
+
if (first) return {
|
|
11237
|
+
kind: "read_target",
|
|
11238
|
+
command: first.openCommand,
|
|
11239
|
+
args: {
|
|
11240
|
+
target: first.target,
|
|
11241
|
+
after: first.lastReadSeq
|
|
11242
|
+
},
|
|
11243
|
+
why: "Open the newest unread conversation from your read position."
|
|
11244
|
+
};
|
|
11245
|
+
return null;
|
|
11246
|
+
}
|
|
11247
|
+
function listingText(listing) {
|
|
11248
|
+
const t = listing.totals;
|
|
11249
|
+
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." : ""}`;
|
|
11250
|
+
const rows = listing.conversations.map((c) => {
|
|
11251
|
+
const flags = [c.mentions > 0 ? "mentions you" : null, c.kind === "thread" ? "thread" : null].filter(Boolean).join(" · ");
|
|
11252
|
+
return `${c.target} · ${c.unread} unread${flags ? ` · ${flags}` : ""}${c.latestSenderName ? ` · latest @${c.latestSenderName}` : ""}\n open: ${c.openCommand}`;
|
|
11253
|
+
});
|
|
11254
|
+
const trailer = [];
|
|
11255
|
+
if (listing.hasMore && listing.nextBeforeSeq !== null) trailer.push(`More: raft inbox check${listing.view === "mentions" ? " --view mentions" : ""} --before ${listing.nextBeforeSeq}`);
|
|
11256
|
+
const next = listingNext(listing);
|
|
11257
|
+
trailer.push(next?.command ? `Next: open the first conversation above: ${next.command}` : "Next: nothing to do.");
|
|
11258
|
+
return [
|
|
11259
|
+
head,
|
|
11260
|
+
...rows,
|
|
11261
|
+
...trailer
|
|
11262
|
+
].join("\n");
|
|
11263
|
+
}
|
|
11264
|
+
/** The agent's Activity panel. Lists only; nothing is consumed. */
|
|
11265
|
+
async function listInbox(client, request = {}) {
|
|
11266
|
+
const result = await client.inbox.list({
|
|
11267
|
+
...request.view && request.view !== "unread" ? { view: request.view } : {},
|
|
11268
|
+
...request.before === void 0 ? {} : { before_seq: String(request.before) },
|
|
11269
|
+
...request.limit === void 0 ? {} : { limit: String(request.limit) }
|
|
11270
|
+
});
|
|
11271
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11272
|
+
const data = result.data;
|
|
11273
|
+
const listing = {
|
|
11274
|
+
view: data.view,
|
|
11275
|
+
conversations: data.items.map((item) => ({
|
|
11276
|
+
target: item.target,
|
|
11277
|
+
kind: item.kind,
|
|
11278
|
+
unread: item.unread,
|
|
11279
|
+
mentions: item.mentions,
|
|
11280
|
+
lastReadSeq: item.lastReadSeq,
|
|
11281
|
+
activitySeq: item.activitySeq,
|
|
11282
|
+
latestSenderName: item.latestSenderName,
|
|
11283
|
+
latestAt: item.latestAt,
|
|
11284
|
+
openCommand: openCommandFor(item)
|
|
11285
|
+
})),
|
|
11286
|
+
totals: data.totals,
|
|
11287
|
+
hasMore: data.hasMore,
|
|
11288
|
+
nextBeforeSeq: data.nextBeforeSeq
|
|
11289
|
+
};
|
|
11290
|
+
return {
|
|
11291
|
+
ok: true,
|
|
11292
|
+
state: listing.conversations.length > 0 ? "listed" : "empty",
|
|
11293
|
+
data: listing,
|
|
11294
|
+
next: listingNext(listing),
|
|
11295
|
+
text: listingText(listing)
|
|
11296
|
+
};
|
|
11297
|
+
}
|
|
11298
|
+
//#endregion
|
|
11299
|
+
//#region ../shared/src/agentOps/messages.ts
|
|
11300
|
+
function historyNext(page) {
|
|
11301
|
+
const seqs = page.messages.map((m) => m.seq).filter((s) => s !== null);
|
|
11302
|
+
if (page.hasNewer && seqs.length > 0) {
|
|
11303
|
+
const max = Math.max(...seqs);
|
|
11304
|
+
return {
|
|
11305
|
+
kind: "read_newer",
|
|
11306
|
+
command: `raft message read --target "${page.target}" --after ${max}`,
|
|
11307
|
+
args: {
|
|
11308
|
+
target: page.target,
|
|
11309
|
+
after: max
|
|
11310
|
+
},
|
|
11311
|
+
why: "Newer messages exist beyond this window."
|
|
11312
|
+
};
|
|
11313
|
+
}
|
|
11314
|
+
if (page.hasOlder && seqs.length > 0) {
|
|
11315
|
+
const min = Math.min(...seqs);
|
|
11316
|
+
return {
|
|
11317
|
+
kind: "read_older",
|
|
11318
|
+
command: `raft message read --target "${page.target}" --before ${min}`,
|
|
11319
|
+
args: {
|
|
11320
|
+
target: page.target,
|
|
11321
|
+
before: min
|
|
11322
|
+
},
|
|
11323
|
+
why: "Older messages exist before this window; read them only if the task needs them."
|
|
11324
|
+
};
|
|
11325
|
+
}
|
|
11326
|
+
return null;
|
|
11327
|
+
}
|
|
11328
|
+
function historyText(page) {
|
|
11329
|
+
if (page.messages.length === 0) return `No messages in ${page.target}.`;
|
|
11330
|
+
const lines = [formatAgentMessages(page.messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11331
|
+
const next = historyNext(page);
|
|
11332
|
+
if (next?.command) lines.push(`${next.kind === "read_newer" ? "Newer" : "Older"} exist: ${next.command}`);
|
|
11333
|
+
return lines.join("\n");
|
|
11334
|
+
}
|
|
11335
|
+
/**
|
|
11336
|
+
* Frontier rules (FH-EXT-001, mirrored from the CLI): a contiguous window may
|
|
11337
|
+
* advance the high-water mark to the Server's `model_seen_up_to_seq`; an
|
|
11338
|
+
* `around` lookup and a legacy Server without that field record exact seqs.
|
|
11339
|
+
*/
|
|
11340
|
+
function recordHistorySeen(frontier, requested, page, around) {
|
|
11341
|
+
if (!frontier) return;
|
|
11342
|
+
frontier.recordAlias(requested, page.target);
|
|
11343
|
+
const seqs = page.messages.map((m) => m.seq).filter((s) => s !== null);
|
|
11344
|
+
if (seqs.length === 0) return;
|
|
11345
|
+
if (!around && page.modelSeenUpToSeq !== null && page.modelSeenUpToSeq > 0) {
|
|
11346
|
+
frontier.recordUpTo(page.target, page.modelSeenUpToSeq);
|
|
11347
|
+
return;
|
|
11348
|
+
}
|
|
11349
|
+
frontier.recordExact(page.target, seqs);
|
|
11350
|
+
}
|
|
11351
|
+
async function readHistory(client, request, frontier) {
|
|
11352
|
+
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to read history." }));
|
|
11353
|
+
const result = await client.history.read({
|
|
11354
|
+
channel: request.target,
|
|
11355
|
+
...request.after === void 0 ? {} : { after: String(request.after) },
|
|
11356
|
+
...request.before === void 0 ? {} : { before: String(request.before) },
|
|
11357
|
+
...request.around === void 0 ? {} : { around: String(request.around) },
|
|
11358
|
+
...request.limit === void 0 ? {} : { limit: String(request.limit) }
|
|
11359
|
+
});
|
|
11360
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11361
|
+
const data = result.data;
|
|
11362
|
+
const page = {
|
|
11363
|
+
target: typeof data.target === "string" && data.target ? data.target : request.target,
|
|
11364
|
+
messages: sortBySeq(data.messages.map(projectRaftMessage)),
|
|
11365
|
+
hasOlder: data.has_older === true,
|
|
11366
|
+
hasNewer: data.has_newer === true,
|
|
11367
|
+
lastReadSeq: typeof data.last_read_seq === "number" ? data.last_read_seq : null,
|
|
11368
|
+
modelSeenUpToSeq: typeof data.model_seen_up_to_seq === "number" ? data.model_seen_up_to_seq : null
|
|
11369
|
+
};
|
|
11370
|
+
recordHistorySeen(frontier, request.target, page, request.around !== void 0);
|
|
11371
|
+
return {
|
|
11372
|
+
ok: true,
|
|
11373
|
+
state: page.messages.length > 0 ? "page" : "empty",
|
|
11374
|
+
data: page,
|
|
11375
|
+
next: historyNext(page),
|
|
11376
|
+
text: historyText(page)
|
|
11377
|
+
};
|
|
11378
|
+
}
|
|
11379
|
+
/** Attempts for a keyed send whose request never reached the Server (transport failure only). */
|
|
11380
|
+
const SEND_TRANSPORT_ATTEMPTS = 3;
|
|
11381
|
+
function heldText(held, action) {
|
|
11382
|
+
const noun = held.newMessageCount === 1 ? "message" : "messages";
|
|
11383
|
+
const head = `Held — ${held.newMessageCount} unread ${noun} in ${held.target}. ${action}`;
|
|
11384
|
+
if (held.withheld) return `${head}\nContext withheld (reviewer isolation).`;
|
|
11385
|
+
const lines = [head];
|
|
11386
|
+
if (held.formalMentionCount > 0) lines.push(`Note: ${held.formalMentionCount} of these messages formally @mention you.`);
|
|
11387
|
+
if (held.omittedMessageCount > 0) lines.push(`${held.omittedMessageCount} earlier ${held.omittedMessageCount === 1 ? "message" : "messages"} not shown.`);
|
|
11388
|
+
if (held.heldMessages.length > 0) lines.push(formatAgentMessages(held.heldMessages.map((m) => toAgentMessageLike(m.raw))));
|
|
11389
|
+
lines.push(`Full text: raft message read --target "${held.target}"`);
|
|
11390
|
+
return lines.join("\n");
|
|
11391
|
+
}
|
|
11392
|
+
function heldNext(held) {
|
|
11393
|
+
return {
|
|
11394
|
+
kind: "review_then_resend",
|
|
11395
|
+
command: `raft message read --target "${held.target}"`,
|
|
11396
|
+
args: {
|
|
11397
|
+
target: held.target,
|
|
11398
|
+
newMessageCount: held.newMessageCount
|
|
11399
|
+
},
|
|
11400
|
+
why: "Newer messages arrived in this conversation; read them, then resend if the message still applies."
|
|
11401
|
+
};
|
|
11402
|
+
}
|
|
11403
|
+
function projectHeld(target, data) {
|
|
11404
|
+
const withheld = data.freshnessContextMode === "withheld";
|
|
11405
|
+
const heldMessages = (data.heldMessages ?? []).map(projectRaftMessage);
|
|
11406
|
+
return {
|
|
11407
|
+
target,
|
|
11408
|
+
newMessageCount: withheld ? data.withheldMessageCount ?? 0 : data.newMessageCount ?? heldMessages.length,
|
|
11409
|
+
heldMessages,
|
|
11410
|
+
omittedMessageCount: data.omittedMessageCount ?? 0,
|
|
11411
|
+
formalMentionCount: data.mentionAnnotation?.formalMentionCount ?? 0,
|
|
11412
|
+
seenUpToSeq: typeof data.seenUpToSeq === "number" ? data.seenUpToSeq : null,
|
|
11413
|
+
withheld
|
|
11414
|
+
};
|
|
11415
|
+
}
|
|
11416
|
+
function isHeldResponse(data) {
|
|
11417
|
+
return Boolean(data) && typeof data === "object" && data.state === "held";
|
|
11418
|
+
}
|
|
11419
|
+
function sentOutcome(target, data) {
|
|
11420
|
+
const sent = {
|
|
11421
|
+
messageId: data.messageId,
|
|
11422
|
+
messageSeq: typeof data.messageSeq === "number" ? data.messageSeq : null,
|
|
11423
|
+
unresolvedMentionHandles: data.unresolvedMentionHandles ?? [],
|
|
11424
|
+
recentUnread: (data.recentUnread ?? []).map(projectRaftMessage)
|
|
11425
|
+
};
|
|
11426
|
+
const warn = sent.unresolvedMentionHandles.length > 0 ? ` Unresolved @handles: ${sent.unresolvedMentionHandles.map((h) => `@${h}`).join(", ")}.` : "";
|
|
11427
|
+
return {
|
|
11428
|
+
ok: true,
|
|
11429
|
+
state: "sent",
|
|
11430
|
+
data: sent,
|
|
11431
|
+
next: sent.recentUnread.length > 0 ? {
|
|
11432
|
+
kind: "read_target",
|
|
11433
|
+
command: `raft message read --target "${target}"`,
|
|
11434
|
+
args: { target },
|
|
11435
|
+
why: "Newer messages arrived while you were sending."
|
|
11436
|
+
} : null,
|
|
11437
|
+
text: `Message sent to ${target}. Message ID: ${sent.messageId}${warn}`
|
|
11438
|
+
};
|
|
11439
|
+
}
|
|
11440
|
+
async function sendMessage(client, request, frontier, internal = {}) {
|
|
11441
|
+
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to send a message." }));
|
|
11442
|
+
if (typeof request.content !== "string" || !request.content.trim() && !request.attachmentIds?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "Message content (or an attachment) is required." }));
|
|
11443
|
+
const idempotencyKey = request.idempotencyKey?.trim() || crypto.randomUUID();
|
|
11444
|
+
const attestation = request.seen ? {
|
|
11445
|
+
...request.seen.upToSeq === void 0 ? {} : { seenUpToSeq: request.seen.upToSeq },
|
|
11446
|
+
seenExactSeqs: request.seen.exactSeqs ?? []
|
|
11447
|
+
} : frontier?.attestation(request.target) ?? { seenExactSeqs: [] };
|
|
11448
|
+
const body = {
|
|
11449
|
+
target: request.target,
|
|
11450
|
+
content: request.content,
|
|
11451
|
+
...request.attachmentIds?.length ? { attachmentIds: request.attachmentIds } : {},
|
|
11452
|
+
idempotencyKey,
|
|
11453
|
+
...request.mentions?.length ? { mentions: request.mentions } : {},
|
|
11454
|
+
...attestation.seenUpToSeq === void 0 ? {} : { seenUpToSeq: attestation.seenUpToSeq },
|
|
11455
|
+
...attestation.seenExactSeqs.length > 0 ? { seenExactSeqs: attestation.seenExactSeqs.slice(0, 2500) } : {},
|
|
11456
|
+
...internal.sendDraft ? { sendDraft: true } : {},
|
|
11457
|
+
...internal.continueAnyway ? { continueAnyway: true } : {},
|
|
11458
|
+
...internal.reholdCount ? { draftReholdCount: internal.reholdCount } : {}
|
|
11459
|
+
};
|
|
11460
|
+
let result = await client.messages.sendV2(body);
|
|
11461
|
+
for (let attempt = 1; !result.ok && result.error.kind === "transport" && attempt < SEND_TRANSPORT_ATTEMPTS; attempt += 1) result = await client.messages.sendV2(body);
|
|
11462
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11463
|
+
const data = result.data;
|
|
11464
|
+
if (isHeldResponse(data)) {
|
|
11465
|
+
const reholdCount = (internal.reholdCount ?? 0) + 1;
|
|
11466
|
+
const keyed = {
|
|
11467
|
+
...request,
|
|
11468
|
+
idempotencyKey
|
|
11469
|
+
};
|
|
11470
|
+
const held = {
|
|
11471
|
+
...projectHeld(request.target, data),
|
|
11472
|
+
resend: (options) => sendMessage(client, options.seen === "held" && typeof data.seenUpToSeq === "number" ? {
|
|
11473
|
+
...keyed,
|
|
11474
|
+
seen: {
|
|
11475
|
+
upToSeq: data.seenUpToSeq,
|
|
11476
|
+
exactSeqs: attestation.seenExactSeqs
|
|
11477
|
+
}
|
|
11478
|
+
} : keyed, frontier, {
|
|
11479
|
+
sendDraft: true,
|
|
11480
|
+
reholdCount,
|
|
11481
|
+
...options.seen === "anyway" ? { continueAnyway: true } : {}
|
|
11482
|
+
})
|
|
11483
|
+
};
|
|
11484
|
+
return {
|
|
11485
|
+
ok: true,
|
|
11486
|
+
state: "held",
|
|
11487
|
+
data: held,
|
|
11488
|
+
next: heldNext(held),
|
|
11489
|
+
text: heldText(held, "Your message was not sent.")
|
|
11490
|
+
};
|
|
11491
|
+
}
|
|
11492
|
+
if (data.state === "sent") return sentOutcome(request.target, data);
|
|
11493
|
+
return failureOutcome(opError("INVALID_RESPONSE", { message: `Unexpected send state "${data.state}".` }));
|
|
11494
|
+
}
|
|
11495
|
+
/** Reply where a received message came from. */
|
|
11496
|
+
function replyTo(client, message, request, frontier) {
|
|
11497
|
+
return sendMessage(client, {
|
|
11498
|
+
...request,
|
|
11499
|
+
target: message.target
|
|
11500
|
+
}, frontier);
|
|
7755
11501
|
}
|
|
7756
11502
|
//#endregion
|
|
7757
|
-
//#region src/
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
11503
|
+
//#region ../shared/src/agentOps/tasks.ts
|
|
11504
|
+
const TASK_CLAIM_REASON_ALREADY_CLAIMED_BY_YOU = "already claimed by you";
|
|
11505
|
+
function rowState(result) {
|
|
11506
|
+
if (result.success) return "claimed";
|
|
11507
|
+
if (result.reason === TASK_CLAIM_REASON_ALREADY_CLAIMED_BY_YOU) return "already_yours";
|
|
11508
|
+
if (result.conflict?.kind === "claim_conflict") return "conflict";
|
|
11509
|
+
return "refused";
|
|
11510
|
+
}
|
|
11511
|
+
function claimNext(claim) {
|
|
11512
|
+
const first = claim.rows.find((row) => row.mayWork);
|
|
11513
|
+
if (first) {
|
|
11514
|
+
const suffix = first.messageId ? first.messageId.slice(0, 8) : null;
|
|
11515
|
+
return {
|
|
11516
|
+
kind: "start_work",
|
|
11517
|
+
...suffix ? {
|
|
11518
|
+
command: `raft message send --target "${claim.target}:${suffix}"`,
|
|
11519
|
+
args: { thread: `${claim.target}:${suffix}` }
|
|
11520
|
+
} : {},
|
|
11521
|
+
why: "The claim is yours; post progress in the task's thread."
|
|
11522
|
+
};
|
|
7764
11523
|
}
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
11524
|
+
return {
|
|
11525
|
+
kind: "do_not_start",
|
|
11526
|
+
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."
|
|
11527
|
+
};
|
|
7768
11528
|
}
|
|
7769
|
-
function
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
11529
|
+
function claimText(claim) {
|
|
11530
|
+
const lines = claim.rows.map((row) => {
|
|
11531
|
+
const label = row.taskNumber ? `#${row.taskNumber}` : `msg:${row.ref}`;
|
|
11532
|
+
if (row.state === "claimed") return `${label}: claimed`;
|
|
11533
|
+
if (row.state === "already_yours") return `${label}: already claimed by you`;
|
|
11534
|
+
if (row.state === "conflict") return `${label}: held by ${row.holder?.name ? `@${row.holder.name}` : "another assignee"}`;
|
|
11535
|
+
return `${label}: refused${row.reason ? ` (${row.reason})` : ""}`;
|
|
11536
|
+
});
|
|
11537
|
+
const head = `Claim results (${claim.rows.filter((row) => row.mayWork).length} of ${claim.rows.length} authorise work):`;
|
|
11538
|
+
const next = claimNext(claim);
|
|
11539
|
+
return [
|
|
11540
|
+
head,
|
|
11541
|
+
...lines,
|
|
11542
|
+
next?.command ? `Follow up in the task's thread: ${next.command}` : next?.why ?? ""
|
|
11543
|
+
].filter(Boolean).join("\n");
|
|
11544
|
+
}
|
|
11545
|
+
async function claimTasks(client, request) {
|
|
11546
|
+
const numbers = (request.taskNumbers ?? []).filter((n) => Number.isInteger(n) && n > 0);
|
|
11547
|
+
const ids = (request.messageIds ?? []).map((id) => id.trim()).filter(Boolean);
|
|
11548
|
+
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target is required to claim tasks." }));
|
|
11549
|
+
if (numbers.length === 0 && ids.length === 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass at least one task number or message id to claim." }));
|
|
11550
|
+
const result = await client.tasks.claim({
|
|
11551
|
+
channel: request.target,
|
|
11552
|
+
...numbers.length ? { task_numbers: numbers } : {},
|
|
11553
|
+
...ids.length ? { message_ids: ids } : {}
|
|
11554
|
+
});
|
|
11555
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11556
|
+
const data = result.data;
|
|
11557
|
+
if (isHeldResponse(data)) {
|
|
11558
|
+
const held = {
|
|
11559
|
+
...projectHeld(request.target, data),
|
|
11560
|
+
retry: () => claimTasks(client, request)
|
|
11561
|
+
};
|
|
11562
|
+
return {
|
|
11563
|
+
ok: true,
|
|
11564
|
+
state: "held",
|
|
11565
|
+
data: held,
|
|
11566
|
+
next: {
|
|
11567
|
+
kind: "review_then_retry",
|
|
11568
|
+
command: `raft message read --target "${request.target}"`,
|
|
11569
|
+
args: {
|
|
11570
|
+
target: request.target,
|
|
11571
|
+
newMessageCount: held.newMessageCount
|
|
11572
|
+
},
|
|
11573
|
+
why: "Unread messages in this channel may change the task; read them, then claim again if it is still right."
|
|
11574
|
+
},
|
|
11575
|
+
text: `Held — ${held.newMessageCount} unread ${held.newMessageCount === 1 ? "message" : "messages"} in ${request.target}. Your task claim was not applied.\nRead them with: raft message read --target "${request.target}"\nAfter reviewing, rerun the claim if it is still correct.`
|
|
11576
|
+
};
|
|
11577
|
+
}
|
|
11578
|
+
const rows = (data.results ?? []).map((row) => {
|
|
11579
|
+
const state = rowState(row);
|
|
11580
|
+
return {
|
|
11581
|
+
ref: row.taskNumber ? String(row.taskNumber) : row.messageId ?? "?",
|
|
11582
|
+
taskNumber: row.taskNumber ?? null,
|
|
11583
|
+
messageId: row.messageId ?? null,
|
|
11584
|
+
state,
|
|
11585
|
+
mayWork: state === "claimed" || state === "already_yours",
|
|
11586
|
+
reason: row.reason ?? null,
|
|
11587
|
+
holder: row.conflict?.currentAssignee ?? null,
|
|
11588
|
+
conflict: row.conflict ?? null
|
|
11589
|
+
};
|
|
11590
|
+
});
|
|
11591
|
+
const claim = {
|
|
11592
|
+
target: request.target,
|
|
11593
|
+
rows,
|
|
11594
|
+
anyAuthorised: rows.some((row) => row.mayWork)
|
|
11595
|
+
};
|
|
7773
11596
|
return {
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
scopes: [...input.scopes]
|
|
11597
|
+
ok: true,
|
|
11598
|
+
state: !claim.anyAuthorised ? "refused" : rows.every((row) => row.mayWork) ? "claimed" : "partial",
|
|
11599
|
+
data: claim,
|
|
11600
|
+
next: claimNext(claim),
|
|
11601
|
+
text: claimText(claim)
|
|
7780
11602
|
};
|
|
7781
11603
|
}
|
|
7782
|
-
|
|
11604
|
+
//#endregion
|
|
11605
|
+
//#region ../shared/src/agentOps/wake.ts
|
|
11606
|
+
const RAFT_INBOX_NOTICE_SCHEMA = "raft-agent-inbox-notice.v1";
|
|
11607
|
+
const RAFT_NOTICE_SIGNATURE_HEADER = "x-raft-signature-256";
|
|
11608
|
+
const RAFT_NOTICE_DELIVERY_ID_HEADER = "x-raft-delivery-id";
|
|
11609
|
+
function headerValue(headers, name) {
|
|
11610
|
+
if (typeof headers.get === "function") return headers.get(name);
|
|
11611
|
+
const record = headers;
|
|
11612
|
+
const key = Object.keys(record).find((k) => k.toLowerCase() === name);
|
|
11613
|
+
const value = key ? record[key] : void 0;
|
|
11614
|
+
return Array.isArray(value) ? value[0] ?? null : value ?? null;
|
|
11615
|
+
}
|
|
11616
|
+
function hexToBytes(hex) {
|
|
11617
|
+
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) return null;
|
|
11618
|
+
const out = new Uint8Array(hex.length / 2);
|
|
11619
|
+
for (let i = 0; i < out.length; i += 1) out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
11620
|
+
return out;
|
|
11621
|
+
}
|
|
11622
|
+
function constantTimeEqual(a, b) {
|
|
11623
|
+
if (a.length !== b.length) return false;
|
|
11624
|
+
let diff = 0;
|
|
11625
|
+
for (let i = 0; i < a.length; i += 1) diff |= a[i] ^ b[i];
|
|
11626
|
+
return diff === 0;
|
|
11627
|
+
}
|
|
11628
|
+
async function hmacSha256(secret, body) {
|
|
11629
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), {
|
|
11630
|
+
name: "HMAC",
|
|
11631
|
+
hash: "SHA-256"
|
|
11632
|
+
}, false, ["sign"]);
|
|
11633
|
+
const bytes = new Uint8Array(body.byteLength);
|
|
11634
|
+
bytes.set(body);
|
|
11635
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", key, bytes.buffer));
|
|
11636
|
+
}
|
|
11637
|
+
const SENDER_TYPES = /* @__PURE__ */ new Set([
|
|
11638
|
+
"human",
|
|
11639
|
+
"agent",
|
|
11640
|
+
"system",
|
|
11641
|
+
"third_party_app"
|
|
11642
|
+
]);
|
|
11643
|
+
const FLAGS = /* @__PURE__ */ new Set([
|
|
11644
|
+
"mention",
|
|
11645
|
+
"non_member_mention",
|
|
11646
|
+
"thread",
|
|
11647
|
+
"dm",
|
|
11648
|
+
"task"
|
|
11649
|
+
]);
|
|
11650
|
+
function parseNotice(value, deliveryId) {
|
|
7783
11651
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
7784
|
-
const
|
|
7785
|
-
if (
|
|
7786
|
-
const
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
11652
|
+
const v = value;
|
|
11653
|
+
if (typeof v.noticeId !== "string" || typeof v.recipientAgentId !== "string" || typeof v.occurredAt !== "string" || !Array.isArray(v.targets)) return null;
|
|
11654
|
+
const targets = [];
|
|
11655
|
+
for (const raw of v.targets) {
|
|
11656
|
+
if (!raw || typeof raw !== "object") return null;
|
|
11657
|
+
const t = raw;
|
|
11658
|
+
if (typeof t.target !== "string") return null;
|
|
11659
|
+
const flags = Array.isArray(t.flags) ? t.flags.filter((f) => typeof f === "string" && FLAGS.has(f)) : [];
|
|
11660
|
+
targets.push({
|
|
11661
|
+
target: t.target,
|
|
11662
|
+
channelId: typeof t.channelId === "string" ? t.channelId : null,
|
|
11663
|
+
channelType: typeof t.channelType === "string" ? t.channelType : null,
|
|
11664
|
+
pendingCount: typeof t.pendingCount === "number" ? t.pendingCount : 0,
|
|
11665
|
+
firstPendingMsgId: typeof t.firstPendingMsgId === "string" ? t.firstPendingMsgId : null,
|
|
11666
|
+
latestMsgId: typeof t.latestMsgId === "string" ? t.latestMsgId : null,
|
|
11667
|
+
latestSenderName: typeof t.latestSenderName === "string" ? t.latestSenderName : null,
|
|
11668
|
+
latestSenderType: typeof t.latestSenderType === "string" && SENDER_TYPES.has(t.latestSenderType) ? t.latestSenderType : null,
|
|
11669
|
+
flags,
|
|
11670
|
+
mentionsYou: flags.includes("mention") || flags.includes("non_member_mention")
|
|
11671
|
+
});
|
|
7795
11672
|
}
|
|
7796
11673
|
return {
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
11674
|
+
noticeId: v.noticeId,
|
|
11675
|
+
deliveryId,
|
|
11676
|
+
recipientAgentId: v.recipientAgentId,
|
|
11677
|
+
occurredAt: v.occurredAt,
|
|
11678
|
+
text: typeof v.text === "string" ? v.text : "",
|
|
11679
|
+
targets
|
|
7802
11680
|
};
|
|
7803
11681
|
}
|
|
7804
11682
|
/**
|
|
7805
|
-
*
|
|
7806
|
-
*
|
|
7807
|
-
* it through the caller's explicit store. This does not mint or rotate a
|
|
7808
|
-
* credential and never returns the credential bytes.
|
|
11683
|
+
* Verify and parse a push notice. Async, WebCrypto, raw bytes. Never rejects
|
|
11684
|
+
* on time: treat every accepted notice as "pull your inbox now".
|
|
7809
11685
|
*/
|
|
7810
|
-
async function
|
|
7811
|
-
const
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
method: "GET",
|
|
7830
|
-
headers: { authorization: `Bearer ${credential}` }
|
|
7831
|
-
});
|
|
7832
|
-
} catch {
|
|
7833
|
-
throw new RaftCredentialError("CREDENTIAL_VERIFY_FAILED", "Could not reach the Raft Server to verify the Agent credential");
|
|
7834
|
-
}
|
|
7835
|
-
if (!response.ok) {
|
|
7836
|
-
if (response.status === 401 || response.status === 403) throw new RaftCredentialError("CREDENTIAL_REJECTED", "The Raft Server rejected the Agent credential");
|
|
7837
|
-
throw new RaftCredentialError("CREDENTIAL_VERIFY_FAILED", `The Raft Server could not verify the Agent credential (status ${response.status})`);
|
|
7838
|
-
}
|
|
7839
|
-
let payload;
|
|
11686
|
+
async function verifyInboxNotice(input) {
|
|
11687
|
+
const header = headerValue(input.headers, "x-raft-signature-256")?.trim() ?? "";
|
|
11688
|
+
if (!header) return {
|
|
11689
|
+
ok: false,
|
|
11690
|
+
reason: "missing_signature",
|
|
11691
|
+
message: `Missing ${RAFT_NOTICE_SIGNATURE_HEADER} header.`
|
|
11692
|
+
};
|
|
11693
|
+
const presented = header.toLowerCase().startsWith("sha256=") ? hexToBytes(header.slice(7)) : null;
|
|
11694
|
+
if (!presented) return {
|
|
11695
|
+
ok: false,
|
|
11696
|
+
reason: "bad_signature",
|
|
11697
|
+
message: "Signature header is not `sha256=<hex>`."
|
|
11698
|
+
};
|
|
11699
|
+
if (!constantTimeEqual(presented, await hmacSha256(input.secret, input.body))) return {
|
|
11700
|
+
ok: false,
|
|
11701
|
+
reason: "bad_signature",
|
|
11702
|
+
message: "Signature does not match the body."
|
|
11703
|
+
};
|
|
11704
|
+
let parsed;
|
|
7840
11705
|
try {
|
|
7841
|
-
|
|
11706
|
+
parsed = JSON.parse(new TextDecoder().decode(input.body));
|
|
7842
11707
|
} catch {
|
|
7843
|
-
|
|
11708
|
+
return {
|
|
11709
|
+
ok: false,
|
|
11710
|
+
reason: "invalid_body",
|
|
11711
|
+
message: "Body is not JSON."
|
|
11712
|
+
};
|
|
7844
11713
|
}
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
if (existing) return {
|
|
7850
|
-
serverUrl,
|
|
7851
|
-
...identity
|
|
11714
|
+
if ((parsed && typeof parsed === "object" ? parsed.schema : void 0) !== "raft-agent-inbox-notice.v1") return {
|
|
11715
|
+
ok: false,
|
|
11716
|
+
reason: "unsupported_schema",
|
|
11717
|
+
message: `Expected schema ${RAFT_INBOX_NOTICE_SCHEMA}.`
|
|
7852
11718
|
};
|
|
7853
|
-
const
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
...identity
|
|
11719
|
+
const notice = parseNotice(parsed, headerValue(input.headers, RAFT_NOTICE_DELIVERY_ID_HEADER));
|
|
11720
|
+
if (!notice) return {
|
|
11721
|
+
ok: false,
|
|
11722
|
+
reason: "invalid_body",
|
|
11723
|
+
message: "Notice body does not match the contract."
|
|
7859
11724
|
};
|
|
7860
|
-
try {
|
|
7861
|
-
await options.store.save(record);
|
|
7862
|
-
} catch (error) {
|
|
7863
|
-
if (error instanceof RaftCredentialError && error.code === "CREDENTIAL_STORE_CONFLICT") throw error;
|
|
7864
|
-
throw new RaftCredentialError("CREDENTIAL_STORE_WRITE_FAILED", "The verified Agent credential could not be saved");
|
|
7865
|
-
}
|
|
7866
11725
|
return {
|
|
7867
|
-
|
|
7868
|
-
|
|
11726
|
+
ok: true,
|
|
11727
|
+
notice
|
|
7869
11728
|
};
|
|
7870
11729
|
}
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
try {
|
|
7875
|
-
record = await options.store.load();
|
|
7876
|
-
} catch {
|
|
7877
|
-
throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The stored Agent credential could not be loaded");
|
|
7878
|
-
}
|
|
7879
|
-
if (!record) throw new RaftCredentialError("CREDENTIAL_NOT_FOUND", "No stored Raft Agent credential was found");
|
|
7880
|
-
const validated = parseStoredRaftCredential(record);
|
|
7881
|
-
if (!validated) throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The stored Agent credential is invalid");
|
|
7882
|
-
if (!validated.scopes.includes("send")) throw new RaftCredentialError("CREDENTIAL_SCOPE_MISSING", "The stored Raft Agent credential does not include the send capability required by this SDK");
|
|
7883
|
-
return createRaftClient({
|
|
7884
|
-
serverUrl: validated.serverUrl,
|
|
7885
|
-
credential: validated.credential,
|
|
7886
|
-
fetch: options.fetch,
|
|
7887
|
-
headers: options.headers,
|
|
7888
|
-
retry: options.retry,
|
|
7889
|
-
throttle: options.throttle
|
|
7890
|
-
});
|
|
11730
|
+
function statusText(status) {
|
|
11731
|
+
if (!status.registered) return "Push webhook: not registered.";
|
|
11732
|
+
return `Push webhook: ${status.url ?? "(url hidden)"} · ${status.enabled ? "enabled" : `disabled (${status.disabledReason ?? "unknown"})`}${status.lastDeliveryAt ? ` · last delivery ${status.lastDeliveryAt}` : ""}${status.consecutiveFailures > 0 ? ` · ${status.consecutiveFailures} consecutive failures` : ""}`;
|
|
7891
11733
|
}
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
/**
|
|
7896
|
-
* Create a Node.js credential store at one explicit absolute path. The SDK
|
|
7897
|
-
* never discovers CLI profiles or a home directory. Writes use a same-directory
|
|
7898
|
-
* temporary file followed by atomic rename, and the final file is mode 0600.
|
|
7899
|
-
*/
|
|
7900
|
-
function createFileCredentialStore(filePath) {
|
|
7901
|
-
if (!filePath.trim() || !(0, node_path.isAbsolute)(filePath)) throw new RaftSdkConfigurationError("INVALID_CREDENTIAL_PATH", "Raft credential file path must be an explicit absolute path");
|
|
11734
|
+
async function webhookStatus(client) {
|
|
11735
|
+
const result = await client.pushWebhook.status();
|
|
11736
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
7902
11737
|
return {
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
const temporaryPath = `${filePath}.${process.pid}.${(0, node_crypto.randomUUID)()}.tmp`;
|
|
7909
|
-
let handle;
|
|
7910
|
-
try {
|
|
7911
|
-
await (0, node_fs_promises.mkdir)(directory, {
|
|
7912
|
-
recursive: true,
|
|
7913
|
-
mode: 448
|
|
7914
|
-
});
|
|
7915
|
-
const existing = await loadFileCredential(filePath);
|
|
7916
|
-
if (existing) {
|
|
7917
|
-
assertSameCredential(existing, validated);
|
|
7918
|
-
return;
|
|
7919
|
-
}
|
|
7920
|
-
handle = await (0, node_fs_promises.open)(temporaryPath, "wx", 384);
|
|
7921
|
-
await handle.writeFile(`${JSON.stringify(validated, null, 2)}\n`, "utf8");
|
|
7922
|
-
await handle.sync();
|
|
7923
|
-
await (0, node_fs_promises.chmod)(temporaryPath, 384);
|
|
7924
|
-
await handle.close();
|
|
7925
|
-
handle = void 0;
|
|
7926
|
-
try {
|
|
7927
|
-
await (0, node_fs_promises.link)(temporaryPath, filePath);
|
|
7928
|
-
} catch (error) {
|
|
7929
|
-
if (error.code !== "EEXIST") throw error;
|
|
7930
|
-
const winner = await loadFileCredential(filePath);
|
|
7931
|
-
if (!winner) throw error;
|
|
7932
|
-
assertSameCredential(winner, validated);
|
|
7933
|
-
}
|
|
7934
|
-
await (0, node_fs_promises.unlink)(temporaryPath);
|
|
7935
|
-
} catch (error) {
|
|
7936
|
-
if (handle) await handle.close().catch(() => void 0);
|
|
7937
|
-
await (0, node_fs_promises.unlink)(temporaryPath).catch(() => void 0);
|
|
7938
|
-
if (error instanceof RaftCredentialError) throw error;
|
|
7939
|
-
throw new RaftCredentialError("CREDENTIAL_STORE_WRITE_FAILED", "The Raft Agent credential could not be written atomically");
|
|
7940
|
-
}
|
|
7941
|
-
}
|
|
11738
|
+
ok: true,
|
|
11739
|
+
state: "status",
|
|
11740
|
+
data: result.data,
|
|
11741
|
+
next: null,
|
|
11742
|
+
text: statusText(result.data)
|
|
7942
11743
|
};
|
|
7943
11744
|
}
|
|
7944
|
-
async function
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
}
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
handle = await (0, node_fs_promises.open)(filePath, node_fs.constants.O_RDONLY | noFollow);
|
|
7958
|
-
const openedMetadata = await handle.stat();
|
|
7959
|
-
if (!openedMetadata.isFile() || openedMetadata.size > MAX_CREDENTIAL_FILE_BYTES || initialMetadata.dev !== openedMetadata.dev || initialMetadata.ino !== openedMetadata.ino) throw fileReadError();
|
|
7960
|
-
if (process.platform !== "win32" && (openedMetadata.mode & 63) !== 0) throw new RaftCredentialError("CREDENTIAL_STORE_READ_FAILED", "The Raft credential file permissions are too broad; expected mode 0600");
|
|
7961
|
-
parsed = JSON.parse(await handle.readFile("utf8"));
|
|
7962
|
-
} catch (error) {
|
|
7963
|
-
if (error instanceof RaftCredentialError) throw error;
|
|
7964
|
-
throw fileReadError();
|
|
7965
|
-
} finally {
|
|
7966
|
-
await handle?.close().catch(() => void 0);
|
|
7967
|
-
}
|
|
7968
|
-
const record = parseStoredRaftCredential(parsed);
|
|
7969
|
-
if (!record) throw fileReadError();
|
|
7970
|
-
return record;
|
|
11745
|
+
async function registerWebhook(client, request) {
|
|
11746
|
+
const result = await client.pushWebhook.register({
|
|
11747
|
+
url: request.url,
|
|
11748
|
+
secret: request.secret
|
|
11749
|
+
});
|
|
11750
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11751
|
+
return {
|
|
11752
|
+
ok: true,
|
|
11753
|
+
state: "registered",
|
|
11754
|
+
data: result.data,
|
|
11755
|
+
next: null,
|
|
11756
|
+
text: statusText(result.data)
|
|
11757
|
+
};
|
|
7971
11758
|
}
|
|
7972
|
-
function
|
|
7973
|
-
|
|
11759
|
+
async function unregisterWebhook(client) {
|
|
11760
|
+
const result = await client.pushWebhook.unregister();
|
|
11761
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
11762
|
+
return {
|
|
11763
|
+
ok: true,
|
|
11764
|
+
state: "unregistered",
|
|
11765
|
+
data: null,
|
|
11766
|
+
next: null,
|
|
11767
|
+
text: "Push webhook: removed."
|
|
11768
|
+
};
|
|
7974
11769
|
}
|
|
7975
|
-
|
|
7976
|
-
|
|
11770
|
+
//#endregion
|
|
11771
|
+
//#region src/raft.ts
|
|
11772
|
+
function createRaft(options) {
|
|
11773
|
+
const serverUrl = requireServerUrl(options.serverUrl);
|
|
11774
|
+
const authorization = `Bearer ${requireAgentCredential(options.credential)}`;
|
|
11775
|
+
const frontier = SeenFrontier.fromSnapshot(options.frontier);
|
|
11776
|
+
const routes = createRaftRoutes({
|
|
11777
|
+
serverUrl,
|
|
11778
|
+
fetch: options.fetch,
|
|
11779
|
+
headers: options.headers,
|
|
11780
|
+
authorization,
|
|
11781
|
+
readAttempts: options.retry?.attempts,
|
|
11782
|
+
beforeRequest: options.throttle?.beforeRequest
|
|
11783
|
+
});
|
|
11784
|
+
const inboxApi = createAgentApiClient({ fetch: {
|
|
11785
|
+
baseUrl: serverUrl,
|
|
11786
|
+
fetch: (input, init) => (options.fetch ?? fetch)(input, {
|
|
11787
|
+
...init,
|
|
11788
|
+
cache: "no-store",
|
|
11789
|
+
redirect: "error"
|
|
11790
|
+
}),
|
|
11791
|
+
headers: Object.fromEntries(new Headers(options.headers)),
|
|
11792
|
+
retry: { attempts: 1 },
|
|
11793
|
+
throttle: options.throttle,
|
|
11794
|
+
auth: { authorization }
|
|
11795
|
+
} });
|
|
11796
|
+
return {
|
|
11797
|
+
identity: { whoami: () => getRaftContext(routes) },
|
|
11798
|
+
wake: {
|
|
11799
|
+
verifyNotice: (input) => verifyInboxNotice(input),
|
|
11800
|
+
webhook: {
|
|
11801
|
+
status: () => webhookStatus(routes),
|
|
11802
|
+
register: (request) => registerWebhook(routes, request),
|
|
11803
|
+
unregister: () => unregisterWebhook(routes)
|
|
11804
|
+
}
|
|
11805
|
+
},
|
|
11806
|
+
inbox: {
|
|
11807
|
+
check: (request) => checkInbox(inboxApi, request, frontier),
|
|
11808
|
+
drain: (request) => drainInbox(inboxApi, request, frontier),
|
|
11809
|
+
list: (request) => listInbox(routes, request)
|
|
11810
|
+
},
|
|
11811
|
+
messages: {
|
|
11812
|
+
read: (request) => readHistory(routes, request, frontier),
|
|
11813
|
+
send: (request) => sendMessage(routes, request, frontier),
|
|
11814
|
+
reply: (message, request) => replyTo(routes, message, request, frontier)
|
|
11815
|
+
},
|
|
11816
|
+
tasks: { claim: (request) => claimTasks(routes, request) },
|
|
11817
|
+
frontier,
|
|
11818
|
+
routes
|
|
11819
|
+
};
|
|
7977
11820
|
}
|
|
7978
11821
|
//#endregion
|
|
11822
|
+
exports.RAFT_INBOX_NOTICE_SCHEMA = RAFT_INBOX_NOTICE_SCHEMA;
|
|
11823
|
+
exports.RAFT_NOTICE_DELIVERY_ID_HEADER = RAFT_NOTICE_DELIVERY_ID_HEADER;
|
|
11824
|
+
exports.RAFT_NOTICE_SIGNATURE_HEADER = RAFT_NOTICE_SIGNATURE_HEADER;
|
|
7979
11825
|
exports.RaftCredentialError = RaftCredentialError;
|
|
7980
11826
|
exports.RaftSdkConfigurationError = RaftSdkConfigurationError;
|
|
11827
|
+
exports.SeenFrontier = SeenFrontier;
|
|
7981
11828
|
exports.bootstrapRaftCredential = bootstrapRaftCredential;
|
|
7982
11829
|
exports.createFileCredentialStore = createFileCredentialStore;
|
|
11830
|
+
exports.createRaft = createRaft;
|
|
7983
11831
|
exports.createRaftClient = createRaftClient;
|
|
7984
11832
|
exports.createRaftClientFromStore = createRaftClientFromStore;
|
|
11833
|
+
exports.createRaftRoutes = createRaftRoutes;
|
|
11834
|
+
exports.describeRaftRoute = describeRaftRoute;
|
|
7985
11835
|
exports.joinRaftChannelByTarget = joinRaftChannelByTarget;
|
|
11836
|
+
exports.listRaftRoutes = listRaftRoutes;
|
|
7986
11837
|
exports.parseRaftRegularChannelTarget = parseRaftRegularChannelTarget;
|
|
11838
|
+
exports.verifyInboxNotice = verifyInboxNotice;
|