@canonmsg/claude-code-plugin 0.14.7 → 0.14.9

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 CHANGED
@@ -687,6 +687,8 @@ async function buildCanonUserContent(input) {
687
687
  // ── Session factory ─────────────────────────────────────────────────
688
688
  function createSession(conversationId, environment, agentId, client, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
689
689
  const { cwd } = environment;
690
+ const cardResponseUserId = config.ownerId ?? undefined;
691
+ const cardResponseUser = cardResponseUserId ? { responseUserId: cardResponseUserId } : undefined;
690
692
  // Per-session input stream with message queue
691
693
  let resolveInput = null;
692
694
  let inputDone = false;
@@ -783,7 +785,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
783
785
  try {
784
786
  const questionId = randomUUID();
785
787
  const questions = input.questions ?? [];
786
- const questionCard = buildQuestionRequest(questionId, questions);
788
+ const questionCard = buildQuestionRequest(questionId, questions, cardResponseUser);
787
789
  await client.sendMessage(conversationId, questionCard.text, {
788
790
  ...(session.activeSelfContextId
789
791
  ? { selfContextId: session.activeSelfContextId }
@@ -828,9 +830,11 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
828
830
  : [];
829
831
  const planApproval = buildPlanApprovalRequest(planId, 'Plan ready for review.', allowedPrompts.length > 0 ? {
830
832
  summary: 'Claude has prepared a plan and is asking permission to continue.',
833
+ ...(cardResponseUser ?? {}),
831
834
  allowedPrompts,
832
835
  } : {
833
836
  summary: 'Claude has prepared a plan and is asking permission to continue.',
837
+ ...(cardResponseUser ?? {}),
834
838
  });
835
839
  await client.sendMessage(conversationId, planApproval.text, {
836
840
  ...(session.activeSelfContextId
@@ -909,6 +913,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
909
913
  turnState: 'idle',
910
914
  currentTurnId: null,
911
915
  currentTurnOpenedAt: null,
916
+ currentTurnUpdatedAt: null,
912
917
  activeSelfContextId: null,
913
918
  lastAcceptedIntent: null,
914
919
  lastActivity: Date.now(),
@@ -1130,6 +1135,13 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1130
1135
  writeState();
1131
1136
  }
1132
1137
  function writeTurn() {
1138
+ const isOpenTurn = session.turnState === 'thinking'
1139
+ || session.turnState === 'streaming'
1140
+ || session.turnState === 'tool'
1141
+ || session.turnState === 'waiting_input';
1142
+ if (isOpenTurn) {
1143
+ session.currentTurnUpdatedAt = Date.now();
1144
+ }
1133
1145
  runtimeState.writeTurnState(conversationId, {
1134
1146
  turnId: session.currentTurnId,
1135
1147
  state: session.turnState,
@@ -1138,6 +1150,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1138
1150
  lastAcceptedIntent: session.lastAcceptedIntent,
1139
1151
  capabilities: CLAUDE_RUNTIME_CAPABILITIES,
1140
1152
  ...(session.currentTurnOpenedAt ? { openedAt: session.currentTurnOpenedAt } : {}),
1153
+ ...(isOpenTurn && session.currentTurnUpdatedAt ? { turnUpdatedAt: session.currentTurnUpdatedAt } : {}),
1141
1154
  ...(session.turnState === 'idle' || session.turnState === 'completed' || session.turnState === 'interrupted'
1142
1155
  ? { completedAt: { '.sv': 'timestamp' } }
1143
1156
  : {}),
@@ -1485,6 +1498,11 @@ export async function main() {
1485
1498
  // ── Canon API client + RTDB auth ──
1486
1499
  const client = new CanonClient(apiKey, baseUrl);
1487
1500
  initRTDBAuth(client);
1501
+ async function markQueuedMessageAcceptedForConversation(conversationId, sourceMessageId, markAccepted) {
1502
+ if (!markAccepted || !sourceMessageId)
1503
+ return;
1504
+ await client.updateMessageDisposition(conversationId, sourceMessageId, 'accepted_now').catch(() => { });
1505
+ }
1488
1506
  let agentId;
1489
1507
  let ownerId = null;
1490
1508
  let ownerName = null;
@@ -1885,18 +1903,7 @@ export async function main() {
1885
1903
  session.pendingInputs = session.pendingInputs.filter((input) => input.sourceMessageId !== sourceMessageId);
1886
1904
  if (session.pendingInputs.length === before)
1887
1905
  return;
1888
- runtimeState.writeTurnState(conversationId, {
1889
- turnId: session.currentTurnId,
1890
- state: session.turnState,
1891
- queueDepth: session.pendingInputs.length,
1892
- currentSpeakerId: agentId,
1893
- lastAcceptedIntent: session.lastAcceptedIntent,
1894
- capabilities: CLAUDE_RUNTIME_CAPABILITIES,
1895
- ...(session.currentTurnOpenedAt ? { openedAt: session.currentTurnOpenedAt } : {}),
1896
- ...(session.turnState === 'idle' || session.turnState === 'completed' || session.turnState === 'interrupted'
1897
- ? { completedAt: { '.sv': 'timestamp' } }
1898
- : {}),
1899
- }).catch(() => { });
1906
+ session.writeTurn();
1900
1907
  }
1901
1908
  function evictOldestIdle() {
1902
1909
  let oldest = null;
@@ -1981,6 +1988,7 @@ export async function main() {
1981
1988
  approvalManager,
1982
1989
  canRequestApproval: () => canRequestCanonApproval(conversationId),
1983
1990
  runtimeId,
1991
+ ownerId,
1984
1992
  }, resumeId);
1985
1993
  sessions.set(conversationId, session);
1986
1994
  await Promise.all([
@@ -2067,6 +2075,9 @@ export async function main() {
2067
2075
  return;
2068
2076
  }
2069
2077
  markGroupContextModeUsed(input.conversationId, participantContext.groupContextMode);
2078
+ const turnMetadata = normalizeTurnMetadata(m.metadata);
2079
+ const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
2080
+ const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
2070
2081
  let session;
2071
2082
  try {
2072
2083
  session = await getOrCreateSession(input.conversationId);
@@ -2075,6 +2086,7 @@ export async function main() {
2075
2086
  const message = error instanceof Error ? error.message : String(error);
2076
2087
  const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
2077
2088
  console.error(`[canon-host] [${input.conversationId.slice(0, 8)}] Failed to create recovered session: ${message}`);
2089
+ await markQueuedMessageAcceptedForConversation(input.conversationId, m.id ?? null, shouldMarkAccepted);
2078
2090
  await client.sendMessage(input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2079
2091
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2080
2092
  metadata: {
@@ -2085,10 +2097,7 @@ export async function main() {
2085
2097
  }).catch(() => { });
2086
2098
  return;
2087
2099
  }
2088
- const turnMetadata = normalizeTurnMetadata(m.metadata);
2089
2100
  session.activeSelfContextId = activeSelfContextId;
2090
- const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
2091
- const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
2092
2101
  const promptText = buildCanonPrompt({
2093
2102
  content,
2094
2103
  conversationId: input.conversationId,
@@ -2245,6 +2254,9 @@ export async function main() {
2245
2254
  }
2246
2255
  markGroupContextModeUsed(convoId, participantContext.groupContextMode);
2247
2256
  console.error(`[canon-host] [${convoId.slice(0, 8)}] Message from ${sender}: "${content.slice(0, 80)}" (${autoReply.reason})`);
2257
+ const turnMetadata = normalizeTurnMetadata(m.metadata);
2258
+ const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
2259
+ const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
2248
2260
  let session;
2249
2261
  try {
2250
2262
  session = await getOrCreateSession(convoId);
@@ -2253,6 +2265,7 @@ export async function main() {
2253
2265
  const message = error instanceof Error ? error.message : String(error);
2254
2266
  const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
2255
2267
  console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to create session: ${message}`);
2268
+ await markQueuedMessageAcceptedForConversation(convoId, m.id ?? null, shouldMarkAccepted);
2256
2269
  await client.sendMessage(convoId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2257
2270
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2258
2271
  metadata: {
@@ -2263,10 +2276,7 @@ export async function main() {
2263
2276
  }).catch(() => { });
2264
2277
  return;
2265
2278
  }
2266
- const turnMetadata = normalizeTurnMetadata(m.metadata);
2267
2279
  session.activeSelfContextId = activeSelfContextId;
2268
- const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
2269
- const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
2270
2280
  const promptText = buildCanonPrompt({
2271
2281
  content,
2272
2282
  conversationId: convoId,
@@ -2457,7 +2467,9 @@ export async function main() {
2457
2467
  const heartbeat = setInterval(() => {
2458
2468
  for (const session of sessions.values()) {
2459
2469
  session.writeState();
2460
- session.writeTurn();
2470
+ if (session.state.state !== 'running' && session.state.state !== 'requires_action') {
2471
+ session.writeTurn();
2472
+ }
2461
2473
  }
2462
2474
  void publishRuntimeHeartbeat();
2463
2475
  }, HEARTBEAT_MS);
@@ -31,6 +31,7 @@ export interface ClaudeCompletedTurnState {
31
31
  turnState: TurnLifecycleState;
32
32
  currentTurnId: string | null;
33
33
  currentTurnOpenedAt: number | null;
34
+ currentTurnUpdatedAt: number | null;
34
35
  lastAcceptedIntent: DeliveryIntent | null;
35
36
  }
36
37
  export declare function createClaudeInputEnvelope(input: {
@@ -36,6 +36,7 @@ export function resetClaudeCompletedTurnState(session) {
36
36
  session.state.state = 'idle';
37
37
  session.currentTurnId = null;
38
38
  session.currentTurnOpenedAt = null;
39
+ session.currentTurnUpdatedAt = null;
39
40
  session.lastAcceptedIntent = null;
40
41
  session.pendingFinalText = null;
41
42
  session.pendingFinalDelivery = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/claude-code-plugin",
3
- "version": "0.14.7",
3
+ "version": "0.14.9",
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": "^1.5.2",
35
- "@canonmsg/core": "^0.19.3",
34
+ "@canonmsg/agent-sdk": "^1.5.5",
35
+ "@canonmsg/core": "^0.21.0",
36
36
  "@modelcontextprotocol/sdk": "^1.12.0"
37
37
  },
38
38
  "engines": {