@canonmsg/claude-code-plugin 0.24.0 → 0.25.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/dist/host.js +31 -46
- package/package.json +3 -3
package/dist/host.js
CHANGED
|
@@ -28,7 +28,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
28
28
|
import { query, } from '@anthropic-ai/claude-agent-sdk';
|
|
29
29
|
import { isAnthropicImageAttachment, materializeMessageMedia, materializeReplyContextMedia, sendMediaFileMessage, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
|
|
30
30
|
import { captureTurnArtifactSnapshot, collectTurnArtifacts, } from '@canonmsg/coding-agent-host';
|
|
31
|
-
import { CLAUDE_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,
|
|
31
|
+
import { CLAUDE_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, resolveQuestionAllowOther, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, normalizeRuntimeCommandDescriptors, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, ControlChannelPoller, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, ApprovalManager, RuntimeRequestManager, ExecutionEnvironmentError, FINAL_MESSAGE_HANDOFF_MS, buildLocalRuntimeId, getActiveProfileLock, heartbeatLocalRuntimeEntry, normalizeTurnMetadata, prepareConversationEnvironment, resolveCanonAgent, initRTDBAuth, sendMessageWithRetry, loadHostSessionConfig, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, saveRuntimeSessionState, clearRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
32
32
|
import { decideAutoReply, } from './inbound-policy.js';
|
|
33
33
|
import { runCli } from './cli-entry.js';
|
|
34
34
|
import { synthesizeClaudeApprovalDiff } from './approval-diff.js';
|
|
@@ -769,7 +769,6 @@ async function buildCanonUserContent(input) {
|
|
|
769
769
|
function createSession(conversationId, environment, agentId, client, typingSignals, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
|
|
770
770
|
const { cwd } = environment;
|
|
771
771
|
const cardResponseUserId = config.ownerId ?? undefined;
|
|
772
|
-
const cardResponseUser = cardResponseUserId ? { responseUserId: cardResponseUserId } : undefined;
|
|
773
772
|
// Per-session input stream with message queue
|
|
774
773
|
let resolveInput = null;
|
|
775
774
|
let inputDone = false;
|
|
@@ -844,30 +843,6 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
844
843
|
};
|
|
845
844
|
});
|
|
846
845
|
}
|
|
847
|
-
async function waitForRuntimeInputResponse(input) {
|
|
848
|
-
session.lastActivity = Date.now();
|
|
849
|
-
while (Date.now() < input.expiresAt) {
|
|
850
|
-
const response = await client.consumeRuntimeInputResponse({
|
|
851
|
-
conversationId,
|
|
852
|
-
inputId: input.inputId,
|
|
853
|
-
}).catch(() => null);
|
|
854
|
-
if (response?.status === 'submitted') {
|
|
855
|
-
return { status: 'submitted', answers: response.answers, value: response.value };
|
|
856
|
-
}
|
|
857
|
-
if (response?.status === 'cancelled' || response?.status === 'timeout') {
|
|
858
|
-
return { status: response.status };
|
|
859
|
-
}
|
|
860
|
-
await sleep(1_000);
|
|
861
|
-
}
|
|
862
|
-
const response = await client.consumeRuntimeInputResponse({
|
|
863
|
-
conversationId,
|
|
864
|
-
inputId: input.inputId,
|
|
865
|
-
}).catch(() => null);
|
|
866
|
-
if (response?.status === 'submitted') {
|
|
867
|
-
return { status: 'submitted', answers: response.answers, value: response.value };
|
|
868
|
-
}
|
|
869
|
-
return { status: 'timeout' };
|
|
870
|
-
}
|
|
871
846
|
function buildArtifactMediaMetadata() {
|
|
872
847
|
return {
|
|
873
848
|
...(session.currentTurnId ? { turnId: session.currentTurnId } : {}),
|
|
@@ -903,18 +878,19 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
903
878
|
const questionId = randomUUID();
|
|
904
879
|
const questions = mapClaudeQuestions(input.questions);
|
|
905
880
|
const expiresAt = Date.now() + REPLY_TIMEOUT_MS;
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
881
|
+
// Create + poll fold onto the shared spine. `responderPolicy: 'infer'`
|
|
882
|
+
// keeps the claude host's responder rule byte-identical (explicit
|
|
883
|
+
// `cardResponseUserId` wins, else omit — never the manager's ownerId
|
|
884
|
+
// fallback), and `native` carries the claude-specific descriptor.
|
|
885
|
+
const reply = await config.runtimeInputManager.request('input', conversationId, {
|
|
909
886
|
kind: 'clarify',
|
|
910
|
-
expiresAt,
|
|
911
887
|
title: 'Claude needs input',
|
|
912
888
|
prompt: questions?.length
|
|
913
889
|
? 'Claude needs your input to continue.'
|
|
914
890
|
: 'Claude needs input to continue.',
|
|
915
891
|
...(questions ? { questions } : {}),
|
|
916
892
|
sensitive: Boolean(questions?.some((question) => question.isSecret)),
|
|
917
|
-
|
|
893
|
+
responseUserId: cardResponseUserId,
|
|
918
894
|
native: {
|
|
919
895
|
runtime: 'claude-code',
|
|
920
896
|
method: 'AskUserQuestion',
|
|
@@ -922,9 +898,15 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
922
898
|
turnId: session.currentTurnId ?? undefined,
|
|
923
899
|
},
|
|
924
900
|
turnId: session.currentTurnId ?? undefined,
|
|
901
|
+
}, {
|
|
902
|
+
requestId: questionId,
|
|
903
|
+
expiresAt,
|
|
904
|
+
responderPolicy: 'infer',
|
|
905
|
+
onCreated: () => {
|
|
906
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Runtime input question sent (${questionId.slice(0, 8)})`);
|
|
907
|
+
session.lastActivity = Date.now();
|
|
908
|
+
},
|
|
925
909
|
});
|
|
926
|
-
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Runtime input question sent (${questionId.slice(0, 8)})`);
|
|
927
|
-
const reply = await waitForRuntimeInputResponse({ inputId: questionId, expiresAt });
|
|
928
910
|
if (reply.status !== 'submitted') {
|
|
929
911
|
return { behavior: 'deny', message: 'No reply received (timed out)' };
|
|
930
912
|
}
|
|
@@ -970,22 +952,19 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
970
952
|
const planBody = typeof input.plan === 'string' && input.plan.trim()
|
|
971
953
|
? input.plan.trim()
|
|
972
954
|
: undefined;
|
|
973
|
-
|
|
955
|
+
// Route plan-CREATE through the unified spine (`/runtime-plan/request`):
|
|
956
|
+
// seeds the server-owned pending node/attention/state and authors the
|
|
957
|
+
// `plan_approval` card. Resolution is UNCHANGED — the gate still blocks
|
|
958
|
+
// on the server-authored `plan_approval_reply` below.
|
|
959
|
+
await client.createRuntimePlanRequest({
|
|
960
|
+
conversationId,
|
|
961
|
+
planId,
|
|
962
|
+
title: 'Plan ready for review.',
|
|
974
963
|
summary: 'Claude has prepared a plan and is asking permission to continue.',
|
|
975
964
|
...(planBody ? { body: planBody } : {}),
|
|
976
|
-
...(
|
|
965
|
+
...(cardResponseUserId ? { responseUserId: cardResponseUserId } : {}),
|
|
977
966
|
...(allowedPrompts.length > 0 ? { allowedPrompts } : {}),
|
|
978
|
-
|
|
979
|
-
await sendMessageWithRetry(client, conversationId, planApproval.text, {
|
|
980
|
-
messageId: `claude-plan-${planId}`,
|
|
981
|
-
...(session.activeSelfContextId
|
|
982
|
-
? { selfContextId: session.activeSelfContextId }
|
|
983
|
-
: {}),
|
|
984
|
-
metadata: {
|
|
985
|
-
...planApproval.metadata,
|
|
986
|
-
turnSemantics: 'control',
|
|
987
|
-
replyBehavior: 'suppress_auto_reply',
|
|
988
|
-
},
|
|
967
|
+
...(session.currentTurnId ? { turnId: session.currentTurnId } : {}),
|
|
989
968
|
});
|
|
990
969
|
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Plan approval sent (${planId.slice(0, 8)})`);
|
|
991
970
|
const reply = await waitForReply('plan_approval_reply', planId);
|
|
@@ -2062,6 +2041,10 @@ export async function main() {
|
|
|
2062
2041
|
// ── Session manager ──
|
|
2063
2042
|
const sessions = new Map();
|
|
2064
2043
|
const approvalManager = ownerId ? new ApprovalManager(client, agentId, ownerId) : null;
|
|
2044
|
+
// Interactive runtime input (AskUserQuestion) works with or without an owner,
|
|
2045
|
+
// so its poll engine is always present. Create + poll fold onto the built-in
|
|
2046
|
+
// input descriptor via `request()` (see the AskUserQuestion canUseTool branch).
|
|
2047
|
+
const runtimeInputManager = new RuntimeRequestManager(client, { agentId, ownerId: ownerId ?? '' });
|
|
2065
2048
|
const pendingSessionCreations = new Map();
|
|
2066
2049
|
// Shared /control channel poller (claude host profile): session + signal
|
|
2067
2050
|
// keys, sequential conversations, active/idle cadence + jitter sampled at
|
|
@@ -2368,6 +2351,7 @@ export async function main() {
|
|
|
2368
2351
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
2369
2352
|
runtimePresentation,
|
|
2370
2353
|
approvalManager,
|
|
2354
|
+
runtimeInputManager,
|
|
2371
2355
|
canRequestApproval: () => canRequestCanonApproval(conversationId),
|
|
2372
2356
|
runtimeId,
|
|
2373
2357
|
ownerId,
|
|
@@ -2778,6 +2762,7 @@ export async function main() {
|
|
|
2778
2762
|
clearInterval(idleCheck);
|
|
2779
2763
|
stream.stop();
|
|
2780
2764
|
approvalManager?.dispose();
|
|
2765
|
+
runtimeInputManager.dispose();
|
|
2781
2766
|
await runtimeState.clearAgentRuntime().catch(() => { });
|
|
2782
2767
|
for (const convoId of [...sessions.keys()]) {
|
|
2783
2768
|
closeSession(convoId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.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,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "^0.3.200",
|
|
34
|
-
"@canonmsg/agent-sdk": "^5.
|
|
34
|
+
"@canonmsg/agent-sdk": "^5.1.0",
|
|
35
35
|
"@canonmsg/coding-agent-host": "^0.2.2",
|
|
36
|
-
"@canonmsg/core": "^4.
|
|
36
|
+
"@canonmsg/core": "^4.2.0",
|
|
37
37
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|