@canonmsg/claude-code-plugin 0.14.15 → 0.14.17
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/dist/host.js +54 -25
- package/dist/mcp-args.d.ts +2 -1
- package/dist/mcp-args.js +3 -1
- package/dist/server.js +17 -4
- package/dist/session-state.d.ts +1 -0
- package/package.json +3 -3
package/dist/host.js
CHANGED
|
@@ -26,7 +26,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
26
26
|
import { existsSync } from 'node:fs';
|
|
27
27
|
import { query, } from '@anthropic-ai/claude-agent-sdk';
|
|
28
28
|
import { isAnthropicImageAttachment, materializeMessageMedia, materializeReplyContextMedia, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
|
|
29
|
-
import { CLAUDE_EFFORT_OPTIONS, CLAUDE_PERMISSION_MODE_OPTIONS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildConversationEnvironmentKey, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPlanApprovalRequest, buildQuestionRequest, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createTurnOutputController, createRuntimeStatePublisher, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, ApprovalManager, ExecutionEnvironmentError, FINAL_MESSAGE_HANDOFF_MS, buildLocalRuntimeId, getActiveProfileLock, heartbeatLocalRuntimeEntry, normalizeTurnMetadata, prepareConversationEnvironment, resolveCanonAgent, initRTDBAuth, rtdbRead, rtdbWrite, loadHostSessionConfig, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, saveRuntimeSessionState, clearRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
29
|
+
import { CLAUDE_EFFORT_OPTIONS, CLAUDE_PERMISSION_MODE_OPTIONS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildConversationEnvironmentKey, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPlanApprovalRequest, buildQuestionRequest, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, ApprovalManager, ExecutionEnvironmentError, FINAL_MESSAGE_HANDOFF_MS, buildLocalRuntimeId, getActiveProfileLock, heartbeatLocalRuntimeEntry, normalizeTurnMetadata, prepareConversationEnvironment, resolveCanonAgent, initRTDBAuth, rtdbRead, rtdbWrite, sendMessageWithRetry, loadHostSessionConfig, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, saveRuntimeSessionState, clearRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
30
30
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
31
31
|
import { runCli } from './cli-entry.js';
|
|
32
32
|
import { decideClaudeToolPermissionForMode, parseAllowedNonOwnerClaudeTools, } from './tool-policy.js';
|
|
@@ -522,7 +522,10 @@ const STREAMING_THROTTLE_MS = 250;
|
|
|
522
522
|
const HEARTBEAT_MS = 30_000;
|
|
523
523
|
const IDLE_CHECK_MS = 60_000;
|
|
524
524
|
const CONTROL_POLL_MS = 2000;
|
|
525
|
-
const
|
|
525
|
+
const IDLE_CONTROL_POLL_MS = 10_000;
|
|
526
|
+
const CONTROL_POLL_JITTER_MS = 1_000;
|
|
527
|
+
const FINAL_DELIVERY_RETRY_MS = 30_000;
|
|
528
|
+
const MAX_FINAL_DELIVERY_RETRIES = 1;
|
|
526
529
|
const CLAUDE_RUNTIME_CAPABILITIES = {
|
|
527
530
|
...DEFAULT_RUNTIME_CAPABILITIES,
|
|
528
531
|
supportsInterrupt: true,
|
|
@@ -532,6 +535,10 @@ const CLAUDE_RUNTIME_CAPABILITIES = {
|
|
|
532
535
|
supportsRequiresAction: true,
|
|
533
536
|
supportsNonFinalPermanentMessages: false,
|
|
534
537
|
};
|
|
538
|
+
function controlPollDelayMs(hasActiveWork) {
|
|
539
|
+
const base = hasActiveWork ? CONTROL_POLL_MS : IDLE_CONTROL_POLL_MS;
|
|
540
|
+
return base + Math.floor(Math.random() * CONTROL_POLL_JITTER_MS);
|
|
541
|
+
}
|
|
535
542
|
function buildCanonPrompt(input) {
|
|
536
543
|
const { session, ...promptInput } = input;
|
|
537
544
|
return buildCanonHostPrompt({
|
|
@@ -602,7 +609,7 @@ async function buildCanonUserContent(input) {
|
|
|
602
609
|
];
|
|
603
610
|
}
|
|
604
611
|
// ── Session factory ─────────────────────────────────────────────────
|
|
605
|
-
function createSession(conversationId, environment, agentId, client, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
|
|
612
|
+
function createSession(conversationId, environment, agentId, client, typingSignals, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
|
|
606
613
|
const { cwd } = environment;
|
|
607
614
|
const cardResponseUserId = config.ownerId ?? undefined;
|
|
608
615
|
const cardResponseUser = cardResponseUserId ? { responseUserId: cardResponseUserId } : undefined;
|
|
@@ -704,7 +711,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
704
711
|
const questionId = randomUUID();
|
|
705
712
|
const questions = input.questions ?? [];
|
|
706
713
|
const questionCard = buildQuestionRequest(questionId, questions, cardResponseUser);
|
|
707
|
-
await client
|
|
714
|
+
await sendMessageWithRetry(client, conversationId, questionCard.text, {
|
|
715
|
+
messageId: `claude-question-${questionId}`,
|
|
708
716
|
...(session.activeSelfContextId
|
|
709
717
|
? { selfContextId: session.activeSelfContextId }
|
|
710
718
|
: {}),
|
|
@@ -754,7 +762,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
754
762
|
summary: 'Claude has prepared a plan and is asking permission to continue.',
|
|
755
763
|
...(cardResponseUser ?? {}),
|
|
756
764
|
});
|
|
757
|
-
await client
|
|
765
|
+
await sendMessageWithRetry(client, conversationId, planApproval.text, {
|
|
766
|
+
messageId: `claude-plan-${planId}`,
|
|
758
767
|
...(session.activeSelfContextId
|
|
759
768
|
? { selfContextId: session.activeSelfContextId }
|
|
760
769
|
: {}),
|
|
@@ -912,17 +921,21 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
912
921
|
clearIdleResetTimer();
|
|
913
922
|
session.idleResetTimer = setTimeout(() => {
|
|
914
923
|
session.idleResetTimer = null;
|
|
915
|
-
|
|
924
|
+
typingSignals.clear(conversationId).catch(() => { });
|
|
916
925
|
clearStreaming();
|
|
917
926
|
resetTurnToIdle();
|
|
918
927
|
}, FINAL_MESSAGE_HANDOFF_MS);
|
|
919
928
|
}
|
|
920
929
|
function markPendingFinalDelivery(finalText, turn) {
|
|
921
930
|
clearIdleResetTimer();
|
|
931
|
+
const retryCount = session.pendingFinalDelivery?.turnKey === turn.turnKey
|
|
932
|
+
? session.pendingFinalDelivery.retryCount
|
|
933
|
+
: 0;
|
|
922
934
|
session.pendingFinalText = finalText;
|
|
923
935
|
session.pendingFinalDelivery = {
|
|
924
936
|
turnKey: turn.turnKey,
|
|
925
937
|
text: finalText,
|
|
938
|
+
retryCount,
|
|
926
939
|
messageId: buildClaudeFinalMessageId({
|
|
927
940
|
agentId,
|
|
928
941
|
conversationId,
|
|
@@ -935,7 +948,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
935
948
|
streamingOutput.replaceSnapshot(finalText, 'streaming').catch(() => { });
|
|
936
949
|
writeState();
|
|
937
950
|
writeTurn();
|
|
938
|
-
|
|
951
|
+
typingSignals.start(conversationId, 'typing').catch(() => { });
|
|
939
952
|
}
|
|
940
953
|
async function deliverFinalReply(finalText, turn = session.activeInput) {
|
|
941
954
|
if (!shouldDeliverClaudeFinal({
|
|
@@ -954,7 +967,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
954
967
|
turnKey: deliverTurn.turnKey,
|
|
955
968
|
});
|
|
956
969
|
try {
|
|
957
|
-
await client
|
|
970
|
+
await sendMessageWithRetry(client, conversationId, finalText, {
|
|
958
971
|
messageId: finalMessageId,
|
|
959
972
|
...(session.activeSelfContextId
|
|
960
973
|
? { selfContextId: session.activeSelfContextId }
|
|
@@ -987,6 +1000,14 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
987
1000
|
function scheduleFinalDeliveryRetry(pending = session.pendingFinalDelivery) {
|
|
988
1001
|
if (!pending || session.closed || session.pendingFinalDelivery?.turnKey !== pending.turnKey)
|
|
989
1002
|
return;
|
|
1003
|
+
if (pending.retryCount >= MAX_FINAL_DELIVERY_RETRIES) {
|
|
1004
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Final reply delivery exhausted after bounded retries`);
|
|
1005
|
+
session.pendingFinalDelivery = null;
|
|
1006
|
+
resetTurnToIdle();
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
pending.retryCount += 1;
|
|
1010
|
+
session.pendingFinalDelivery = pending;
|
|
990
1011
|
clearFinalDeliveryTimer();
|
|
991
1012
|
session.finalDeliveryTimer = setTimeout(() => {
|
|
992
1013
|
session.finalDeliveryTimer = null;
|
|
@@ -1091,19 +1112,17 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1091
1112
|
return;
|
|
1092
1113
|
if (session.turnState !== 'thinking' && session.turnState !== 'tool')
|
|
1093
1114
|
return;
|
|
1094
|
-
|
|
1115
|
+
typingSignals.start(conversationId, 'thinking').catch(() => { });
|
|
1095
1116
|
}
|
|
1096
1117
|
function startVisibleWorkSignal() {
|
|
1097
1118
|
refreshVisibleWorkSignal();
|
|
1098
|
-
if (session.typingKeepaliveTimer)
|
|
1099
|
-
return;
|
|
1100
|
-
session.typingKeepaliveTimer = setInterval(refreshVisibleWorkSignal, 3500);
|
|
1101
1119
|
}
|
|
1102
1120
|
function stopVisibleWorkSignal() {
|
|
1103
1121
|
if (session.typingKeepaliveTimer) {
|
|
1104
1122
|
clearInterval(session.typingKeepaliveTimer);
|
|
1105
1123
|
session.typingKeepaliveTimer = null;
|
|
1106
1124
|
}
|
|
1125
|
+
typingSignals.clear(conversationId).catch(() => { });
|
|
1107
1126
|
}
|
|
1108
1127
|
async function markQueuedMessageAccepted(sourceMessageId, markAccepted) {
|
|
1109
1128
|
if (!markAccepted || !sourceMessageId)
|
|
@@ -1149,7 +1168,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1149
1168
|
session.turnState = 'interrupted';
|
|
1150
1169
|
stopVisibleWorkSignal();
|
|
1151
1170
|
clearStreaming().catch(() => { });
|
|
1152
|
-
client.setTyping(conversationId, false).catch(() => { });
|
|
1153
1171
|
writeTurn();
|
|
1154
1172
|
void session.query.interrupt().catch(() => { });
|
|
1155
1173
|
return;
|
|
@@ -1189,7 +1207,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1189
1207
|
const event = msg.event;
|
|
1190
1208
|
if (isActiveTurnInterrupted()) {
|
|
1191
1209
|
stopVisibleWorkSignal();
|
|
1192
|
-
client.setTyping(conversationId, false).catch(() => { });
|
|
1193
1210
|
break;
|
|
1194
1211
|
}
|
|
1195
1212
|
if (event?.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
@@ -1219,7 +1236,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1219
1236
|
session.toolInProgress = false;
|
|
1220
1237
|
session.turnState = 'streaming';
|
|
1221
1238
|
stopVisibleWorkSignal();
|
|
1222
|
-
client.setTyping(conversationId, false).catch(() => { });
|
|
1223
1239
|
writeTurn();
|
|
1224
1240
|
onStreamDelta(event.delta.text);
|
|
1225
1241
|
}
|
|
@@ -1244,7 +1260,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1244
1260
|
}
|
|
1245
1261
|
else if (state === 'idle') {
|
|
1246
1262
|
stopVisibleWorkSignal();
|
|
1247
|
-
client.setTyping(conversationId, false).catch(() => { });
|
|
1248
1263
|
}
|
|
1249
1264
|
else if (state === 'requires_action') {
|
|
1250
1265
|
clearIdleResetTimer();
|
|
@@ -1255,7 +1270,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1255
1270
|
session.toolInProgress = false;
|
|
1256
1271
|
session.turnState = 'waiting_input';
|
|
1257
1272
|
stopVisibleWorkSignal();
|
|
1258
|
-
client.setTyping(conversationId, false).catch(() => { });
|
|
1259
1273
|
}
|
|
1260
1274
|
writeState();
|
|
1261
1275
|
writeTurn();
|
|
@@ -1439,6 +1453,11 @@ export async function main() {
|
|
|
1439
1453
|
// ── Canon API client + RTDB auth ──
|
|
1440
1454
|
const client = new CanonClient(apiKey, baseUrl);
|
|
1441
1455
|
initRTDBAuth(client);
|
|
1456
|
+
const typingSignals = createTypingStatusPublisher({
|
|
1457
|
+
setTyping: (conversationId, typing, status) => status
|
|
1458
|
+
? client.setTyping(conversationId, typing, status)
|
|
1459
|
+
: client.setTyping(conversationId, typing),
|
|
1460
|
+
});
|
|
1442
1461
|
async function markQueuedMessageAcceptedForConversation(conversationId, sourceMessageId, markAccepted) {
|
|
1443
1462
|
if (!markAccepted || !sourceMessageId)
|
|
1444
1463
|
return;
|
|
@@ -1645,7 +1664,7 @@ export async function main() {
|
|
|
1645
1664
|
runtimeState.clearStreaming(conversation.id).catch(() => { });
|
|
1646
1665
|
runtimeState.clearSessionState(conversation.id).catch(() => { });
|
|
1647
1666
|
runtimeState.clearTurnState(conversation.id).catch(() => { });
|
|
1648
|
-
|
|
1667
|
+
typingSignals.clear(conversation.id).catch(() => { });
|
|
1649
1668
|
}
|
|
1650
1669
|
console.error(`[canon-host] ${convos.length} conversations`);
|
|
1651
1670
|
}
|
|
@@ -1803,7 +1822,8 @@ export async function main() {
|
|
|
1803
1822
|
}
|
|
1804
1823
|
runtimeState.clearSessionState(conversationId).catch(() => { });
|
|
1805
1824
|
runtimeState.clearTurnState(conversationId).catch(() => { });
|
|
1806
|
-
|
|
1825
|
+
typingSignals.clear(conversationId).catch(() => { });
|
|
1826
|
+
typingSignals.dispose(conversationId);
|
|
1807
1827
|
try {
|
|
1808
1828
|
session.query.close();
|
|
1809
1829
|
}
|
|
@@ -1822,7 +1842,7 @@ export async function main() {
|
|
|
1822
1842
|
session.pendingFinalDelivery = null;
|
|
1823
1843
|
session.turnState = 'interrupted';
|
|
1824
1844
|
session.clearStreaming().catch(() => { });
|
|
1825
|
-
|
|
1845
|
+
typingSignals.clear(conversationId).catch(() => { });
|
|
1826
1846
|
if (session.state.state === 'running') {
|
|
1827
1847
|
await session.query.interrupt().catch(() => { });
|
|
1828
1848
|
}
|
|
@@ -1890,7 +1910,7 @@ export async function main() {
|
|
|
1890
1910
|
executionMode: environment.mode,
|
|
1891
1911
|
});
|
|
1892
1912
|
const resumeId = savedSessionIds.get(resumeKey) ?? persisted?.claudeSessionId;
|
|
1893
|
-
const session = createSession(conversationId, environment, agentId, client, runtimeState, (convoId, sdkSessionId) => {
|
|
1913
|
+
const session = createSession(conversationId, environment, agentId, client, typingSignals, runtimeState, (convoId, sdkSessionId) => {
|
|
1894
1914
|
// Called when session processing loop ends — save session ID for resume
|
|
1895
1915
|
const existing = sessions.get(convoId);
|
|
1896
1916
|
if (existing) {
|
|
@@ -1913,6 +1933,8 @@ export async function main() {
|
|
|
1913
1933
|
}
|
|
1914
1934
|
}
|
|
1915
1935
|
existing?.clearStreaming().catch(() => { });
|
|
1936
|
+
typingSignals.clear(convoId).catch(() => { });
|
|
1937
|
+
typingSignals.dispose(convoId);
|
|
1916
1938
|
sessions.delete(convoId);
|
|
1917
1939
|
runtimeState.clearSessionState(convoId).catch(() => { });
|
|
1918
1940
|
runtimeState.clearTurnState(convoId).catch(() => { });
|
|
@@ -2028,7 +2050,8 @@ export async function main() {
|
|
|
2028
2050
|
const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
|
|
2029
2051
|
console.error(`[canon-host] [${input.conversationId.slice(0, 8)}] Failed to create recovered session: ${message}`);
|
|
2030
2052
|
await markQueuedMessageAcceptedForConversation(input.conversationId, m.id ?? null, shouldMarkAccepted);
|
|
2031
|
-
await client
|
|
2053
|
+
await sendMessageWithRetry(client, input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
|
|
2054
|
+
messageId: `claude-start-failed-${m.id}`,
|
|
2032
2055
|
...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
|
|
2033
2056
|
metadata: {
|
|
2034
2057
|
turnSemantics: 'turn_complete',
|
|
@@ -2205,7 +2228,8 @@ export async function main() {
|
|
|
2205
2228
|
const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
|
|
2206
2229
|
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to create session: ${message}`);
|
|
2207
2230
|
await markQueuedMessageAcceptedForConversation(convoId, m.id ?? null, shouldMarkAccepted);
|
|
2208
|
-
await client
|
|
2231
|
+
await sendMessageWithRetry(client, convoId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
|
|
2232
|
+
messageId: `claude-start-failed-${m.id}`,
|
|
2209
2233
|
...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
|
|
2210
2234
|
metadata: {
|
|
2211
2235
|
turnSemantics: 'turn_complete',
|
|
@@ -2270,6 +2294,11 @@ export async function main() {
|
|
|
2270
2294
|
// ── Control signal watcher ──
|
|
2271
2295
|
const pollControl = async () => {
|
|
2272
2296
|
while (!controlStopped) {
|
|
2297
|
+
const hadActiveWork = [...sessions.values()].some((session) => !session.closed
|
|
2298
|
+
&& (session.state.state === 'running'
|
|
2299
|
+
|| session.state.state === 'requires_action'
|
|
2300
|
+
|| session.pendingInputs.length > 0
|
|
2301
|
+
|| Boolean(session.pendingFinalDelivery)));
|
|
2273
2302
|
for (const convoId of [...sessions.keys()]) {
|
|
2274
2303
|
try {
|
|
2275
2304
|
// Session control signals (model, mode)
|
|
@@ -2365,7 +2394,7 @@ export async function main() {
|
|
|
2365
2394
|
session.pendingFinalText = null;
|
|
2366
2395
|
session.turnState = 'interrupted';
|
|
2367
2396
|
session.clearStreaming().catch(() => { });
|
|
2368
|
-
|
|
2397
|
+
typingSignals.clear(convoId).catch(() => { });
|
|
2369
2398
|
runtimeState.writeTurnState(convoId, {
|
|
2370
2399
|
turnId: session.currentTurnId,
|
|
2371
2400
|
state: 'interrupted',
|
|
@@ -2392,7 +2421,7 @@ export async function main() {
|
|
|
2392
2421
|
// Transient RTDB errors — ignore and retry next cycle
|
|
2393
2422
|
}
|
|
2394
2423
|
}
|
|
2395
|
-
await new Promise((r) => setTimeout(r,
|
|
2424
|
+
await new Promise((r) => setTimeout(r, controlPollDelayMs(hadActiveWork)));
|
|
2396
2425
|
}
|
|
2397
2426
|
};
|
|
2398
2427
|
pollControl().catch((err) => console.error('[canon-host] Control poll crashed:', err));
|
package/dist/mcp-args.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type McpParseResult<T> = {
|
|
|
6
6
|
ok: false;
|
|
7
7
|
error: string;
|
|
8
8
|
};
|
|
9
|
-
type ContentType = 'text' | 'image' | 'audio' | 'file';
|
|
9
|
+
type ContentType = 'text' | 'image' | 'audio' | 'video' | 'file';
|
|
10
10
|
export interface ReplyArgs {
|
|
11
11
|
conversationId: string;
|
|
12
12
|
text: string;
|
|
@@ -18,6 +18,7 @@ export interface SendMessageArgs {
|
|
|
18
18
|
contentType: ContentType;
|
|
19
19
|
imageUrl?: string;
|
|
20
20
|
audioUrl?: string;
|
|
21
|
+
videoUrl?: string;
|
|
21
22
|
fileUrl?: string;
|
|
22
23
|
mediaPath?: string;
|
|
23
24
|
fileName?: string;
|
package/dist/mcp-args.js
CHANGED
|
@@ -31,7 +31,7 @@ function optionalNumber(record, key) {
|
|
|
31
31
|
function parseContentType(value) {
|
|
32
32
|
if (value == null)
|
|
33
33
|
return { ok: true, value: 'text' };
|
|
34
|
-
if (value === 'text' || value === 'image' || value === 'audio' || value === 'file') {
|
|
34
|
+
if (value === 'text' || value === 'image' || value === 'audio' || value === 'video' || value === 'file') {
|
|
35
35
|
return { ok: true, value };
|
|
36
36
|
}
|
|
37
37
|
return { ok: false, error: 'Invalid content_type' };
|
|
@@ -64,6 +64,7 @@ export function parseSendMessageArgs(value) {
|
|
|
64
64
|
return contentType;
|
|
65
65
|
const imageUrl = optionalString(value, 'image_url', 4096);
|
|
66
66
|
const audioUrl = optionalString(value, 'audio_url', 4096);
|
|
67
|
+
const videoUrl = optionalString(value, 'video_url', 4096);
|
|
67
68
|
const fileUrl = optionalString(value, 'file_url', 4096);
|
|
68
69
|
const mediaPath = optionalString(value, 'media_path', 4096);
|
|
69
70
|
const fileName = optionalString(value, 'file_name', 512);
|
|
@@ -77,6 +78,7 @@ export function parseSendMessageArgs(value) {
|
|
|
77
78
|
contentType: contentType.value,
|
|
78
79
|
...(imageUrl ? { imageUrl } : {}),
|
|
79
80
|
...(audioUrl ? { audioUrl } : {}),
|
|
81
|
+
...(videoUrl ? { videoUrl } : {}),
|
|
80
82
|
...(fileUrl ? { fileUrl } : {}),
|
|
81
83
|
...(mediaPath ? { mediaPath } : {}),
|
|
82
84
|
...(fileName ? { fileName } : {}),
|
package/dist/server.js
CHANGED
|
@@ -84,7 +84,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
name: 'send_message',
|
|
87
|
-
description: 'Send a message to a Canon conversation (supports text, images, audio, files)',
|
|
87
|
+
description: 'Send a message to a Canon conversation (supports text, images, audio, video, files)',
|
|
88
88
|
inputSchema: {
|
|
89
89
|
type: 'object',
|
|
90
90
|
properties: {
|
|
@@ -98,7 +98,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
98
98
|
},
|
|
99
99
|
content_type: {
|
|
100
100
|
type: 'string',
|
|
101
|
-
enum: ['text', 'image', 'audio', 'file'],
|
|
101
|
+
enum: ['text', 'image', 'audio', 'video', 'file'],
|
|
102
102
|
description: 'Content type (default: text)',
|
|
103
103
|
},
|
|
104
104
|
image_url: {
|
|
@@ -109,13 +109,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
109
109
|
type: 'string',
|
|
110
110
|
description: 'Audio URL (when content_type is audio)',
|
|
111
111
|
},
|
|
112
|
+
video_url: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Video URL (when content_type is video)',
|
|
115
|
+
},
|
|
112
116
|
file_url: {
|
|
113
117
|
type: 'string',
|
|
114
118
|
description: 'File URL (when content_type is file)',
|
|
115
119
|
},
|
|
116
120
|
media_path: {
|
|
117
121
|
type: 'string',
|
|
118
|
-
description: 'Local image/audio/file path to upload and send',
|
|
122
|
+
description: 'Local image/audio/video/file path to upload and send',
|
|
119
123
|
},
|
|
120
124
|
file_name: {
|
|
121
125
|
type: 'string',
|
|
@@ -244,7 +248,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
244
248
|
const parsed = parseSendMessageArgs(args);
|
|
245
249
|
if (!parsed.ok)
|
|
246
250
|
return toolArgumentError(parsed.error);
|
|
247
|
-
const { conversationId, text, contentType, imageUrl, audioUrl, fileUrl, mediaPath, fileName, mimeType, durationMs, } = parsed.value;
|
|
251
|
+
const { conversationId, text, contentType, imageUrl, audioUrl, videoUrl, fileUrl, mediaPath, fileName, mimeType, durationMs, } = parsed.value;
|
|
248
252
|
const opts = {};
|
|
249
253
|
const metadata = TURN_COMPLETE_METADATA;
|
|
250
254
|
if (mediaPath) {
|
|
@@ -276,6 +280,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
276
280
|
...(mimeType ? { mimeType } : {}),
|
|
277
281
|
}];
|
|
278
282
|
}
|
|
283
|
+
else if (contentType === 'video' && videoUrl) {
|
|
284
|
+
opts.contentType = 'video';
|
|
285
|
+
opts.attachments = [{
|
|
286
|
+
kind: 'video',
|
|
287
|
+
url: videoUrl,
|
|
288
|
+
...(mimeType ? { mimeType } : {}),
|
|
289
|
+
...(durationMs != null ? { durationMs } : {}),
|
|
290
|
+
}];
|
|
291
|
+
}
|
|
279
292
|
else if (contentType === 'file' || fileUrl) {
|
|
280
293
|
opts.contentType = 'file';
|
|
281
294
|
if (fileUrl) {
|
package/dist/session-state.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.17",
|
|
4
4
|
"description": "Canon channel plugin for Claude Code — messaging where AI agents are first-class citizens",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "^0.2.141",
|
|
34
|
-
"@canonmsg/agent-sdk": "^2.
|
|
35
|
-
"@canonmsg/core": "^1.
|
|
34
|
+
"@canonmsg/agent-sdk": "^2.3.0",
|
|
35
|
+
"@canonmsg/core": "^1.6.0",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|