@canonmsg/claude-code-plugin 0.14.11 → 0.14.13

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,9 +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, 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, 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_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';
30
30
  import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
31
- import { createStreamingBuffer } from './streaming-buffer.js';
32
31
  import { runCli } from './cli-entry.js';
33
32
  import { decideClaudeToolPermissionForMode, parseAllowedNonOwnerClaudeTools, } from './tool-policy.js';
34
33
  import { buildClaudeFinalMessageId, createClaudeInputEnvelope, formatClaudeControlError, mergeClaudeModelOptions, resetClaudeCompletedTurnState, shouldDeliverClaudeFinal, } from './session-state.js';
@@ -183,6 +182,8 @@ function buildClaudeRuntimeDescriptor(input) {
183
182
  ...RUNTIME_NEW_SESSION_ACTION,
184
183
  primitive: 'session.new',
185
184
  },
185
+ RUNTIME_STOP_ACTION,
186
+ RUNTIME_STOP_AND_DROP_ACTION,
186
187
  ];
187
188
  return buildFirstPartyCodingRuntimeDescriptor({
188
189
  clientType: 'claude-code',
@@ -196,86 +197,6 @@ function buildClaudeRuntimeDescriptor(input) {
196
197
  admissionActions: HOST_ADMISSION_ACTIONS_DISABLED,
197
198
  presentation: input.presentation,
198
199
  commands,
199
- actions: [
200
- {
201
- id: 'plan',
202
- label: 'Plan first',
203
- description: 'Switch Claude into Plan mode. Text after /plan is kept as a ready prompt while the mode applies.',
204
- aliases: ['plan'],
205
- category: 'plan',
206
- placements: ['composer_slash', 'command_palette'],
207
- availability: ['always'],
208
- trailingTextBehavior: 'send_as_prompt',
209
- dispatch: {
210
- kind: 'control',
211
- controlId: 'permissionMode',
212
- value: 'plan',
213
- },
214
- },
215
- {
216
- id: 'runtime-status',
217
- label: 'Runtime status',
218
- description: 'Open Claude runtime details, including account, project, and policy status.',
219
- aliases: ['status'],
220
- category: 'details',
221
- placements: ['composer_slash', 'command_palette'],
222
- availability: ['always'],
223
- dispatch: { kind: 'open_details', target: 'status' },
224
- },
225
- {
226
- id: 'mcp-inventory',
227
- label: 'MCP servers',
228
- description: 'Open the Claude MCP inventory published by the host.',
229
- aliases: ['mcp'],
230
- category: 'details',
231
- placements: ['composer_slash', 'command_palette'],
232
- availability: ['always'],
233
- dispatch: { kind: 'open_details', target: 'mcp' },
234
- },
235
- {
236
- id: 'plugin-inventory',
237
- label: 'Plugins',
238
- description: 'Open the Claude plugin and command inventory published by the host.',
239
- aliases: ['plugins'],
240
- category: 'details',
241
- placements: ['composer_slash', 'command_palette'],
242
- availability: ['always'],
243
- dispatch: { kind: 'open_details', target: 'plugins' },
244
- },
245
- {
246
- id: 'model-details',
247
- label: 'Model control',
248
- description: 'Open runtime details and use the live Model control.',
249
- aliases: ['model'],
250
- category: 'details',
251
- placements: ['composer_slash', 'command_palette'],
252
- availability: ['always'],
253
- dispatch: { kind: 'open_details', target: 'model' },
254
- },
255
- {
256
- id: 'permission-details',
257
- label: 'Permission mode',
258
- description: 'Open runtime details and use the live Permission mode control.',
259
- aliases: ['permission'],
260
- category: 'details',
261
- placements: ['composer_slash', 'command_palette'],
262
- availability: ['always'],
263
- dispatch: { kind: 'open_details', target: 'permissionMode' },
264
- },
265
- {
266
- id: 'effort-details',
267
- label: 'Thinking level',
268
- description: 'Open runtime details and use the live Thinking level control.',
269
- aliases: ['effort'],
270
- category: 'details',
271
- placements: ['composer_slash', 'command_palette'],
272
- availability: ['always'],
273
- dispatch: { kind: 'open_details', target: 'effort' },
274
- },
275
- RUNTIME_STOP_ACTION,
276
- RUNTIME_STOP_AND_DROP_ACTION,
277
- RUNTIME_NEW_SESSION_ACTION,
278
- ],
279
200
  });
280
201
  }
281
202
  function parseClaudePermissionMode(value) {
@@ -923,40 +844,50 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
923
844
  pendingReply: null,
924
845
  writeState: () => writeState(),
925
846
  writeTurn: () => writeTurn(),
847
+ clearStreaming: () => clearStreaming(),
926
848
  setRuntimeControlError: (controlId, value, error) => setRuntimeControlError(controlId, value, error),
927
849
  clearRuntimeControlError: (controlId) => clearRuntimeControlError(controlId),
928
850
  };
929
- const streamingBuffer = createStreamingBuffer({
851
+ const streamingOutput = createTurnOutputController({
852
+ turnId: `claude-${conversationId}`,
853
+ mode: 'delta',
930
854
  throttleMs: STREAMING_THROTTLE_MS,
931
- getText: () => session.streamingText,
932
- setText: (text) => {
933
- session.streamingText = text;
934
- },
935
- getTimer: () => session.streamingTimer,
936
- setTimer: (timer) => {
937
- session.streamingTimer = timer;
855
+ writeSnapshot: (snapshot) => {
856
+ if (session.closed)
857
+ return Promise.resolve();
858
+ const turnId = session.currentTurnId ?? snapshot.turnId;
859
+ session.streamingText = snapshot.text;
860
+ const blocks = snapshot.blocks?.map((block) => ({ ...block, turnId }));
861
+ return runtimeState.writeStreaming(conversationId, {
862
+ text: snapshot.text,
863
+ status: snapshot.status,
864
+ messageId: turnId,
865
+ turnId,
866
+ blocks,
867
+ });
938
868
  },
939
- onWrite: (text) => {
940
- runtimeState.writeStreaming(conversationId, {
941
- text,
942
- status: 'streaming',
943
- }).catch(() => { });
869
+ clearSnapshot: () => {
870
+ session.streamingText = '';
871
+ return runtimeState.clearStreaming(conversationId);
944
872
  },
945
873
  });
946
874
  // ── Per-session streaming helpers ──
947
875
  function flushStreaming() {
948
- streamingBuffer.flush();
876
+ streamingOutput.flush().catch(() => { });
949
877
  }
950
878
  function onStreamDelta(text) {
951
- streamingBuffer.append(text);
879
+ streamingOutput.appendDelta(text);
952
880
  }
953
881
  function isActiveTurnInterrupted() {
954
882
  const turnKey = session.activeInput?.turnKey;
955
883
  return Boolean(turnKey && session.interruptedTurnKeys.has(turnKey));
956
884
  }
957
885
  function clearStreaming() {
958
- streamingBuffer.clear();
959
- runtimeState.clearStreaming(conversationId).catch(() => { });
886
+ return streamingOutput.clear().catch(() => { });
887
+ }
888
+ function getFinalTurnTrail() {
889
+ const turnId = session.currentTurnId ?? `claude-${conversationId}`;
890
+ return streamingOutput.getFinalTrail().map((block) => ({ ...block, turnId }));
960
891
  }
961
892
  function clearIdleResetTimer() {
962
893
  if (!session.idleResetTimer)
@@ -1004,7 +935,7 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1004
935
  session.state.state = 'running';
1005
936
  session.turnState = 'streaming';
1006
937
  stopVisibleWorkSignal();
1007
- streamingBuffer.replace(finalText);
938
+ streamingOutput.replaceSnapshot(finalText, 'streaming').catch(() => { });
1008
939
  writeState();
1009
940
  writeTurn();
1010
941
  client.setTyping(conversationId, true, 'typing').catch(() => { });
@@ -1036,8 +967,10 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1036
967
  turnKey: deliverTurn.turnKey,
1037
968
  sourceMessageId: deliverTurn.sourceMessageId ?? undefined,
1038
969
  turnSemantics: 'turn_complete',
1039
- turnComplete: true,
1040
970
  deliveryIntent: session.lastAcceptedIntent ?? undefined,
971
+ ...(getFinalTurnTrail().length > 0
972
+ ? { turnTrail: getFinalTurnTrail() }
973
+ : {}),
1041
974
  },
1042
975
  });
1043
976
  session.finalizedTurnKeys.add(deliverTurn.turnKey);
@@ -1218,6 +1151,8 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1218
1151
  session.pendingInputs.unshift(input);
1219
1152
  session.turnState = 'interrupted';
1220
1153
  stopVisibleWorkSignal();
1154
+ clearStreaming().catch(() => { });
1155
+ client.setTyping(conversationId, false).catch(() => { });
1221
1156
  writeTurn();
1222
1157
  void session.query.interrupt().catch(() => { });
1223
1158
  return;
@@ -1263,11 +1198,20 @@ function createSession(conversationId, environment, agentId, client, runtimeStat
1263
1198
  if (event?.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
1264
1199
  session.toolInProgress = true;
1265
1200
  session.turnState = 'tool';
1201
+ streamingOutput.addBlock({
1202
+ id: `tool:${session.currentTurnId ?? conversationId}`,
1203
+ kind: 'tool',
1204
+ status: 'running',
1205
+ title: typeof event.content_block?.name === 'string'
1206
+ ? event.content_block.name
1207
+ : 'Claude tool use',
1208
+ }).catch(() => { });
1266
1209
  startVisibleWorkSignal();
1267
1210
  writeTurn();
1268
1211
  }
1269
1212
  else if (event?.type === 'content_block_stop' && session.toolInProgress) {
1270
1213
  session.toolInProgress = false;
1214
+ streamingOutput.completeBlock(`tool:${session.currentTurnId ?? conversationId}`).catch(() => { });
1271
1215
  if (session.state.state === 'running') {
1272
1216
  session.turnState = 'thinking';
1273
1217
  startVisibleWorkSignal();
@@ -1851,10 +1795,7 @@ export async function main() {
1851
1795
  });
1852
1796
  }
1853
1797
  }
1854
- if (session.streamingTimer)
1855
- clearTimeout(session.streamingTimer);
1856
- session.streamingText = '';
1857
- runtimeState.clearStreaming(conversationId).catch(() => { });
1798
+ session.clearStreaming().catch(() => { });
1858
1799
  if (session.idleResetTimer) {
1859
1800
  clearTimeout(session.idleResetTimer);
1860
1801
  session.idleResetTimer = null;
@@ -1883,6 +1824,8 @@ export async function main() {
1883
1824
  session.pendingFinalText = null;
1884
1825
  session.pendingFinalDelivery = null;
1885
1826
  session.turnState = 'interrupted';
1827
+ session.clearStreaming().catch(() => { });
1828
+ client.setTyping(conversationId, false).catch(() => { });
1886
1829
  if (session.state.state === 'running') {
1887
1830
  await session.query.interrupt().catch(() => { });
1888
1831
  }
@@ -1972,6 +1915,7 @@ export async function main() {
1972
1915
  });
1973
1916
  }
1974
1917
  }
1918
+ existing?.clearStreaming().catch(() => { });
1975
1919
  sessions.delete(convoId);
1976
1920
  runtimeState.clearSessionState(convoId).catch(() => { });
1977
1921
  runtimeState.clearTurnState(convoId).catch(() => { });
@@ -2091,7 +2035,6 @@ export async function main() {
2091
2035
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2092
2036
  metadata: {
2093
2037
  turnSemantics: 'turn_complete',
2094
- turnComplete: true,
2095
2038
  replyBehavior: 'suppress_auto_reply',
2096
2039
  },
2097
2040
  }).catch(() => { });
@@ -2170,7 +2113,6 @@ export async function main() {
2170
2113
  if (m.metadata?.type === 'approval_reply' || (approvalManager?.pendingCount ?? 0) > 0) {
2171
2114
  const consumed = approvalManager?.handleMessage(convoId, {
2172
2115
  senderId: m.senderId,
2173
- text: m.text ?? undefined,
2174
2116
  metadata: m.metadata,
2175
2117
  }) ?? false;
2176
2118
  if (m.metadata?.type === 'approval_reply') {
@@ -2270,7 +2212,6 @@ export async function main() {
2270
2212
  ...(activeSelfContextId ? { selfContextId: activeSelfContextId } : {}),
2271
2213
  metadata: {
2272
2214
  turnSemantics: 'turn_complete',
2273
- turnComplete: true,
2274
2215
  replyBehavior: 'suppress_auto_reply',
2275
2216
  },
2276
2217
  }).catch(() => { });
@@ -2426,6 +2367,8 @@ export async function main() {
2426
2367
  }
2427
2368
  session.pendingFinalText = null;
2428
2369
  session.turnState = 'interrupted';
2370
+ session.clearStreaming().catch(() => { });
2371
+ client.setTyping(convoId, false).catch(() => { });
2429
2372
  runtimeState.writeTurnState(convoId, {
2430
2373
  turnId: session.currentTurnId,
2431
2374
  state: 'interrupted',
@@ -2439,13 +2382,6 @@ export async function main() {
2439
2382
  if (isActive) {
2440
2383
  await session.query.interrupt();
2441
2384
  }
2442
- if (session.streamingTimer) {
2443
- clearTimeout(session.streamingTimer);
2444
- session.streamingTimer = null;
2445
- }
2446
- session.streamingText = '';
2447
- runtimeState.clearStreaming(convoId).catch(() => { });
2448
- client.setTyping(convoId, false).catch(() => { });
2449
2385
  await rtdbWrite(`/control/${convoId}/${agentId}/signal`, null).catch(() => { });
2450
2386
  }
2451
2387
  catch (err) {
package/dist/server.js CHANGED
@@ -41,7 +41,6 @@ let approvalServer = null;
41
41
  const conversationCache = new Map();
42
42
  const TURN_COMPLETE_METADATA = {
43
43
  turnSemantics: 'turn_complete',
44
- turnComplete: true,
45
44
  };
46
45
  /** Last owner-visible conversation to route approval cards into. */
47
46
  let approvalConversationId = null;
@@ -419,7 +418,6 @@ async function handleInboundMessage(payload) {
419
418
  if (approvalManager) {
420
419
  const consumed = approvalManager.handleMessage(payload.conversationId, {
421
420
  senderId: m.senderId,
422
- text: m.text ?? undefined,
423
421
  metadata: m.metadata,
424
422
  });
425
423
  if (consumed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/claude-code-plugin",
3
- "version": "0.14.11",
3
+ "version": "0.14.13",
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.6.1",
35
- "@canonmsg/core": "^0.23.0",
34
+ "@canonmsg/agent-sdk": "^2.0.0",
35
+ "@canonmsg/core": "^1.0.0",
36
36
  "@modelcontextprotocol/sdk": "^1.12.0"
37
37
  },
38
38
  "engines": {