@canonmsg/claude-code-plugin 0.14.14 → 0.14.16

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
@@ -26,7 +26,7 @@ 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, 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';
29
+ import { CLAUDE_EFFORT_OPTIONS, 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, createTypingStatusPublisher, 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, sendMessageWithRetry, loadHostSessionConfig, loadRuntimeSessionState, markLocalRuntimeStopped, releaseConversationEnvironment, saveRuntimeSessionState, clearRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
30
30
  import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
31
31
  import { runCli } from './cli-entry.js';
32
32
  import { decideClaudeToolPermissionForMode, parseAllowedNonOwnerClaudeTools, } from './tool-policy.js';
@@ -167,11 +167,7 @@ function buildClaudeRuntimeDescriptor(input) {
167
167
  label: 'Level',
168
168
  kind: 'enum',
169
169
  required: true,
170
- choices: [
171
- { value: 'low', label: 'Low' },
172
- { value: 'medium', label: 'Medium' },
173
- { value: 'high', label: 'High' },
174
- ],
170
+ choices: [...CLAUDE_EFFORT_OPTIONS],
175
171
  }],
176
172
  dispatch: {
177
173
  kind: 'control',
@@ -199,6 +195,7 @@ function buildClaudeRuntimeDescriptor(input) {
199
195
  commands,
200
196
  });
201
197
  }
198
+ const CLAUDE_EFFORT_VALUES = new Set(CLAUDE_EFFORT_OPTIONS.map((option) => option.value));
202
199
  function parseClaudePermissionMode(value) {
203
200
  if (value === 'acceptEdits'
204
201
  || value === 'auto'
@@ -211,10 +208,13 @@ function parseClaudePermissionMode(value) {
211
208
  return null;
212
209
  }
213
210
  function normalizeClaudeEffortLevel(value) {
214
- return value === 'low' || value === 'medium' || value === 'high'
211
+ return typeof value === 'string' && CLAUDE_EFFORT_VALUES.has(value)
215
212
  ? value
216
213
  : undefined;
217
214
  }
215
+ async function applyClaudeEffortLevel(q, effort) {
216
+ await q.applyFlagSettings({ effortLevel: effort });
217
+ }
218
218
  let cachedClaudeCliPath;
219
219
  // The SDK bundles a Bun-compiled `claude` Mach-O binary next to
220
220
  // claude-agent-sdk-darwin-arm64. On some macOS setups that exact binary gets
@@ -522,7 +522,10 @@ const STREAMING_THROTTLE_MS = 250;
522
522
  const HEARTBEAT_MS = 30_000;
523
523
  const IDLE_CHECK_MS = 60_000;
524
524
  const CONTROL_POLL_MS = 2000;
525
- const FINAL_DELIVERY_RETRY_MS = 2_000;
525
+ const IDLE_CONTROL_POLL_MS = 10_000;
526
+ const CONTROL_POLL_JITTER_MS = 1_000;
527
+ const FINAL_DELIVERY_RETRY_MS = 30_000;
528
+ const MAX_FINAL_DELIVERY_RETRIES = 1;
526
529
  const CLAUDE_RUNTIME_CAPABILITIES = {
527
530
  ...DEFAULT_RUNTIME_CAPABILITIES,
528
531
  supportsInterrupt: true,
@@ -532,6 +535,10 @@ const CLAUDE_RUNTIME_CAPABILITIES = {
532
535
  supportsRequiresAction: true,
533
536
  supportsNonFinalPermanentMessages: false,
534
537
  };
538
+ function controlPollDelayMs(hasActiveWork) {
539
+ const base = hasActiveWork ? CONTROL_POLL_MS : IDLE_CONTROL_POLL_MS;
540
+ return base + Math.floor(Math.random() * CONTROL_POLL_JITTER_MS);
541
+ }
535
542
  function buildCanonPrompt(input) {
536
543
  const { session, ...promptInput } = input;
537
544
  return buildCanonHostPrompt({
@@ -550,11 +557,7 @@ function optionLabel(value, options) {
550
557
  function buildClaudeSessionContextLines(session) {
551
558
  const workspace = workspaceOptions.find((option) => option.cwd === session.environment.baseCwd);
552
559
  const permissionMode = optionLabel(session.state.permissionMode, CLAUDE_PERMISSION_MODE_OPTIONS);
553
- const effort = optionLabel(session.state.effort, [
554
- { value: 'low', label: 'Low' },
555
- { value: 'medium', label: 'Medium' },
556
- { value: 'high', label: 'High' },
557
- ]);
560
+ const effort = optionLabel(session.state.effort, CLAUDE_EFFORT_OPTIONS);
558
561
  const model = optionLabel(session.state.model, session.availableModels);
559
562
  return [
560
563
  model ? `Selected model: ${model}` : null,
@@ -606,7 +609,7 @@ async function buildCanonUserContent(input) {
606
609
  ];
607
610
  }
608
611
  // ── Session factory ─────────────────────────────────────────────────
609
- function createSession(conversationId, environment, agentId, client, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
612
+ function createSession(conversationId, environment, agentId, client, typingSignals, runtimeState, onSessionEnd, onRuntimeDescriptorUpdate, config, resumeSessionId) {
610
613
  const { cwd } = environment;
611
614
  const cardResponseUserId = config.ownerId ?? undefined;
612
615
  const cardResponseUser = cardResponseUserId ? { responseUserId: cardResponseUserId } : undefined;
@@ -692,6 +695,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
692
695
  cwd,
693
696
  includePartialMessages: true,
694
697
  maxTurns: 200,
698
+ ...(config.effort ? { effort: config.effort } : {}),
695
699
  settings: { forceLoginMethod: 'claudeai' },
696
700
  settingSources: ['project', 'local'],
697
701
  ...(claudeCliPath ? { pathToClaudeCodeExecutable: claudeCliPath } : {}),
@@ -707,7 +711,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
707
711
  const questionId = randomUUID();
708
712
  const questions = input.questions ?? [];
709
713
  const questionCard = buildQuestionRequest(questionId, questions, cardResponseUser);
710
- await client.sendMessage(conversationId, questionCard.text, {
714
+ await sendMessageWithRetry(client, conversationId, questionCard.text, {
715
+ messageId: `claude-question-${questionId}`,
711
716
  ...(session.activeSelfContextId
712
717
  ? { selfContextId: session.activeSelfContextId }
713
718
  : {}),
@@ -757,7 +762,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
757
762
  summary: 'Claude has prepared a plan and is asking permission to continue.',
758
763
  ...(cardResponseUser ?? {}),
759
764
  });
760
- await client.sendMessage(conversationId, planApproval.text, {
765
+ await sendMessageWithRetry(client, conversationId, planApproval.text, {
766
+ messageId: `claude-plan-${planId}`,
761
767
  ...(session.activeSelfContextId
762
768
  ? { selfContextId: session.activeSelfContextId }
763
769
  : {}),
@@ -915,17 +921,21 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
915
921
  clearIdleResetTimer();
916
922
  session.idleResetTimer = setTimeout(() => {
917
923
  session.idleResetTimer = null;
918
- client.setTyping(conversationId, false).catch(() => { });
924
+ typingSignals.clear(conversationId).catch(() => { });
919
925
  clearStreaming();
920
926
  resetTurnToIdle();
921
927
  }, FINAL_MESSAGE_HANDOFF_MS);
922
928
  }
923
929
  function markPendingFinalDelivery(finalText, turn) {
924
930
  clearIdleResetTimer();
931
+ const retryCount = session.pendingFinalDelivery?.turnKey === turn.turnKey
932
+ ? session.pendingFinalDelivery.retryCount
933
+ : 0;
925
934
  session.pendingFinalText = finalText;
926
935
  session.pendingFinalDelivery = {
927
936
  turnKey: turn.turnKey,
928
937
  text: finalText,
938
+ retryCount,
929
939
  messageId: buildClaudeFinalMessageId({
930
940
  agentId,
931
941
  conversationId,
@@ -938,7 +948,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
938
948
  streamingOutput.replaceSnapshot(finalText, 'streaming').catch(() => { });
939
949
  writeState();
940
950
  writeTurn();
941
- client.setTyping(conversationId, true, 'typing').catch(() => { });
951
+ typingSignals.start(conversationId, 'typing').catch(() => { });
942
952
  }
943
953
  async function deliverFinalReply(finalText, turn = session.activeInput) {
944
954
  if (!shouldDeliverClaudeFinal({
@@ -957,7 +967,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
957
967
  turnKey: deliverTurn.turnKey,
958
968
  });
959
969
  try {
960
- await client.sendMessage(conversationId, finalText, {
970
+ await sendMessageWithRetry(client, conversationId, finalText, {
961
971
  messageId: finalMessageId,
962
972
  ...(session.activeSelfContextId
963
973
  ? { selfContextId: session.activeSelfContextId }
@@ -990,6 +1000,14 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
990
1000
  function scheduleFinalDeliveryRetry(pending = session.pendingFinalDelivery) {
991
1001
  if (!pending || session.closed || session.pendingFinalDelivery?.turnKey !== pending.turnKey)
992
1002
  return;
1003
+ if (pending.retryCount >= MAX_FINAL_DELIVERY_RETRIES) {
1004
+ console.error(`[canon-host] [${conversationId.slice(0, 8)}] Final reply delivery exhausted after bounded retries`);
1005
+ session.pendingFinalDelivery = null;
1006
+ resetTurnToIdle();
1007
+ return;
1008
+ }
1009
+ pending.retryCount += 1;
1010
+ session.pendingFinalDelivery = pending;
993
1011
  clearFinalDeliveryTimer();
994
1012
  session.finalDeliveryTimer = setTimeout(() => {
995
1013
  session.finalDeliveryTimer = null;
@@ -1094,19 +1112,17 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1094
1112
  return;
1095
1113
  if (session.turnState !== 'thinking' && session.turnState !== 'tool')
1096
1114
  return;
1097
- client.setTyping(conversationId, true, 'thinking').catch(() => { });
1115
+ typingSignals.start(conversationId, 'thinking').catch(() => { });
1098
1116
  }
1099
1117
  function startVisibleWorkSignal() {
1100
1118
  refreshVisibleWorkSignal();
1101
- if (session.typingKeepaliveTimer)
1102
- return;
1103
- session.typingKeepaliveTimer = setInterval(refreshVisibleWorkSignal, 3500);
1104
1119
  }
1105
1120
  function stopVisibleWorkSignal() {
1106
1121
  if (session.typingKeepaliveTimer) {
1107
1122
  clearInterval(session.typingKeepaliveTimer);
1108
1123
  session.typingKeepaliveTimer = null;
1109
1124
  }
1125
+ typingSignals.clear(conversationId).catch(() => { });
1110
1126
  }
1111
1127
  async function markQueuedMessageAccepted(sourceMessageId, markAccepted) {
1112
1128
  if (!markAccepted || !sourceMessageId)
@@ -1152,7 +1168,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1152
1168
  session.turnState = 'interrupted';
1153
1169
  stopVisibleWorkSignal();
1154
1170
  clearStreaming().catch(() => { });
1155
- client.setTyping(conversationId, false).catch(() => { });
1156
1171
  writeTurn();
1157
1172
  void session.query.interrupt().catch(() => { });
1158
1173
  return;
@@ -1192,7 +1207,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1192
1207
  const event = msg.event;
1193
1208
  if (isActiveTurnInterrupted()) {
1194
1209
  stopVisibleWorkSignal();
1195
- client.setTyping(conversationId, false).catch(() => { });
1196
1210
  break;
1197
1211
  }
1198
1212
  if (event?.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
@@ -1222,7 +1236,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1222
1236
  session.toolInProgress = false;
1223
1237
  session.turnState = 'streaming';
1224
1238
  stopVisibleWorkSignal();
1225
- client.setTyping(conversationId, false).catch(() => { });
1226
1239
  writeTurn();
1227
1240
  onStreamDelta(event.delta.text);
1228
1241
  }
@@ -1247,7 +1260,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1247
1260
  }
1248
1261
  else if (state === 'idle') {
1249
1262
  stopVisibleWorkSignal();
1250
- client.setTyping(conversationId, false).catch(() => { });
1251
1263
  }
1252
1264
  else if (state === 'requires_action') {
1253
1265
  clearIdleResetTimer();
@@ -1258,7 +1270,6 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1258
1270
  session.toolInProgress = false;
1259
1271
  session.turnState = 'waiting_input';
1260
1272
  stopVisibleWorkSignal();
1261
- client.setTyping(conversationId, false).catch(() => { });
1262
1273
  }
1263
1274
  writeState();
1264
1275
  writeTurn();
@@ -1345,7 +1356,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1345
1356
  }
1346
1357
  if (config.effort) {
1347
1358
  try {
1348
- await q.applyFlagSettings({ effortLevel: config.effort });
1359
+ await applyClaudeEffortLevel(q, config.effort);
1349
1360
  session.state.effort = config.effort;
1350
1361
  }
1351
1362
  catch (err) {
@@ -1442,6 +1453,11 @@ export async function main() {
1442
1453
  // ── Canon API client + RTDB auth ──
1443
1454
  const client = new CanonClient(apiKey, baseUrl);
1444
1455
  initRTDBAuth(client);
1456
+ const typingSignals = createTypingStatusPublisher({
1457
+ setTyping: (conversationId, typing, status) => status
1458
+ ? client.setTyping(conversationId, typing, status)
1459
+ : client.setTyping(conversationId, typing),
1460
+ });
1445
1461
  async function markQueuedMessageAcceptedForConversation(conversationId, sourceMessageId, markAccepted) {
1446
1462
  if (!markAccepted || !sourceMessageId)
1447
1463
  return;
@@ -1648,7 +1664,7 @@ export async function main() {
1648
1664
  runtimeState.clearStreaming(conversation.id).catch(() => { });
1649
1665
  runtimeState.clearSessionState(conversation.id).catch(() => { });
1650
1666
  runtimeState.clearTurnState(conversation.id).catch(() => { });
1651
- client.setTyping(conversation.id, false).catch(() => { });
1667
+ typingSignals.clear(conversation.id).catch(() => { });
1652
1668
  }
1653
1669
  console.error(`[canon-host] ${convos.length} conversations`);
1654
1670
  }
@@ -1806,7 +1822,8 @@ export async function main() {
1806
1822
  }
1807
1823
  runtimeState.clearSessionState(conversationId).catch(() => { });
1808
1824
  runtimeState.clearTurnState(conversationId).catch(() => { });
1809
- client.setTyping(conversationId, false).catch(() => { });
1825
+ typingSignals.clear(conversationId).catch(() => { });
1826
+ typingSignals.dispose(conversationId);
1810
1827
  try {
1811
1828
  session.query.close();
1812
1829
  }
@@ -1825,7 +1842,7 @@ export async function main() {
1825
1842
  session.pendingFinalDelivery = null;
1826
1843
  session.turnState = 'interrupted';
1827
1844
  session.clearStreaming().catch(() => { });
1828
- client.setTyping(conversationId, false).catch(() => { });
1845
+ typingSignals.clear(conversationId).catch(() => { });
1829
1846
  if (session.state.state === 'running') {
1830
1847
  await session.query.interrupt().catch(() => { });
1831
1848
  }
@@ -1893,7 +1910,7 @@ export async function main() {
1893
1910
  executionMode: environment.mode,
1894
1911
  });
1895
1912
  const resumeId = savedSessionIds.get(resumeKey) ?? persisted?.claudeSessionId;
1896
- const session = createSession(conversationId, environment, agentId, client, runtimeState, (convoId, sdkSessionId) => {
1913
+ const session = createSession(conversationId, environment, agentId, client, typingSignals, runtimeState, (convoId, sdkSessionId) => {
1897
1914
  // Called when session processing loop ends — save session ID for resume
1898
1915
  const existing = sessions.get(convoId);
1899
1916
  if (existing) {
@@ -1916,6 +1933,8 @@ export async function main() {
1916
1933
  }
1917
1934
  }
1918
1935
  existing?.clearStreaming().catch(() => { });
1936
+ typingSignals.clear(convoId).catch(() => { });
1937
+ typingSignals.dispose(convoId);
1919
1938
  sessions.delete(convoId);
1920
1939
  runtimeState.clearSessionState(convoId).catch(() => { });
1921
1940
  runtimeState.clearTurnState(convoId).catch(() => { });
@@ -2031,7 +2050,8 @@ export async function main() {
2031
2050
  const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
2032
2051
  console.error(`[canon-host] [${input.conversationId.slice(0, 8)}] Failed to create recovered session: ${message}`);
2033
2052
  await markQueuedMessageAcceptedForConversation(input.conversationId, m.id ?? null, shouldMarkAccepted);
2034
- await client.sendMessage(input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2053
+ await sendMessageWithRetry(client, input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2054
+ messageId: `claude-start-failed-${m.id}`,
2035
2055
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2036
2056
  metadata: {
2037
2057
  turnSemantics: 'turn_complete',
@@ -2208,7 +2228,8 @@ export async function main() {
2208
2228
  const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
2209
2229
  console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to create session: ${message}`);
2210
2230
  await markQueuedMessageAcceptedForConversation(convoId, m.id ?? null, shouldMarkAccepted);
2211
- await client.sendMessage(convoId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2231
+ await sendMessageWithRetry(client, convoId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
2232
+ messageId: `claude-start-failed-${m.id}`,
2212
2233
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2213
2234
  metadata: {
2214
2235
  turnSemantics: 'turn_complete',
@@ -2273,6 +2294,11 @@ export async function main() {
2273
2294
  // ── Control signal watcher ──
2274
2295
  const pollControl = async () => {
2275
2296
  while (!controlStopped) {
2297
+ const hadActiveWork = [...sessions.values()].some((session) => !session.closed
2298
+ && (session.state.state === 'running'
2299
+ || session.state.state === 'requires_action'
2300
+ || session.pendingInputs.length > 0
2301
+ || Boolean(session.pendingFinalDelivery)));
2276
2302
  for (const convoId of [...sessions.keys()]) {
2277
2303
  try {
2278
2304
  // Session control signals (model, mode)
@@ -2317,7 +2343,7 @@ export async function main() {
2317
2343
  const nextEffort = normalizeClaudeEffortLevel(ctrl.effort);
2318
2344
  if (nextEffort && nextEffort !== session.state.effort) {
2319
2345
  try {
2320
- await session.query.applyFlagSettings({ effortLevel: nextEffort });
2346
+ await applyClaudeEffortLevel(session.query, nextEffort);
2321
2347
  session.state.effort = nextEffort;
2322
2348
  session.clearRuntimeControlError('effort');
2323
2349
  console.error(`[canon-host] [${convoId.slice(0, 8)}] Effort → ${nextEffort}`);
@@ -2368,7 +2394,7 @@ export async function main() {
2368
2394
  session.pendingFinalText = null;
2369
2395
  session.turnState = 'interrupted';
2370
2396
  session.clearStreaming().catch(() => { });
2371
- client.setTyping(convoId, false).catch(() => { });
2397
+ typingSignals.clear(convoId).catch(() => { });
2372
2398
  runtimeState.writeTurnState(convoId, {
2373
2399
  turnId: session.currentTurnId,
2374
2400
  state: 'interrupted',
@@ -2395,7 +2421,7 @@ export async function main() {
2395
2421
  // Transient RTDB errors — ignore and retry next cycle
2396
2422
  }
2397
2423
  }
2398
- await new Promise((r) => setTimeout(r, CONTROL_POLL_MS));
2424
+ await new Promise((r) => setTimeout(r, controlPollDelayMs(hadActiveWork)));
2399
2425
  }
2400
2426
  };
2401
2427
  pollControl().catch((err) => console.error('[canon-host] Control poll crashed:', err));
@@ -19,6 +19,7 @@ export interface ClaudePendingFinalDelivery {
19
19
  turnKey: string;
20
20
  text: string;
21
21
  messageId: string;
22
+ retryCount: number;
22
23
  }
23
24
  export interface ClaudeCompletedTurnState {
24
25
  state: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/claude-code-plugin",
3
- "version": "0.14.14",
3
+ "version": "0.14.16",
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.141",
34
- "@canonmsg/agent-sdk": "^2.0.0",
35
- "@canonmsg/core": "^1.0.0",
34
+ "@canonmsg/agent-sdk": "^2.1.1",
35
+ "@canonmsg/core": "^1.3.0",
36
36
  "@modelcontextprotocol/sdk": "^1.29.0"
37
37
  },
38
38
  "engines": {