@canonmsg/claude-code-plugin 0.9.4 โ 0.9.6
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-runtime.d.ts +21 -0
- package/dist/host-runtime.js +80 -2
- package/dist/host.js +85 -14
- package/package.json +6 -5
package/dist/host-runtime.d.ts
CHANGED
|
@@ -37,6 +37,12 @@ interface HostWorkspaceResolverOption {
|
|
|
37
37
|
id: string;
|
|
38
38
|
cwd: string;
|
|
39
39
|
}
|
|
40
|
+
export declare const HOST_ADMISSION_ACTION_CAPABILITIES: Readonly<{
|
|
41
|
+
canStartDirectConversation: false;
|
|
42
|
+
canSendContactRequest: false;
|
|
43
|
+
canApprovePendingContactRequests: false;
|
|
44
|
+
canRejectPendingContactRequests: false;
|
|
45
|
+
}>;
|
|
40
46
|
export declare function buildCanonHostPrompt(input: {
|
|
41
47
|
hostLabel: string;
|
|
42
48
|
content: string;
|
|
@@ -82,6 +88,21 @@ export declare function buildHydratedInboundContext(input: {
|
|
|
82
88
|
hydratedFromPage: boolean;
|
|
83
89
|
};
|
|
84
90
|
export declare function publishHostAgentRuntime(agentId: string, clientType: AgentClientType, runtime: AgentRuntime): Promise<void>;
|
|
91
|
+
export declare function publishHostSessionSnapshots(input: {
|
|
92
|
+
conversationIds: string[];
|
|
93
|
+
agentId: string;
|
|
94
|
+
clientType: AgentClientType;
|
|
95
|
+
runtime: AgentRuntime;
|
|
96
|
+
workspaceOptions: HostWorkspaceResolverOption[];
|
|
97
|
+
defaultCwd: string;
|
|
98
|
+
liveSessionConfigByConversation?: ReadonlyMap<string, {
|
|
99
|
+
model?: string;
|
|
100
|
+
permissionMode?: string;
|
|
101
|
+
workspaceId?: string;
|
|
102
|
+
executionMode?: SessionWorkspaceConfig['executionMode'];
|
|
103
|
+
executionBranch?: string | null;
|
|
104
|
+
}>;
|
|
105
|
+
}): Promise<void>;
|
|
85
106
|
export declare function readHostSessionConfig<TExtra extends string = never>(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial<Record<TExtra, string>>) | null;
|
|
86
107
|
export declare function loadHostSessionConfig<TExtra extends string = never>(input: {
|
|
87
108
|
conversationId: string;
|
package/dist/host-runtime.js
CHANGED
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
* behavior here, update that copy too and adjust the shared golden
|
|
12
12
|
* fixture test (`packages/claude-code-plugin/src/host-runtime.test.ts`).
|
|
13
13
|
*/
|
|
14
|
-
import { buildBehaviorPolicyLines, buildParticipationHistorySnapshot, buildWorkSessionsPromptLines, mergeWorkSessionContexts, normalizeOptionalString, readSessionWorkspaceConfig, resolveConfiguredWorkspaceCwd, rtdbRead, rtdbWrite, } from '@canonmsg/core';
|
|
14
|
+
import { buildAgentSessionSnapshot, buildConversationWorktreeSpec, buildBehaviorPolicyLines, buildParticipationHistorySnapshot, buildWorkSessionsPromptLines, mergeWorkSessionContexts, normalizeOptionalString, patchAgentSessionSnapshot, readSessionWorkspaceConfig, resolveConfiguredWorkspaceCwd, rtdbRead, rtdbWrite, } from '@canonmsg/core';
|
|
15
|
+
export const HOST_ADMISSION_ACTION_CAPABILITIES = Object.freeze({
|
|
16
|
+
canStartDirectConversation: false,
|
|
17
|
+
canSendContactRequest: false,
|
|
18
|
+
canApprovePendingContactRequests: false,
|
|
19
|
+
canRejectPendingContactRequests: false,
|
|
20
|
+
});
|
|
15
21
|
export function buildCanonHostPrompt(input) {
|
|
16
22
|
const resolvedWorkSessions = mergeWorkSessionContexts(input.workSession, input.workSessions);
|
|
17
23
|
return [
|
|
@@ -70,7 +76,21 @@ function describeContactCard(card) {
|
|
|
70
76
|
if (card.about)
|
|
71
77
|
parts.push(`about: ${card.about}`);
|
|
72
78
|
const identity = `๐ Contact card: "${card.displayName}" (${parts.join(' ยท ')}).`;
|
|
73
|
-
const
|
|
79
|
+
const missingCapabilities = [
|
|
80
|
+
!HOST_ADMISSION_ACTION_CAPABILITIES.canStartDirectConversation
|
|
81
|
+
? 'start a direct conversation'
|
|
82
|
+
: null,
|
|
83
|
+
!HOST_ADMISSION_ACTION_CAPABILITIES.canSendContactRequest
|
|
84
|
+
? 'send a contact request'
|
|
85
|
+
: null,
|
|
86
|
+
!HOST_ADMISSION_ACTION_CAPABILITIES.canApprovePendingContactRequests
|
|
87
|
+
? 'approve pending requests'
|
|
88
|
+
: null,
|
|
89
|
+
!HOST_ADMISSION_ACTION_CAPABILITIES.canRejectPendingContactRequests
|
|
90
|
+
? 'reject pending requests'
|
|
91
|
+
: null,
|
|
92
|
+
].filter(Boolean).join(', ');
|
|
93
|
+
const hint = `This host can inspect the card, but Canon admission actions are missing here. Missing capabilities: ${missingCapabilities}. Use another Canon surface for userId ${card.userId}.`;
|
|
74
94
|
return `${identity}\n${hint}`;
|
|
75
95
|
}
|
|
76
96
|
function describeAttachment(attachment, materialized) {
|
|
@@ -117,6 +137,64 @@ export async function publishHostAgentRuntime(agentId, clientType, runtime) {
|
|
|
117
137
|
updatedAt: { '.sv': 'timestamp' },
|
|
118
138
|
});
|
|
119
139
|
}
|
|
140
|
+
export async function publishHostSessionSnapshots(input) {
|
|
141
|
+
if (input.conversationIds.length === 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
await Promise.all(input.conversationIds.map(async (conversationId) => {
|
|
145
|
+
const persistedConfig = await loadHostSessionConfig({
|
|
146
|
+
conversationId,
|
|
147
|
+
agentId: input.agentId,
|
|
148
|
+
extraStringFields: ['permissionMode'],
|
|
149
|
+
});
|
|
150
|
+
const liveConfig = input.liveSessionConfigByConversation?.get(conversationId) ?? null;
|
|
151
|
+
const mergedConfig = {
|
|
152
|
+
...(persistedConfig ?? {}),
|
|
153
|
+
...(liveConfig ?? {}),
|
|
154
|
+
};
|
|
155
|
+
const snapshot = buildAgentSessionSnapshot({
|
|
156
|
+
conversationId,
|
|
157
|
+
agentId: input.agentId,
|
|
158
|
+
runtime: {
|
|
159
|
+
...input.runtime,
|
|
160
|
+
clientType: input.clientType,
|
|
161
|
+
hostMode: true,
|
|
162
|
+
},
|
|
163
|
+
sessionConfig: {
|
|
164
|
+
...(mergedConfig.model ? { model: mergedConfig.model } : {}),
|
|
165
|
+
...(mergedConfig.permissionMode ? { permissionMode: mergedConfig.permissionMode } : {}),
|
|
166
|
+
...(mergedConfig.workspaceId ? { workspaceId: mergedConfig.workspaceId } : {}),
|
|
167
|
+
...(mergedConfig.executionMode ? { executionMode: mergedConfig.executionMode } : {}),
|
|
168
|
+
},
|
|
169
|
+
lastHeartbeatAt: undefined,
|
|
170
|
+
});
|
|
171
|
+
let executionBranch = liveConfig?.executionBranch ?? null;
|
|
172
|
+
if (!executionBranch && snapshot.executionMode === 'worktree' && snapshot.workspaceId) {
|
|
173
|
+
const workspace = input.workspaceOptions.find((option) => option.id === snapshot.workspaceId);
|
|
174
|
+
if (workspace) {
|
|
175
|
+
executionBranch = buildConversationWorktreeSpec({
|
|
176
|
+
agentId: input.agentId,
|
|
177
|
+
conversationId,
|
|
178
|
+
workspaceCwd: workspace.cwd,
|
|
179
|
+
}).branch;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return patchAgentSessionSnapshot(conversationId, input.agentId, {
|
|
183
|
+
clientType: input.clientType,
|
|
184
|
+
hostMode: true,
|
|
185
|
+
model: snapshot.model ?? null,
|
|
186
|
+
permissionMode: snapshot.permissionMode ?? null,
|
|
187
|
+
workspaceId: snapshot.workspaceId ?? null,
|
|
188
|
+
executionMode: snapshot.executionMode ?? null,
|
|
189
|
+
executionBranch,
|
|
190
|
+
modelOptions: snapshot.modelOptions,
|
|
191
|
+
permissionModeOptions: snapshot.permissionModeOptions,
|
|
192
|
+
workspaceOptions: snapshot.workspaceOptions,
|
|
193
|
+
availableExecutionModes: snapshot.availableExecutionModes,
|
|
194
|
+
lastHeartbeatAt: { '.sv': 'timestamp' },
|
|
195
|
+
});
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
120
198
|
export function readHostSessionConfig(raw, extraStringFields = []) {
|
|
121
199
|
const baseConfig = readSessionWorkspaceConfig(raw);
|
|
122
200
|
if (!raw || typeof raw !== 'object') {
|
package/dist/host.js
CHANGED
|
@@ -26,8 +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, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
|
|
29
|
-
import { buildConfiguredWorkspaceOptions, buildConversationEnvironmentKey, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, ExecutionEnvironmentError, FINAL_MESSAGE_HANDOFF_MS, normalizeTurnMetadata, prepareConversationEnvironment, resolveCanonAgent, getActiveProfile, releaseLock, initRTDBAuth, rtdbRead, rtdbWrite, writeSessionState, clearSessionState, writeTurnState, releaseConversationEnvironment, } from '@canonmsg/core';
|
|
30
|
-
import { buildCanonHostPrompt, buildHydratedInboundContext, createConversationMetadataLoader, loadHostSessionConfig, publishHostAgentRuntime, renderCanonHostInboundContent, resolveHostWorkspaceCwd, } from './host-runtime.js';
|
|
29
|
+
import { CLAUDE_PERMISSION_MODE_OPTIONS, buildConfiguredWorkspaceOptions, buildConversationEnvironmentKey, buildPlanApprovalRequest, buildQuestionRequest, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, ExecutionEnvironmentError, FINAL_MESSAGE_HANDOFF_MS, normalizeTurnMetadata, prepareConversationEnvironment, resolveCanonAgent, getActiveProfile, releaseLock, initRTDBAuth, rtdbRead, rtdbWrite, writeSessionState, clearSessionState, writeTurnState, releaseConversationEnvironment, } from '@canonmsg/core';
|
|
30
|
+
import { buildCanonHostPrompt, buildHydratedInboundContext, createConversationMetadataLoader, loadHostSessionConfig, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, } from './host-runtime.js';
|
|
31
31
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
32
32
|
import { createStreamingBuffer } from './streaming-buffer.js';
|
|
33
33
|
import { runCli } from './cli-entry.js';
|
|
@@ -222,7 +222,7 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
222
222
|
// eslint-disable-next-line prefer-const -- session must be declared before queryOptions but assigned after query()
|
|
223
223
|
let session;
|
|
224
224
|
const REPLY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
225
|
-
function waitForReply() {
|
|
225
|
+
function waitForReply(type, requestId) {
|
|
226
226
|
// Keep session alive while waiting for user input
|
|
227
227
|
session.lastActivity = Date.now();
|
|
228
228
|
return new Promise((resolve, reject) => {
|
|
@@ -231,6 +231,8 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
231
231
|
reject(new Error('Timed out waiting for user reply'));
|
|
232
232
|
}, REPLY_TIMEOUT_MS);
|
|
233
233
|
session.pendingReply = {
|
|
234
|
+
type,
|
|
235
|
+
requestId,
|
|
234
236
|
resolve: (metadata) => {
|
|
235
237
|
clearTimeout(timer);
|
|
236
238
|
resolve(metadata);
|
|
@@ -260,17 +262,16 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
260
262
|
try {
|
|
261
263
|
const questionId = randomUUID();
|
|
262
264
|
const questions = input.questions ?? [];
|
|
263
|
-
|
|
265
|
+
const questionCard = buildQuestionRequest(questionId, questions);
|
|
266
|
+
await client.sendMessage(conversationId, questionCard.text, {
|
|
264
267
|
metadata: {
|
|
265
|
-
|
|
266
|
-
questionId,
|
|
267
|
-
questions,
|
|
268
|
+
...questionCard.metadata,
|
|
268
269
|
turnSemantics: 'control',
|
|
269
270
|
replyBehavior: 'suppress_auto_reply',
|
|
270
271
|
},
|
|
271
272
|
});
|
|
272
273
|
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Question sent (${questionId.slice(0, 8)})`);
|
|
273
|
-
const reply = await waitForReply();
|
|
274
|
+
const reply = await waitForReply('question_reply', questionId);
|
|
274
275
|
return {
|
|
275
276
|
behavior: 'allow',
|
|
276
277
|
updatedInput: { ...input, answers: reply.answers },
|
|
@@ -283,16 +284,16 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
283
284
|
if (toolName === 'ExitPlanMode') {
|
|
284
285
|
try {
|
|
285
286
|
const planId = randomUUID();
|
|
286
|
-
|
|
287
|
+
const planApproval = buildPlanApprovalRequest(planId);
|
|
288
|
+
await client.sendMessage(conversationId, planApproval.text, {
|
|
287
289
|
metadata: {
|
|
288
|
-
|
|
289
|
-
planId,
|
|
290
|
+
...planApproval.metadata,
|
|
290
291
|
turnSemantics: 'control',
|
|
291
292
|
replyBehavior: 'suppress_auto_reply',
|
|
292
293
|
},
|
|
293
294
|
});
|
|
294
295
|
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Plan approval sent (${planId.slice(0, 8)})`);
|
|
295
|
-
const reply = await waitForReply();
|
|
296
|
+
const reply = await waitForReply('plan_approval_reply', planId);
|
|
296
297
|
if (reply.decision === 'approve') {
|
|
297
298
|
return { behavior: 'allow' };
|
|
298
299
|
}
|
|
@@ -755,15 +756,45 @@ export async function main() {
|
|
|
755
756
|
let runtimeDescriptor = {
|
|
756
757
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
757
758
|
defaultPermissionMode: 'default',
|
|
759
|
+
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
758
760
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
759
761
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
760
762
|
};
|
|
763
|
+
const conversationCache = new Map();
|
|
764
|
+
const knownConversationIds = new Set();
|
|
765
|
+
let lastKnownConversationRefreshAt = 0;
|
|
761
766
|
const publishRuntimeHeartbeat = async () => {
|
|
762
767
|
if (!streamConnected)
|
|
763
768
|
return;
|
|
769
|
+
await refreshKnownConversationIds().catch((error) => {
|
|
770
|
+
console.error('[canon-host] Failed to refresh known conversations:', error);
|
|
771
|
+
});
|
|
764
772
|
await publishAgentRuntime(agentId, runtimeDescriptor).catch((error) => {
|
|
765
773
|
console.error('[canon-host] Failed to publish agent runtime:', error);
|
|
766
774
|
});
|
|
775
|
+
await publishHostSessionSnapshots({
|
|
776
|
+
conversationIds: Array.from(knownConversationIds),
|
|
777
|
+
agentId,
|
|
778
|
+
clientType: 'claude-code',
|
|
779
|
+
runtime: runtimeDescriptor,
|
|
780
|
+
workspaceOptions,
|
|
781
|
+
defaultCwd: workingDir,
|
|
782
|
+
liveSessionConfigByConversation: new Map(Array.from(sessions.values()).map((session) => {
|
|
783
|
+
const workspaceId = resolveWorkspaceIdForBaseCwd(session.environment.baseCwd);
|
|
784
|
+
return [
|
|
785
|
+
session.conversationId,
|
|
786
|
+
{
|
|
787
|
+
...(session.state.model ? { model: session.state.model } : {}),
|
|
788
|
+
...(session.state.permissionMode ? { permissionMode: session.state.permissionMode } : {}),
|
|
789
|
+
...(workspaceId ? { workspaceId } : {}),
|
|
790
|
+
executionMode: session.environment.mode,
|
|
791
|
+
executionBranch: session.environment.branch ?? null,
|
|
792
|
+
},
|
|
793
|
+
];
|
|
794
|
+
})),
|
|
795
|
+
}).catch((error) => {
|
|
796
|
+
console.error('[canon-host] Failed to publish session snapshots:', error);
|
|
797
|
+
});
|
|
767
798
|
};
|
|
768
799
|
// โโ Load conversations (informational โ sessions created on demand) โโ
|
|
769
800
|
try {
|
|
@@ -772,6 +803,7 @@ export async function main() {
|
|
|
772
803
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
773
804
|
defaultModel: runtimeModels[0]?.value,
|
|
774
805
|
defaultPermissionMode: 'default',
|
|
806
|
+
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
775
807
|
availableModels: runtimeModels,
|
|
776
808
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
777
809
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
@@ -781,12 +813,18 @@ export async function main() {
|
|
|
781
813
|
runtimeDescriptor = {
|
|
782
814
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
783
815
|
defaultPermissionMode: 'default',
|
|
816
|
+
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
784
817
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
785
818
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
786
819
|
};
|
|
787
820
|
}
|
|
788
821
|
try {
|
|
789
822
|
const convos = await client.getConversations();
|
|
823
|
+
lastKnownConversationRefreshAt = Date.now();
|
|
824
|
+
for (const conversation of convos) {
|
|
825
|
+
knownConversationIds.add(conversation.id);
|
|
826
|
+
conversationCache.set(conversation.id, conversation);
|
|
827
|
+
}
|
|
790
828
|
console.error(`[canon-host] ${convos.length} conversations`);
|
|
791
829
|
}
|
|
792
830
|
catch {
|
|
@@ -797,11 +835,25 @@ export async function main() {
|
|
|
797
835
|
const pendingSessionCreations = new Map();
|
|
798
836
|
/** Saved SDK session IDs for resume after idle eviction, keyed by conversation + workspace */
|
|
799
837
|
const savedSessionIds = new Map();
|
|
800
|
-
const conversationCache = new Map();
|
|
801
838
|
const { getConversationMeta } = createConversationMetadataLoader({
|
|
802
839
|
client,
|
|
803
840
|
conversationCache,
|
|
804
841
|
});
|
|
842
|
+
function resolveWorkspaceIdForBaseCwd(baseCwd) {
|
|
843
|
+
return workspaceOptions.find((option) => option.cwd === baseCwd)?.id;
|
|
844
|
+
}
|
|
845
|
+
async function refreshKnownConversationIds(force = false) {
|
|
846
|
+
if (!force && Date.now() - lastKnownConversationRefreshAt < HEARTBEAT_MS) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const conversations = await client.getConversations();
|
|
850
|
+
knownConversationIds.clear();
|
|
851
|
+
for (const conversation of conversations) {
|
|
852
|
+
knownConversationIds.add(conversation.id);
|
|
853
|
+
conversationCache.set(conversation.id, conversation);
|
|
854
|
+
}
|
|
855
|
+
lastKnownConversationRefreshAt = Date.now();
|
|
856
|
+
}
|
|
805
857
|
async function loadHydratedInboundContext(input) {
|
|
806
858
|
const [conversation, page] = await Promise.all([
|
|
807
859
|
getConversationMeta(input.conversationId),
|
|
@@ -862,6 +914,7 @@ export async function main() {
|
|
|
862
914
|
}
|
|
863
915
|
}
|
|
864
916
|
async function getOrCreateSession(conversationId) {
|
|
917
|
+
knownConversationIds.add(conversationId);
|
|
865
918
|
const existing = sessions.get(conversationId);
|
|
866
919
|
if (existing && !existing.closed) {
|
|
867
920
|
existing.lastActivity = Date.now();
|
|
@@ -934,15 +987,33 @@ export async function main() {
|
|
|
934
987
|
if (m.senderId === agentId)
|
|
935
988
|
return;
|
|
936
989
|
const convoId = payload.conversationId;
|
|
990
|
+
knownConversationIds.add(convoId);
|
|
937
991
|
// Check for metadata replies (question answers, plan approvals)
|
|
938
992
|
if (m.metadata?.type === 'question_reply' || m.metadata?.type === 'plan_approval_reply') {
|
|
939
993
|
const existingSession = sessions.get(convoId);
|
|
994
|
+
const pendingReply = existingSession?.pendingReply;
|
|
995
|
+
const isOwnerReply = m.isOwner ?? (ownerId != null && m.senderId === ownerId);
|
|
996
|
+
const replyRequestId = m.metadata.type === 'question_reply'
|
|
997
|
+
? m.metadata.questionId
|
|
998
|
+
: m.metadata.planId;
|
|
999
|
+
if (!pendingReply) {
|
|
1000
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Ignoring stale ${m.metadata.type} with no pending request`);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (!isOwnerReply) {
|
|
1004
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Ignoring ${m.metadata.type} from non-owner ${m.senderId}`);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
if (pendingReply.type !== m.metadata.type || pendingReply.requestId !== replyRequestId) {
|
|
1008
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Ignoring mismatched ${m.metadata.type} (${String(replyRequestId ?? 'missing-id')})`);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
940
1011
|
if (existingSession?.pendingReply) {
|
|
941
1012
|
const { resolve } = existingSession.pendingReply;
|
|
942
1013
|
existingSession.pendingReply = null;
|
|
943
1014
|
existingSession.lastActivity = Date.now();
|
|
944
1015
|
resolve(m.metadata);
|
|
945
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Reply received (${m.metadata.type})`);
|
|
1016
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Reply received (${m.metadata.type}:${pendingReply.requestId.slice(0, 8)})`);
|
|
946
1017
|
return;
|
|
947
1018
|
}
|
|
948
1019
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
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",
|
|
@@ -23,15 +23,16 @@
|
|
|
23
23
|
".claude-plugin"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"prepare:workspace-deps": "npm --prefix ../core run build && npm --prefix ../agent-sdk run build",
|
|
27
|
+
"build": "npm run prepare:workspace-deps && node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
28
|
+
"dev": "npm run prepare:workspace-deps && tsc --watch",
|
|
28
29
|
"prepack": "npm run build",
|
|
29
30
|
"test": "vitest run"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@anthropic-ai/claude-agent-sdk": "^0.2.101",
|
|
33
|
-
"@canonmsg/agent-sdk": "^0.8.
|
|
34
|
-
"@canonmsg/core": "^0.7.
|
|
34
|
+
"@canonmsg/agent-sdk": "^0.8.3",
|
|
35
|
+
"@canonmsg/core": "^0.7.5",
|
|
35
36
|
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
36
37
|
},
|
|
37
38
|
"engines": {
|