@canonmsg/claude-code-plugin 0.14.11 → 0.14.13
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 +51 -115
- package/dist/server.js +0 -2
- package/package.json +3 -3
package/dist/host.js
CHANGED
|
@@ -26,9 +26,8 @@ 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_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, 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_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';
|
|
30
30
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
31
|
-
import { createStreamingBuffer } from './streaming-buffer.js';
|
|
32
31
|
import { runCli } from './cli-entry.js';
|
|
33
32
|
import { decideClaudeToolPermissionForMode, parseAllowedNonOwnerClaudeTools, } from './tool-policy.js';
|
|
34
33
|
import { buildClaudeFinalMessageId, createClaudeInputEnvelope, formatClaudeControlError, mergeClaudeModelOptions, resetClaudeCompletedTurnState, shouldDeliverClaudeFinal, } from './session-state.js';
|
|
@@ -183,6 +182,8 @@ function buildClaudeRuntimeDescriptor(input) {
|
|
|
183
182
|
...RUNTIME_NEW_SESSION_ACTION,
|
|
184
183
|
primitive: 'session.new',
|
|
185
184
|
},
|
|
185
|
+
RUNTIME_STOP_ACTION,
|
|
186
|
+
RUNTIME_STOP_AND_DROP_ACTION,
|
|
186
187
|
];
|
|
187
188
|
return buildFirstPartyCodingRuntimeDescriptor({
|
|
188
189
|
clientType: 'claude-code',
|
|
@@ -196,86 +197,6 @@ function buildClaudeRuntimeDescriptor(input) {
|
|
|
196
197
|
admissionActions: HOST_ADMISSION_ACTIONS_DISABLED,
|
|
197
198
|
presentation: input.presentation,
|
|
198
199
|
commands,
|
|
199
|
-
actions: [
|
|
200
|
-
{
|
|
201
|
-
id: 'plan',
|
|
202
|
-
label: 'Plan first',
|
|
203
|
-
description: 'Switch Claude into Plan mode. Text after /plan is kept as a ready prompt while the mode applies.',
|
|
204
|
-
aliases: ['plan'],
|
|
205
|
-
category: 'plan',
|
|
206
|
-
placements: ['composer_slash', 'command_palette'],
|
|
207
|
-
availability: ['always'],
|
|
208
|
-
trailingTextBehavior: 'send_as_prompt',
|
|
209
|
-
dispatch: {
|
|
210
|
-
kind: 'control',
|
|
211
|
-
controlId: 'permissionMode',
|
|
212
|
-
value: 'plan',
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
id: 'runtime-status',
|
|
217
|
-
label: 'Runtime status',
|
|
218
|
-
description: 'Open Claude runtime details, including account, project, and policy status.',
|
|
219
|
-
aliases: ['status'],
|
|
220
|
-
category: 'details',
|
|
221
|
-
placements: ['composer_slash', 'command_palette'],
|
|
222
|
-
availability: ['always'],
|
|
223
|
-
dispatch: { kind: 'open_details', target: 'status' },
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
id: 'mcp-inventory',
|
|
227
|
-
label: 'MCP servers',
|
|
228
|
-
description: 'Open the Claude MCP inventory published by the host.',
|
|
229
|
-
aliases: ['mcp'],
|
|
230
|
-
category: 'details',
|
|
231
|
-
placements: ['composer_slash', 'command_palette'],
|
|
232
|
-
availability: ['always'],
|
|
233
|
-
dispatch: { kind: 'open_details', target: 'mcp' },
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
id: 'plugin-inventory',
|
|
237
|
-
label: 'Plugins',
|
|
238
|
-
description: 'Open the Claude plugin and command inventory published by the host.',
|
|
239
|
-
aliases: ['plugins'],
|
|
240
|
-
category: 'details',
|
|
241
|
-
placements: ['composer_slash', 'command_palette'],
|
|
242
|
-
availability: ['always'],
|
|
243
|
-
dispatch: { kind: 'open_details', target: 'plugins' },
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
id: 'model-details',
|
|
247
|
-
label: 'Model control',
|
|
248
|
-
description: 'Open runtime details and use the live Model control.',
|
|
249
|
-
aliases: ['model'],
|
|
250
|
-
category: 'details',
|
|
251
|
-
placements: ['composer_slash', 'command_palette'],
|
|
252
|
-
availability: ['always'],
|
|
253
|
-
dispatch: { kind: 'open_details', target: 'model' },
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
id: 'permission-details',
|
|
257
|
-
label: 'Permission mode',
|
|
258
|
-
description: 'Open runtime details and use the live Permission mode control.',
|
|
259
|
-
aliases: ['permission'],
|
|
260
|
-
category: 'details',
|
|
261
|
-
placements: ['composer_slash', 'command_palette'],
|
|
262
|
-
availability: ['always'],
|
|
263
|
-
dispatch: { kind: 'open_details', target: 'permissionMode' },
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
id: 'effort-details',
|
|
267
|
-
label: 'Thinking level',
|
|
268
|
-
description: 'Open runtime details and use the live Thinking level control.',
|
|
269
|
-
aliases: ['effort'],
|
|
270
|
-
category: 'details',
|
|
271
|
-
placements: ['composer_slash', 'command_palette'],
|
|
272
|
-
availability: ['always'],
|
|
273
|
-
dispatch: { kind: 'open_details', target: 'effort' },
|
|
274
|
-
},
|
|
275
|
-
RUNTIME_STOP_ACTION,
|
|
276
|
-
RUNTIME_STOP_AND_DROP_ACTION,
|
|
277
|
-
RUNTIME_NEW_SESSION_ACTION,
|
|
278
|
-
],
|
|
279
200
|
});
|
|
280
201
|
}
|
|
281
202
|
function parseClaudePermissionMode(value) {
|
|
@@ -923,40 +844,50 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
923
844
|
pendingReply: null,
|
|
924
845
|
writeState: () => writeState(),
|
|
925
846
|
writeTurn: () => writeTurn(),
|
|
847
|
+
clearStreaming: () => clearStreaming(),
|
|
926
848
|
setRuntimeControlError: (controlId, value, error) => setRuntimeControlError(controlId, value, error),
|
|
927
849
|
clearRuntimeControlError: (controlId) => clearRuntimeControlError(controlId),
|
|
928
850
|
};
|
|
929
|
-
const
|
|
851
|
+
const streamingOutput = createTurnOutputController({
|
|
852
|
+
turnId: `claude-${conversationId}`,
|
|
853
|
+
mode: 'delta',
|
|
930
854
|
throttleMs: STREAMING_THROTTLE_MS,
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
855
|
+
writeSnapshot: (snapshot) => {
|
|
856
|
+
if (session.closed)
|
|
857
|
+
return Promise.resolve();
|
|
858
|
+
const turnId = session.currentTurnId ?? snapshot.turnId;
|
|
859
|
+
session.streamingText = snapshot.text;
|
|
860
|
+
const blocks = snapshot.blocks?.map((block) => ({ ...block, turnId }));
|
|
861
|
+
return runtimeState.writeStreaming(conversationId, {
|
|
862
|
+
text: snapshot.text,
|
|
863
|
+
status: snapshot.status,
|
|
864
|
+
messageId: turnId,
|
|
865
|
+
turnId,
|
|
866
|
+
blocks,
|
|
867
|
+
});
|
|
938
868
|
},
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
status: 'streaming',
|
|
943
|
-
}).catch(() => { });
|
|
869
|
+
clearSnapshot: () => {
|
|
870
|
+
session.streamingText = '';
|
|
871
|
+
return runtimeState.clearStreaming(conversationId);
|
|
944
872
|
},
|
|
945
873
|
});
|
|
946
874
|
// ── Per-session streaming helpers ──
|
|
947
875
|
function flushStreaming() {
|
|
948
|
-
|
|
876
|
+
streamingOutput.flush().catch(() => { });
|
|
949
877
|
}
|
|
950
878
|
function onStreamDelta(text) {
|
|
951
|
-
|
|
879
|
+
streamingOutput.appendDelta(text);
|
|
952
880
|
}
|
|
953
881
|
function isActiveTurnInterrupted() {
|
|
954
882
|
const turnKey = session.activeInput?.turnKey;
|
|
955
883
|
return Boolean(turnKey && session.interruptedTurnKeys.has(turnKey));
|
|
956
884
|
}
|
|
957
885
|
function clearStreaming() {
|
|
958
|
-
|
|
959
|
-
|
|
886
|
+
return streamingOutput.clear().catch(() => { });
|
|
887
|
+
}
|
|
888
|
+
function getFinalTurnTrail() {
|
|
889
|
+
const turnId = session.currentTurnId ?? `claude-${conversationId}`;
|
|
890
|
+
return streamingOutput.getFinalTrail().map((block) => ({ ...block, turnId }));
|
|
960
891
|
}
|
|
961
892
|
function clearIdleResetTimer() {
|
|
962
893
|
if (!session.idleResetTimer)
|
|
@@ -1004,7 +935,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1004
935
|
session.state.state = 'running';
|
|
1005
936
|
session.turnState = 'streaming';
|
|
1006
937
|
stopVisibleWorkSignal();
|
|
1007
|
-
|
|
938
|
+
streamingOutput.replaceSnapshot(finalText, 'streaming').catch(() => { });
|
|
1008
939
|
writeState();
|
|
1009
940
|
writeTurn();
|
|
1010
941
|
client.setTyping(conversationId, true, 'typing').catch(() => { });
|
|
@@ -1036,8 +967,10 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1036
967
|
turnKey: deliverTurn.turnKey,
|
|
1037
968
|
sourceMessageId: deliverTurn.sourceMessageId ?? undefined,
|
|
1038
969
|
turnSemantics: 'turn_complete',
|
|
1039
|
-
turnComplete: true,
|
|
1040
970
|
deliveryIntent: session.lastAcceptedIntent ?? undefined,
|
|
971
|
+
...(getFinalTurnTrail().length > 0
|
|
972
|
+
? { turnTrail: getFinalTurnTrail() }
|
|
973
|
+
: {}),
|
|
1041
974
|
},
|
|
1042
975
|
});
|
|
1043
976
|
session.finalizedTurnKeys.add(deliverTurn.turnKey);
|
|
@@ -1218,6 +1151,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1218
1151
|
session.pendingInputs.unshift(input);
|
|
1219
1152
|
session.turnState = 'interrupted';
|
|
1220
1153
|
stopVisibleWorkSignal();
|
|
1154
|
+
clearStreaming().catch(() => { });
|
|
1155
|
+
client.setTyping(conversationId, false).catch(() => { });
|
|
1221
1156
|
writeTurn();
|
|
1222
1157
|
void session.query.interrupt().catch(() => { });
|
|
1223
1158
|
return;
|
|
@@ -1263,11 +1198,20 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
|
|
|
1263
1198
|
if (event?.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
1264
1199
|
session.toolInProgress = true;
|
|
1265
1200
|
session.turnState = 'tool';
|
|
1201
|
+
streamingOutput.addBlock({
|
|
1202
|
+
id: `tool:${session.currentTurnId ?? conversationId}`,
|
|
1203
|
+
kind: 'tool',
|
|
1204
|
+
status: 'running',
|
|
1205
|
+
title: typeof event.content_block?.name === 'string'
|
|
1206
|
+
? event.content_block.name
|
|
1207
|
+
: 'Claude tool use',
|
|
1208
|
+
}).catch(() => { });
|
|
1266
1209
|
startVisibleWorkSignal();
|
|
1267
1210
|
writeTurn();
|
|
1268
1211
|
}
|
|
1269
1212
|
else if (event?.type === 'content_block_stop' && session.toolInProgress) {
|
|
1270
1213
|
session.toolInProgress = false;
|
|
1214
|
+
streamingOutput.completeBlock(`tool:${session.currentTurnId ?? conversationId}`).catch(() => { });
|
|
1271
1215
|
if (session.state.state === 'running') {
|
|
1272
1216
|
session.turnState = 'thinking';
|
|
1273
1217
|
startVisibleWorkSignal();
|
|
@@ -1851,10 +1795,7 @@ export async function main() {
|
|
|
1851
1795
|
});
|
|
1852
1796
|
}
|
|
1853
1797
|
}
|
|
1854
|
-
|
|
1855
|
-
clearTimeout(session.streamingTimer);
|
|
1856
|
-
session.streamingText = '';
|
|
1857
|
-
runtimeState.clearStreaming(conversationId).catch(() => { });
|
|
1798
|
+
session.clearStreaming().catch(() => { });
|
|
1858
1799
|
if (session.idleResetTimer) {
|
|
1859
1800
|
clearTimeout(session.idleResetTimer);
|
|
1860
1801
|
session.idleResetTimer = null;
|
|
@@ -1883,6 +1824,8 @@ export async function main() {
|
|
|
1883
1824
|
session.pendingFinalText = null;
|
|
1884
1825
|
session.pendingFinalDelivery = null;
|
|
1885
1826
|
session.turnState = 'interrupted';
|
|
1827
|
+
session.clearStreaming().catch(() => { });
|
|
1828
|
+
client.setTyping(conversationId, false).catch(() => { });
|
|
1886
1829
|
if (session.state.state === 'running') {
|
|
1887
1830
|
await session.query.interrupt().catch(() => { });
|
|
1888
1831
|
}
|
|
@@ -1972,6 +1915,7 @@ export async function main() {
|
|
|
1972
1915
|
});
|
|
1973
1916
|
}
|
|
1974
1917
|
}
|
|
1918
|
+
existing?.clearStreaming().catch(() => { });
|
|
1975
1919
|
sessions.delete(convoId);
|
|
1976
1920
|
runtimeState.clearSessionState(convoId).catch(() => { });
|
|
1977
1921
|
runtimeState.clearTurnState(convoId).catch(() => { });
|
|
@@ -2091,7 +2035,6 @@ export async function main() {
|
|
|
2091
2035
|
...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
|
|
2092
2036
|
metadata: {
|
|
2093
2037
|
turnSemantics: 'turn_complete',
|
|
2094
|
-
turnComplete: true,
|
|
2095
2038
|
replyBehavior: 'suppress_auto_reply',
|
|
2096
2039
|
},
|
|
2097
2040
|
}).catch(() => { });
|
|
@@ -2170,7 +2113,6 @@ export async function main() {
|
|
|
2170
2113
|
if (m.metadata?.type === 'approval_reply' || (approvalManager?.pendingCount ?? 0) > 0) {
|
|
2171
2114
|
const consumed = approvalManager?.handleMessage(convoId, {
|
|
2172
2115
|
senderId: m.senderId,
|
|
2173
|
-
text: m.text ?? undefined,
|
|
2174
2116
|
metadata: m.metadata,
|
|
2175
2117
|
}) ?? false;
|
|
2176
2118
|
if (m.metadata?.type === 'approval_reply') {
|
|
@@ -2270,7 +2212,6 @@ export async function main() {
|
|
|
2270
2212
|
...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
|
|
2271
2213
|
metadata: {
|
|
2272
2214
|
turnSemantics: 'turn_complete',
|
|
2273
|
-
turnComplete: true,
|
|
2274
2215
|
replyBehavior: 'suppress_auto_reply',
|
|
2275
2216
|
},
|
|
2276
2217
|
}).catch(() => { });
|
|
@@ -2426,6 +2367,8 @@ export async function main() {
|
|
|
2426
2367
|
}
|
|
2427
2368
|
session.pendingFinalText = null;
|
|
2428
2369
|
session.turnState = 'interrupted';
|
|
2370
|
+
session.clearStreaming().catch(() => { });
|
|
2371
|
+
client.setTyping(convoId, false).catch(() => { });
|
|
2429
2372
|
runtimeState.writeTurnState(convoId, {
|
|
2430
2373
|
turnId: session.currentTurnId,
|
|
2431
2374
|
state: 'interrupted',
|
|
@@ -2439,13 +2382,6 @@ export async function main() {
|
|
|
2439
2382
|
if (isActive) {
|
|
2440
2383
|
await session.query.interrupt();
|
|
2441
2384
|
}
|
|
2442
|
-
if (session.streamingTimer) {
|
|
2443
|
-
clearTimeout(session.streamingTimer);
|
|
2444
|
-
session.streamingTimer = null;
|
|
2445
|
-
}
|
|
2446
|
-
session.streamingText = '';
|
|
2447
|
-
runtimeState.clearStreaming(convoId).catch(() => { });
|
|
2448
|
-
client.setTyping(convoId, false).catch(() => { });
|
|
2449
2385
|
await rtdbWrite(`/control/${convoId}/${agentId}/signal`, null).catch(() => { });
|
|
2450
2386
|
}
|
|
2451
2387
|
catch (err) {
|
package/dist/server.js
CHANGED
|
@@ -41,7 +41,6 @@ let approvalServer = null;
|
|
|
41
41
|
const conversationCache = new Map();
|
|
42
42
|
const TURN_COMPLETE_METADATA = {
|
|
43
43
|
turnSemantics: 'turn_complete',
|
|
44
|
-
turnComplete: true,
|
|
45
44
|
};
|
|
46
45
|
/** Last owner-visible conversation to route approval cards into. */
|
|
47
46
|
let approvalConversationId = null;
|
|
@@ -419,7 +418,6 @@ async function handleInboundMessage(payload) {
|
|
|
419
418
|
if (approvalManager) {
|
|
420
419
|
const consumed = approvalManager.handleMessage(payload.conversationId, {
|
|
421
420
|
senderId: m.senderId,
|
|
422
|
-
text: m.text ?? undefined,
|
|
423
421
|
metadata: m.metadata,
|
|
424
422
|
});
|
|
425
423
|
if (consumed) {
|
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.13",
|
|
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.101",
|
|
34
|
-
"@canonmsg/agent-sdk": "^
|
|
35
|
-
"@canonmsg/core": "^0.
|
|
34
|
+
"@canonmsg/agent-sdk": "^2.0.0",
|
|
35
|
+
"@canonmsg/core": "^1.0.0",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|