conduyt-mcp 4.105.7 → 4.105.8
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.
|
@@ -103,7 +103,7 @@ const seg = (teammateId) => encodeURIComponent(teammateId.trim().toLowerCase());
|
|
|
103
103
|
const WHO = "Owners and admins, with a person's API key (the chat scope); an AI teammate's own key is refused (403 agent_route_refused). The teammate must have the provider Grok Bot (409 agent_not_webhook_provider otherwise). Answers 404 ai_teammates_off while AI teammates are switched off and 402 plan_required when the workspace is not entitled. Against a server that predates these routes (an HTML 404) the error says so.";
|
|
104
104
|
export function registerAiTeammateConnectTools(server, client) {
|
|
105
105
|
// (#1153, tri-surface parity) the workspace's AI teammates, so an agent can find the one to connect by id or @handle
|
|
106
|
-
strictInput(server.tool("conduyt_list_ai_teammates", "List the workspace's live AI teammates (GET /api/v1/chat/agents; a removed teammate is not listed), oldest first: [{ id, handle, name, provider (claude_code, codex, grok), state (listening, replying, away), connected (a Grok Bot teammate: its connection is stored and live; any other: a listener runs now), connection (a Grok Bot teammate's not_connected, waiting_for_hello, listening or not_listening; null otherwise), createdAt }]. Nothing secret is listed. Name a teammate to the Grok Bot tools by its id or its @handle. Owners and admins, with a person's API key (the chat scope; reading is enough); a member's key is refused (403) and an AI teammate's own key too (403 agent_route_refused). Adding a teammate stays in Settings > AI. Answers 404 ai_teammates_off while AI teammates are switched off and 402 plan_required when the workspace is not entitled; a workspace with more than 200 live teammates is refused to an API key (413 agent_list_over_ceiling; Settings > AI lists them all).", {}, async () => {
|
|
106
|
+
strictInput(server.tool("conduyt_list_ai_teammates", "List the workspace's live AI teammates (GET /api/v1/chat/agents; a removed teammate is not listed), oldest first: [{ id, handle, name, provider (claude_code, codex, grok), state (listening, replying, needs_attention (#1213: listening, nothing running, and a turn in the last 24 hours failed or timed out), away), connected (a Grok Bot teammate: its connection is stored and live; any other: a listener runs now), connection (a Grok Bot teammate's not_connected, waiting_for_hello, listening or not_listening; null otherwise), knowledgeFolders (#1209: how many knowledge folders the teammate's RUNNING listeners reported, the sum over the listeners whose heartbeat beats now, 0 = none; a knowledge folder is a folder on the listener's computer the teammate reads on every turn, whoever asked, read-only, secrets-looking files excluded; it is chosen on that computer with \`conduyt agent listen\` (the start directory, --knowledge <folder> or --no-knowledge), never through Conduyt, and its path never leaves the computer), createdAt }]. Nothing secret is listed. Name a teammate to the Grok Bot tools by its id or its @handle. Owners and admins, with a person's API key (the chat scope; reading is enough); a member's key is refused (403) and an AI teammate's own key too (403 agent_route_refused). Adding a teammate stays in Settings > AI. Answers 404 ai_teammates_off while AI teammates are switched off and 402 plan_required when the workspace is not entitled; a workspace with more than 200 live teammates is refused to an API key (413 agent_list_over_ceiling; Settings > AI lists them all).", {}, async () => {
|
|
107
107
|
try {
|
|
108
108
|
const res = await client.get("/api/v1/chat/agents");
|
|
109
109
|
return connectResult(unwrap(res), []);
|
package/dist/tools/chat.js
CHANGED
|
@@ -129,7 +129,7 @@ export function registerChatTools(server, client) {
|
|
|
129
129
|
return formatToolError("This Conduyt server did not apply topLevel: its messages carry no thread summaries (it predates the thread view). Read the channel without topLevel.");
|
|
130
130
|
return formatResult(page);
|
|
131
131
|
}));
|
|
132
|
-
strictInput(server.tool("conduyt_send_chat_message", "Send a Team Chat message as the API key's user (POST /api/v1/chat/channels/{id}/messages). The caller must be a channel member and have the communications:create permission. AI-agent keys are subject to the server's six-consecutive-agent-turn cap; a 409 agent_human_required means no message was sent (markerCreated: true on the refusal that inserted the one human-handoff line, false while the thread stays paused). A 409 agents_muted_in_channel means an admin turned \"agents respond here\" off for this channel: the post was refused, nothing was written, and it must not be retried (same shape as agent_human_required). body is plain Team Chat text (1-10,000 characters).", {
|
|
132
|
+
strictInput(server.tool("conduyt_send_chat_message", "Send a Team Chat message as the API key's user (POST /api/v1/chat/channels/{id}/messages). The caller must be a channel member and have the communications:create permission. AI-agent keys are subject to the server's six-consecutive-agent-turn cap; a 409 agent_human_required means no message was sent (markerCreated: true on the refusal that inserted the one human-handoff line, false while the thread stays paused). A 409 agents_muted_in_channel means an admin turned \"agents respond here\" off for this channel: the post was refused, nothing was written, and it must not be retried (same shape as agent_human_required). (#1213) A 409 write_outside_turn means this AI teammate is answering a turn (it holds a running claim): every post must be that turn's reply, in its thread, with conduyt_reply_chat_message and the turn's idempotencyKey + claimToken; nothing was written, do not retry. body is plain Team Chat text (1-10,000 characters).", {
|
|
133
133
|
channelId: z.string().uuid().describe("Channel UUID"),
|
|
134
134
|
body: z.string().trim().min(1).max(10_000).describe("Message text"),
|
|
135
135
|
idempotencyKey: z.string().regex(/^[A-Za-z0-9:_.-]{1,128}$/).optional().describe("Paired AI agent keys only: a key unique per agent and source turn (the listener uses reply:<source message id>). The same key returns the reply already created (200, idempotentReplay: true) instead of posting a second one; a key used in another channel is a 409 idempotency_key_reused. A person's key gets 400."),
|
|
@@ -137,14 +137,19 @@ export function registerChatTools(server, client) {
|
|
|
137
137
|
const result = await client.post(`/api/v1/chat/channels/${channelId}/messages`, { body, ...(idempotencyKey ? { idempotencyKey } : {}) });
|
|
138
138
|
return formatResult(result);
|
|
139
139
|
}));
|
|
140
|
-
strictInput(server.tool("conduyt_reply_chat_message", "Reply in a Team Chat thread as the API key's user (POST /api/v1/chat/channels/{id}/messages with parentId). parentId must name a live top-level message in that channel. The caller must be a member with communications:create. AI-agent keys are subject to the same six-consecutive-agent-turn cap as a top-level send, and to the same 409 agent_human_required / agents_muted_in_channel refusals (nothing written, do not retry; agents_muted_in_channel = an admin turned \"agents respond here\" off for the channel).", {
|
|
140
|
+
strictInput(server.tool("conduyt_reply_chat_message", "Reply in a Team Chat thread as the API key's user (POST /api/v1/chat/channels/{id}/messages with parentId). parentId must name a live top-level message in that channel. The caller must be a member with communications:create. AI-agent keys are subject to the same six-consecutive-agent-turn cap as a top-level send, and to the same 409 agent_human_required / agents_muted_in_channel refusals (nothing written, do not retry; agents_muted_in_channel = an admin turned \"agents respond here\" off for the channel). (#1213) AI teammates may talk to each other in a thread, bounded: with two consecutive AI posts already (the asker and the answerer), a turn answering an AI teammate's message is refused 409 agent_human_required with reason agent_chain_cap (the same one pause line; the thread waits for a person). A 409 parent_deleted means the parent, its thread or the turn's message was deleted: nothing was written and the turn ended; do not retry. (#1213) While an AI teammate is answering a turn (it holds a running claim) every post must be that turn's reply (its reply:<source> key and claim token, in the source's channel and thread): anything else is refused 409 write_outside_turn, nothing posted. A Grok Bot turn's answer (idempotencyKey turn:<message id>) needs turnToken (409 turn_token_invalid otherwise).", {
|
|
141
141
|
channelId: z.string().uuid().describe("Channel UUID"),
|
|
142
142
|
parentId: z.string().uuid().describe("Top-level message UUID to reply to"),
|
|
143
143
|
body: z.string().trim().min(1).max(10_000).describe("Reply text"),
|
|
144
144
|
idempotencyKey: z.string().regex(/^[A-Za-z0-9:_.-]{1,128}$/).optional().describe("Paired AI agent keys only: a key unique per agent and source turn (the listener uses reply:<source message id>). The same key returns the reply already created (200, idempotentReplay: true) instead of posting a second one; a key used in another channel is a 409 idempotency_key_reused. A person's key gets 400."),
|
|
145
145
|
alsoInChannel: z.boolean().optional().describe("(#684, a person's reply, only while the workspace's thread view is on) true = \"Also send to #channel\": the ONE reply is also shown in the channel (topLevel reads list it with threadReply.reason alsoInChannel); never a second message or notification. Sent only when true. 409 team_chat_threads_off while the thread view is off (nothing posted); an AI teammate never sets it (its first answer already shows in the channel): refused here with claimToken, 400 from the server otherwise"),
|
|
146
146
|
claimToken: z.string().regex(/^[1-9][0-9]{0,8}:[A-Za-z0-9:_.-]{8,128}$/).optional().describe("Paired AI agent keys only; REQUIRED with idempotencyKey reply:<source message id> (refused otherwise, nothing posted): the listener's claim token <generation>:<listener id> from the turn prompt. The reply completes that turn only when posted in the source's channel and thread with the current generation's token; a reply:<source> reply anywhere else, or with a stale token, is refused 409 (reply_wrong_channel / reply_wrong_thread / reply_stale_claim) with nothing posted, so a corrected retry still completes the turn."),
|
|
147
|
-
|
|
147
|
+
turnToken: z.string().regex(/^[1-9][0-9]{9,15}\.[0-9a-f]{64}$/).optional().describe("(#1213) an AI teammate answering a Grok Bot turn: the turn's turnToken from its payload; REQUIRED with idempotencyKey turn:<message id> (refused otherwise, nothing posted)"),
|
|
148
|
+
}, async ({ channelId, parentId, body, idempotencyKey, claimToken, turnToken, alsoInChannel }) => {
|
|
149
|
+
// (#1213) a Grok Bot turn's answer always carries the turn's server-minted token
|
|
150
|
+
if (!turnToken && typeof idempotencyKey === "string" && /^turn:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(idempotencyKey)) {
|
|
151
|
+
throw new Error("turnToken is required with an idempotencyKey turn:<message id> (the turn's turnToken from its payload); nothing was posted");
|
|
152
|
+
}
|
|
148
153
|
// (#599 r7) a reply to an inbox turn (idempotencyKey reply:<source message id>) always names its claim
|
|
149
154
|
if (!claimToken && typeof idempotencyKey === "string" && /^reply:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(idempotencyKey)) {
|
|
150
155
|
throw new Error("claimToken is required with an idempotencyKey reply:<source message id> (the turn prompt's reply claim token); nothing was posted");
|
|
@@ -155,7 +160,7 @@ export function registerChatTools(server, client) {
|
|
|
155
160
|
const also = alsoInChannel === true;
|
|
156
161
|
let result;
|
|
157
162
|
try {
|
|
158
|
-
result = await client.post(`/api/v1/chat/channels/${channelId}/messages`, { body, parentId, ...(idempotencyKey ? { idempotencyKey } : {}), ...(claimToken ? { claimToken } : {}), ...(also ? { alsoInChannel: true } : {}) });
|
|
163
|
+
result = await client.post(`/api/v1/chat/channels/${channelId}/messages`, { body, parentId, ...(idempotencyKey ? { idempotencyKey } : {}), ...(claimToken ? { claimToken } : {}), ...(turnToken ? { turnToken } : {}), ...(also ? { alsoInChannel: true } : {}) });
|
|
159
164
|
}
|
|
160
165
|
catch (err) {
|
|
161
166
|
const refused = also ? threadsRefusal(err, { offSentence: `${THREADS_OFF_SENTENCE} Nothing was posted: send it again without alsoInChannel.` }) : null;
|
|
@@ -167,20 +172,22 @@ export function registerChatTools(server, client) {
|
|
|
167
172
|
const out = formatResult(result);
|
|
168
173
|
return note ? { ...out, content: [...out.content, { type: "text", text: note }] } : out;
|
|
169
174
|
}));
|
|
170
|
-
strictInput(server.tool("conduyt_react_chat_message", "Add a reaction to a Team Chat message as the API key's user (POST /api/v1/chat/channels/{id}/messages/{messageId}/reactions). The server accepts only its Team Chat reaction set and returns the fully hydrated message. Repeating the same reaction is safe and leaves one reaction from this user. (#802, app #655 c) An AI teammate ENDS its turn with a reaction by passing claimToken (the turn prompt's reply claim token): the reaction then completes that turn (outcome reacted) in the same transaction, so the turn needs no reply. React to the turn's message or one in its thread; when it is another message of the thread, pass the turn's message id as sourceMessageId. Exactly one of a reply, a reaction or conduyt_stay_silent ends a turn: a token that is not the running holder's, or a message outside the turn's thread, is refused 409 reaction_stale_claim, and a turn that already ended (a reply, a silence, another reaction) 409 turn_finished with its outcome; nothing is written on either, do not retry. A person's key sending claimToken gets 400.", {
|
|
175
|
+
strictInput(server.tool("conduyt_react_chat_message", "Add a reaction to a Team Chat message as the API key's user (POST /api/v1/chat/channels/{id}/messages/{messageId}/reactions). The server accepts only its Team Chat reaction set and returns the fully hydrated message. Repeating the same reaction is safe and leaves one reaction from this user. (#802, app #655 c) An AI teammate ENDS its turn with a reaction by passing claimToken (the turn prompt's reply claim token): the reaction then completes that turn (outcome reacted) in the same transaction, so the turn needs no reply. React to the turn's message or one in its thread; when it is another message of the thread, pass the turn's message id as sourceMessageId. Exactly one of a reply, a reaction or conduyt_stay_silent ends a turn: a token that is not the running holder's, or a message outside the turn's thread, is refused 409 reaction_stale_claim, and a turn that already ended (a reply, a silence, another reaction) 409 turn_finished with its outcome; nothing is written on either, do not retry. A person's key sending claimToken gets 400. (#1213 r3) While the teammate is answering a turn its only reaction is that turn's (409 write_outside_turn otherwise); a Grok Bot teammate passes its turn's turnToken (and sourceMessageId for another message of the turn's thread).", {
|
|
171
176
|
channelId: z.string().uuid().describe("Channel UUID"),
|
|
172
177
|
messageId: z.string().uuid().describe("Message UUID"),
|
|
173
178
|
emoji: z.string().min(1).max(16).describe("One allowed Team Chat emoji"),
|
|
174
179
|
claimToken: z.string().regex(/^[1-9][0-9]{0,8}:[A-Za-z0-9:_.-]{8,128}$/).optional().describe("Paired AI agent keys only: the turn prompt's reply claim token <generation>:<listener id>; the reaction then ends that turn"),
|
|
175
|
-
sourceMessageId: z.string().uuid().optional().describe("With claimToken
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
sourceMessageId: z.string().uuid().optional().describe("With claimToken (or turnToken): the turn's message id, when the reaction targets ANOTHER message of its thread (default: the reacted message)"),
|
|
181
|
+
turnToken: z.string().regex(/^[1-9][0-9]{9,15}\.[0-9a-f]{64}$/).optional().describe("(#1213 r3) an AI teammate answering a Grok Bot turn: the turn's turnToken from its payload; REQUIRED for that teammate's reaction"),
|
|
182
|
+
}, async ({ channelId, messageId, emoji, claimToken, sourceMessageId, turnToken }) => {
|
|
183
|
+
if (sourceMessageId !== undefined && claimToken === undefined && turnToken === undefined)
|
|
184
|
+
throw new Error("sourceMessageId needs claimToken or turnToken (it names the turn the reaction belongs to); nothing was sent");
|
|
185
|
+
const source = sourceMessageId && sourceMessageId.toLowerCase() !== messageId.toLowerCase() ? { sourceMessageId } : {};
|
|
186
|
+
const turn = claimToken ? { claimToken, ...source } : turnToken ? { turnToken, ...source } : {};
|
|
180
187
|
const result = await client.post(`/api/v1/chat/channels/${channelId}/messages/${messageId}/reactions`, { emoji, ...turn });
|
|
181
188
|
return formatResult(result);
|
|
182
189
|
}));
|
|
183
|
-
strictInput(server.tool("conduyt_stay_silent", "(#802, app #655 c; paired AI agent keys) The turn prompt's stay_silent: END an AI teammate's Team Chat turn without replying or reacting, when no response is warranted (POST /api/v1/chat/agents/me/inbox/claim with action silent, for the turn's message, with the generation and listener id read from claimToken). Completes the claim with outcome silent and moves the agent's inbox cursor as an acknowledgement does; repeating it on the claim it completed answers the same ({ silent: true, outcome, cursor }). Exactly one of a reply, a reaction or a silence ends a turn: a turn that already ended with a reply or a reaction is refused 409 turn_finished (outcome says which), a stale token 409 agent_claim_lost; nothing changes on either, do not retry. Any other key is refused.", {
|
|
190
|
+
strictInput(server.tool("conduyt_stay_silent", "(#802, app #655 c; paired AI agent keys) The turn prompt's stay_silent: END an AI teammate's Team Chat turn without replying or reacting, when no response is warranted (POST /api/v1/chat/agents/me/inbox/claim with action silent, for the turn's message, with the generation and listener id read from claimToken). Completes the claim with outcome silent and moves the agent's inbox cursor as an acknowledgement does; repeating it on the claim it completed answers the same ({ silent: true, outcome, cursor }). Exactly one of a reply, a reaction or a silence ends a turn: a turn that already ended with a reply or a reaction is refused 409 turn_finished (outcome says which; (#1213) also paused or trigger_deleted when the server ended it), a stale token 409 agent_claim_lost; nothing changes on either, do not retry. (#1213) Silence is the exception: use it only when the message is clearly not for this teammate; a turn whose teammate already posted in the channel since it started ends replied, never silent. Any other key is refused.", {
|
|
184
191
|
messageId: z.string().uuid().describe("The turn's message id (the turn prompt's Message id)"),
|
|
185
192
|
claimToken: z.string().regex(/^[1-9][0-9]{0,8}:[A-Za-z0-9:_.-]{8,128}$/).describe("The turn prompt's reply claim token <generation>:<listener id>"),
|
|
186
193
|
}, async ({ messageId, claimToken }) => {
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-11-
|
|
2
|
+
"generatedAt": "2026-11-08T09:31:39.733Z",
|
|
3
3
|
"toolCount": 393,
|
|
4
|
-
"totalEndpoints":
|
|
4
|
+
"totalEndpoints": 1168,
|
|
5
5
|
"counts": {
|
|
6
6
|
"covered": 347,
|
|
7
|
-
"excluded":
|
|
8
|
-
"planned":
|
|
7
|
+
"excluded": 360,
|
|
8
|
+
"planned": 461,
|
|
9
9
|
"uncategorized": 0
|
|
10
10
|
},
|
|
11
11
|
"matrix": {
|
|
@@ -365,10 +365,26 @@
|
|
|
365
365
|
"status": "excluded",
|
|
366
366
|
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
367
367
|
},
|
|
368
|
+
"POST /api/v1/admin/external-deletions/:id/confirm-mailbox": {
|
|
369
|
+
"status": "excluded",
|
|
370
|
+
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
371
|
+
},
|
|
372
|
+
"POST /api/v1/admin/external-deletions/:id/release": {
|
|
373
|
+
"status": "excluded",
|
|
374
|
+
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
375
|
+
},
|
|
368
376
|
"POST /api/v1/admin/external-deletions/:id/replay": {
|
|
369
377
|
"status": "excluded",
|
|
370
378
|
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
371
379
|
},
|
|
380
|
+
"POST /api/v1/admin/external-deletions/:id/resolve": {
|
|
381
|
+
"status": "excluded",
|
|
382
|
+
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
383
|
+
},
|
|
384
|
+
"GET /api/v1/admin/external-deletions/recovery": {
|
|
385
|
+
"status": "excluded",
|
|
386
|
+
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
387
|
+
},
|
|
372
388
|
"POST /api/v1/admin/impersonate": {
|
|
373
389
|
"status": "excluded",
|
|
374
390
|
"reason": "super-admin platform operations (Conduyt staff only)"
|
|
@@ -1293,6 +1309,10 @@
|
|
|
1293
1309
|
"status": "planned",
|
|
1294
1310
|
"reason": "call subroutes beyond list/log"
|
|
1295
1311
|
},
|
|
1312
|
+
"POST /api/v1/calls/:id/diagnostics": {
|
|
1313
|
+
"status": "planned",
|
|
1314
|
+
"reason": "call subroutes beyond list/log"
|
|
1315
|
+
},
|
|
1296
1316
|
"POST /api/v1/calls/:id/link": {
|
|
1297
1317
|
"status": "covered",
|
|
1298
1318
|
"tools": [
|
|
@@ -1381,6 +1401,10 @@
|
|
|
1381
1401
|
"conduyt_stay_silent"
|
|
1382
1402
|
]
|
|
1383
1403
|
},
|
|
1404
|
+
"PUT /api/v1/chat/agents/me/knowledge": {
|
|
1405
|
+
"status": "excluded",
|
|
1406
|
+
"reason": "(#599) the listener protocol of a paired agent key (long poll, per-event ack, heartbeat): served by the npm CLI `conduyt agent listen`, not a request/response MCP tool"
|
|
1407
|
+
},
|
|
1384
1408
|
"GET /api/v1/chat/agents/me/mcp": {
|
|
1385
1409
|
"status": "excluded",
|
|
1386
1410
|
"reason": "(#599) the listener protocol of a paired agent key (long poll, per-event ack, heartbeat): served by the npm CLI `conduyt agent listen`, not a request/response MCP tool"
|