@canonmsg/claude-code-plugin 0.34.5 → 0.35.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/.claude-plugin/plugin.json +1 -1
- package/dist/host.js +45 -12
- package/dist/session-state.d.ts +5 -0
- package/dist/session-state.js +5 -1
- package/package.json +5 -5
package/dist/host.js
CHANGED
|
@@ -30,7 +30,7 @@ import { materializeMessageMedia, materializeReplyContextMedia, sendMediaFileMes
|
|
|
30
30
|
import { captureTurnArtifactSnapshot, createTurnArtifactRouter, IDLE_TIMEOUT_MS, collectMissedInboundMessages, createRecoveryCheckpointTracker, createReconnectRecoveryCoordinator, STARTUP_RECOVERY_PAGE_SIZE, } from '@canonmsg/coding-agent-host';
|
|
31
31
|
import { buildCanonUserContent, renderClaudeInboundContent, withClaudeReplyContextMediaReferences, } from './canon-user-content.js';
|
|
32
32
|
import { buildClaudeNativeCommandInput, createClaudeCommandContextHook, getClaudeNativeCommandOutput, } from './native-command-input.js';
|
|
33
|
-
import { EFFORT_OPTIONS, CLAUDE_PERMISSION_MODE_OPTIONS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonInboundFrameV1, buildCanonTurnContextV2, buildConfiguredWorkspaceOptionsWithRoots, buildConversationEnvironmentKey, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, normalizeRuntimeCommandDescriptors, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createTurnOutputController, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, ControlChannelPoller, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, ApprovalManager, RuntimeRequestManager, FINAL_MESSAGE_HANDOFF_MS, buildLocalRuntimeId, getActiveProfileLock, heartbeatLocalRuntimeEntry, normalizeTurnMetadata, parseTurnVerbosityConfig, prepareConversationEnvironment, resolveCanonAgent, verifyResolvedAgentEnvironment, decideAutoReply, initRTDBAuth, isChunkedSendMessageError, sendMessageWithRetry, sendMessageWithRetryChunked, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, resolveLocalRuntimeSessionState, saveRuntimeSessionState, clearRuntimeSessionState, publishHostSessionSnapshots, renderCodingHostInboundPrompt, resolveConfiguredWorkspaceCwd, resolveTurnVerbosity, shouldTriggerAgentTurn, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
33
|
+
import { EFFORT_OPTIONS, CLAUDE_PERMISSION_MODE_OPTIONS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonInboundFrameV1, buildCanonGroupContext, buildCompactGroupContextLines, buildCanonTurnContextV2, buildConfiguredWorkspaceOptionsWithRoots, buildConversationEnvironmentKey, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, normalizeRuntimeCommandDescriptors, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createTurnOutputController, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, ControlChannelPoller, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, ApprovalManager, RuntimeRequestManager, FINAL_MESSAGE_HANDOFF_MS, buildLocalRuntimeId, getActiveProfileLock, heartbeatLocalRuntimeEntry, normalizeTurnMetadata, parseTurnVerbosityConfig, prepareConversationEnvironment, resolveCanonAgent, verifyResolvedAgentEnvironment, decideAutoReply, initRTDBAuth, isChunkedSendMessageError, sendMessageWithRetry, sendMessageWithRetryChunked, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, resolveLocalRuntimeSessionState, saveRuntimeSessionState, clearRuntimeSessionState, publishHostSessionSnapshots, renderCodingHostInboundPrompt, resolveConfiguredWorkspaceCwd, resolveTurnVerbosity, shouldTriggerAgentTurn, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
34
34
|
import { runCli } from '@canonmsg/core';
|
|
35
35
|
import { CANON_VERB_MCP_SERVER_NAME, createCanonVerbMcpServer } from '@canonmsg/agent-tools';
|
|
36
36
|
import { synthesizeClaudeApprovalDiff } from './approval-diff.js';
|
|
@@ -615,6 +615,22 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
615
615
|
// Only Canon turns may own the active-input slot. Result ownership comes from
|
|
616
616
|
// UUID correlation, while this slot drives live tool/dialog state.
|
|
617
617
|
function bindActiveInput(input) {
|
|
618
|
+
if (claudeInputOwnsTurnSlot(input)) {
|
|
619
|
+
const roster = config.currentParticipantContext?.();
|
|
620
|
+
if (roster && input.commandContext) {
|
|
621
|
+
// Native slash-command arguments must stay byte-for-byte intact. Their
|
|
622
|
+
// context travels through UserPromptExpansion rather than the command.
|
|
623
|
+
input.commandContext = `${input.commandContext}\n\n${roster}`;
|
|
624
|
+
}
|
|
625
|
+
else if (roster) {
|
|
626
|
+
const content = input.msg.message.content;
|
|
627
|
+
input.msg = { ...input.msg, message: { ...input.msg.message,
|
|
628
|
+
content: typeof content === 'string'
|
|
629
|
+
? `${content}\n\n${roster}`
|
|
630
|
+
: [...content, { type: 'text', text: roster }],
|
|
631
|
+
} };
|
|
632
|
+
}
|
|
633
|
+
}
|
|
618
634
|
rememberDispatchedClaudeInput(dispatchedInputs, input);
|
|
619
635
|
// The slot drives in-turn concerns (tool routing, dialogs, interrupts), so
|
|
620
636
|
// it tracks the newest Canon turn. Result ownership is resolved by uuid, not
|
|
@@ -1359,8 +1375,9 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
1359
1375
|
return false;
|
|
1360
1376
|
}
|
|
1361
1377
|
}
|
|
1362
|
-
function sendTurnArtifactFile(file) {
|
|
1378
|
+
function sendTurnArtifactFile(file, canPublish) {
|
|
1363
1379
|
return sendMediaFileMessage(client, conversationId, file.path, '', {
|
|
1380
|
+
canPublish,
|
|
1364
1381
|
...(session.activeInput?.replyAuthority
|
|
1365
1382
|
? { replyAuthority: session.activeInput.replyAuthority }
|
|
1366
1383
|
: {}),
|
|
@@ -1382,16 +1399,18 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
1382
1399
|
* `composeClaudeTurnFinal` makes for the text.
|
|
1383
1400
|
*/
|
|
1384
1401
|
function routeArtifactsForInput(input, finalText) {
|
|
1402
|
+
const decideArtifactRouting = () => shouldRouteClaudeTurnArtifacts({
|
|
1403
|
+
turn: input,
|
|
1404
|
+
interruptedTurnKeys: session.interruptedTurnKeys,
|
|
1405
|
+
silencedTurnKeys: session.silencedTurnKeys,
|
|
1406
|
+
finalText,
|
|
1407
|
+
currentAudience: config.currentArtifactAudience?.() ?? { agentId, ownerId: config.ownerId },
|
|
1408
|
+
});
|
|
1385
1409
|
return createTurnArtifactRouter({
|
|
1386
|
-
decide:
|
|
1387
|
-
turn: input,
|
|
1388
|
-
interruptedTurnKeys: session.interruptedTurnKeys,
|
|
1389
|
-
silencedTurnKeys: session.silencedTurnKeys,
|
|
1390
|
-
finalText,
|
|
1391
|
-
}),
|
|
1410
|
+
decide: decideArtifactRouting,
|
|
1392
1411
|
baseline: () => input.artifactBaseline,
|
|
1393
1412
|
cwd: () => session.cwd,
|
|
1394
|
-
send: (file) => sendTurnArtifactFile(file),
|
|
1413
|
+
send: (file) => sendTurnArtifactFile(file, () => decideArtifactRouting().route),
|
|
1395
1414
|
log: (line) => console.error(`[canon-host] [${conversationId.slice(0, 8)}] ${line}`),
|
|
1396
1415
|
}).route();
|
|
1397
1416
|
}
|
|
@@ -2532,6 +2551,7 @@ export async function main() {
|
|
|
2532
2551
|
if (cached) {
|
|
2533
2552
|
conversationCache.set(payload.conversationId, {
|
|
2534
2553
|
...cached,
|
|
2554
|
+
...payload.changes,
|
|
2535
2555
|
memberIds,
|
|
2536
2556
|
});
|
|
2537
2557
|
}
|
|
@@ -2540,11 +2560,12 @@ export async function main() {
|
|
|
2540
2560
|
}
|
|
2541
2561
|
if (!memberIds.includes(agentId)) {
|
|
2542
2562
|
knownConversationIds.delete(payload.conversationId);
|
|
2543
|
-
|
|
2563
|
+
pendingMembershipChanges.delete(payload.conversationId);
|
|
2564
|
+
closeSession(payload.conversationId);
|
|
2544
2565
|
}
|
|
2545
2566
|
}
|
|
2546
2567
|
function getGroupContextMode(conversationId, conversation) {
|
|
2547
|
-
if (conversation
|
|
2568
|
+
if (!conversation)
|
|
2548
2569
|
return undefined;
|
|
2549
2570
|
if (pendingMembershipChanges.has(conversationId))
|
|
2550
2571
|
return 'membership_change';
|
|
@@ -2562,7 +2583,7 @@ export async function main() {
|
|
|
2562
2583
|
}
|
|
2563
2584
|
async function loadHydratedInboundContext(input) {
|
|
2564
2585
|
const [conversation, page] = await Promise.all([
|
|
2565
|
-
getConversationMeta(input.conversationId),
|
|
2586
|
+
getConversationMeta(input.conversationId, { refreshIfMemberMissing: agentId }),
|
|
2566
2587
|
input.hydratedPage
|
|
2567
2588
|
? Promise.resolve(input.hydratedPage)
|
|
2568
2589
|
: client.getMessagesPage(input.conversationId, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT).catch(() => null),
|
|
@@ -2571,6 +2592,7 @@ export async function main() {
|
|
|
2571
2592
|
agentId,
|
|
2572
2593
|
conversationId: input.conversationId,
|
|
2573
2594
|
conversation,
|
|
2595
|
+
requireAgentMembership: true,
|
|
2574
2596
|
page,
|
|
2575
2597
|
activeSelfContextId: input.activeSelfContextId,
|
|
2576
2598
|
selfContexts: input.selfContexts,
|
|
@@ -2704,6 +2726,10 @@ export async function main() {
|
|
|
2704
2726
|
}
|
|
2705
2727
|
}
|
|
2706
2728
|
async function getOrCreateSession(conversationId) {
|
|
2729
|
+
const current = conversationCache.get(conversationId);
|
|
2730
|
+
if (current && !current.memberIds.includes(agentId)) {
|
|
2731
|
+
throw new Error('Agent is no longer a member of this conversation');
|
|
2732
|
+
}
|
|
2707
2733
|
knownConversationIds.add(conversationId);
|
|
2708
2734
|
const existing = sessions.get(conversationId);
|
|
2709
2735
|
if (existing && !existing.closed) {
|
|
@@ -2813,6 +2839,13 @@ export async function main() {
|
|
|
2813
2839
|
runtimeId,
|
|
2814
2840
|
ownerId,
|
|
2815
2841
|
communicationEnabled,
|
|
2842
|
+
currentParticipantContext: () => {
|
|
2843
|
+
const roster = buildCanonGroupContext({
|
|
2844
|
+
conversation: conversationCache.get(conversationId), messages: [], agentId, ownerId, ownerName,
|
|
2845
|
+
});
|
|
2846
|
+
return roster ? buildCompactGroupContextLines(roster, 'initial').join('\n') : '';
|
|
2847
|
+
},
|
|
2848
|
+
currentArtifactAudience: () => ({ memberIds: conversationCache.get(conversationId)?.memberIds, agentId, ownerId }),
|
|
2816
2849
|
onInputCompleted: (sourceMessageId) => {
|
|
2817
2850
|
recoveryCheckpointsFor(conversationId).settle(sourceMessageId);
|
|
2818
2851
|
settleInboundMessageId(sourceMessageId, true);
|
package/dist/session-state.d.ts
CHANGED
|
@@ -142,6 +142,11 @@ export declare function shouldRouteClaudeTurnArtifacts(input: {
|
|
|
142
142
|
interruptedTurnKeys: ReadonlySet<string>;
|
|
143
143
|
silencedTurnKeys: ReadonlySet<string>;
|
|
144
144
|
finalText: string | null | undefined;
|
|
145
|
+
currentAudience?: {
|
|
146
|
+
memberIds?: readonly string[];
|
|
147
|
+
agentId: string;
|
|
148
|
+
ownerId: string | null | undefined;
|
|
149
|
+
};
|
|
145
150
|
}): TurnArtifactRoutingDecision;
|
|
146
151
|
export declare function rememberDispatchedClaudeInput(dispatched: Map<string, ClaudeInputEnvelope>, input: ClaudeInputEnvelope): void;
|
|
147
152
|
/**
|
package/dist/session-state.js
CHANGED
|
@@ -74,8 +74,12 @@ export function shouldDeliverClaudeFinal(input) {
|
|
|
74
74
|
* `DEFAULT_SILENT_TURN_PRECEDENCE` moves a turn's text and its files together.
|
|
75
75
|
*/
|
|
76
76
|
export function shouldRouteClaudeTurnArtifacts(input) {
|
|
77
|
+
const audience = input.currentAudience;
|
|
78
|
+
const ownerPair = !audience || (audience.ownerId !== audience.agentId
|
|
79
|
+
&& Boolean(audience.ownerId) && audience.memberIds?.length === 2
|
|
80
|
+
&& audience.memberIds.includes(audience.agentId) && audience.memberIds.includes(audience.ownerId));
|
|
77
81
|
return resolveTurnArtifactRouting({
|
|
78
|
-
artifactRoutingMode: input.turn.artifactRoutingMode,
|
|
82
|
+
artifactRoutingMode: ownerPair ? input.turn.artifactRoutingMode : 'disabled',
|
|
79
83
|
interrupted: input.interruptedTurnKeys.has(input.turn.turnKey),
|
|
80
84
|
silenced: isSilentTurnSuppressed({
|
|
81
85
|
silenced: input.silencedTurnKeys.has(input.turn.turnKey),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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,10 +31,10 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "0.3.228",
|
|
34
|
-
"@canonmsg/agent-sdk": "^10.
|
|
35
|
-
"@canonmsg/agent-tools": "^0.
|
|
36
|
-
"@canonmsg/coding-agent-host": "^0.
|
|
37
|
-
"@canonmsg/core": "^12.
|
|
34
|
+
"@canonmsg/agent-sdk": "^10.4.0",
|
|
35
|
+
"@canonmsg/agent-tools": "^0.10.0",
|
|
36
|
+
"@canonmsg/coding-agent-host": "^0.8.0",
|
|
37
|
+
"@canonmsg/core": "^12.5.0",
|
|
38
38
|
"@canonmsg/rich-cards": "^0.10.5",
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
40
40
|
},
|