@canonmsg/claude-code-plugin 0.14.1 → 0.14.3

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/README.md CHANGED
@@ -48,7 +48,7 @@ Advertise multiple project choices to the Canon app:
48
48
  canon-claude --cwd ~/dev --workspace-root ~/dev
49
49
  ```
50
50
 
51
- `--cwd` is the default workspace. Each `--workspace-root` value is an approved local root; the host discovers immediate child projects with common markers such as `.git`, `package.json`, `pyproject.toml`, `Cargo.toml`, or `go.mod` and publishes them as selectable projects during session creation. Use repeated `--workspace /path/to/project` entries to advertise specific projects outside those roots. Worktree mode creates a per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-project mode runs directly in the selected directory.
51
+ `--cwd` is the default workspace. Each `--workspace-root` value is an approved local root; the host discovers immediate child projects with common markers such as `.git`, `package.json`, `pyproject.toml`, `Cargo.toml`, or `go.mod` and publishes them as selectable projects during session creation. Use repeated `--workspace /path/to/project` entries to advertise specific projects outside those roots. Worktree mode creates a best-effort per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-project mode runs directly in the selected directory.
52
52
 
53
53
  Current Canon truth for Claude host mode:
54
54
 
@@ -56,7 +56,7 @@ Current Canon truth for Claude host mode:
56
56
  - thinking level / effort is live-editable when the host reports it
57
57
  - workspace selection is setup-only
58
58
  - execution mode selection is setup-only
59
- - permission modes are rendered exactly as the Claude runtime reports them; Canon does not flatten them to a smaller hardcoded set
59
+ - permission modes are the known Claude Code modes supported by this host; Claude Code applies and enforces the selected mode
60
60
  - if worktree creation is unavailable for the selected project, Canon may fall back to shared-project execution and surface the fallback reason in session details
61
61
 
62
62
  ## Multiple agents
package/dist/host.js CHANGED
@@ -25,7 +25,7 @@ import { randomUUID } from 'node:crypto';
25
25
  import { execFileSync } from 'node:child_process';
26
26
  import { existsSync } from 'node:fs';
27
27
  import { query, } from '@anthropic-ai/claude-agent-sdk';
28
- import { isAnthropicImageAttachment, materializeMessageMedia, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
28
+ import { isAnthropicImageAttachment, materializeMessageMedia, materializeReplyContextMedia, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
29
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, createConversationMetadataLoader, createRuntimeStatePublisher, EXECUTION_ENVIRONMENT_MODES, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, 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
31
  import { createStreamingBuffer } from './streaming-buffer.js';
@@ -618,6 +618,21 @@ function buildClaudeSessionContextLines(session) {
618
618
  function renderInboundContent(message, materialized) {
619
619
  return renderCanonHostInboundContent(message, materialized);
620
620
  }
621
+ async function materializePromptReplyContext(input) {
622
+ if (!input.replyContext?.found || !input.replyContext.attachments?.length) {
623
+ return { replyContext: input.replyContext, materialized: [] };
624
+ }
625
+ try {
626
+ return await materializeReplyContextMedia(input.replyContext, {
627
+ agentId: input.agentId,
628
+ conversationId: input.conversationId,
629
+ });
630
+ }
631
+ catch (error) {
632
+ console.error(`${input.logPrefix} Failed to materialize replied-to media:`, error instanceof Error ? error.message : error);
633
+ return { replyContext: input.replyContext, materialized: [] };
634
+ }
635
+ }
621
636
  /**
622
637
  * Build the multimodal `content` payload handed to the Claude Code SDK.
623
638
  *
@@ -1945,6 +1960,13 @@ export async function main() {
1945
1960
  const behavior = input.hydratedPage?.behavior ?? hydrated.behavior;
1946
1961
  const activeSelfContextId = hydrated.activeSelfContextId;
1947
1962
  const selfContexts = hydrated.selfContexts;
1963
+ const replyMedia = await materializePromptReplyContext({
1964
+ replyContext: hydrated.replyContext,
1965
+ agentId,
1966
+ conversationId: input.conversationId,
1967
+ logPrefix: `[canon-host] [${input.conversationId.slice(0, 8)}]`,
1968
+ });
1969
+ const replyContext = replyMedia.replyContext;
1948
1970
  const participantContext = hydrated.participantContext;
1949
1971
  const autoReply = decideAutoReply(participantContext, behavior);
1950
1972
  if (!autoReply.allow) {
@@ -1980,9 +2002,13 @@ export async function main() {
1980
2002
  participantContext,
1981
2003
  behavior,
1982
2004
  selfContexts,
2005
+ replyContext,
1983
2006
  session,
1984
2007
  });
1985
- const messageContent = await buildCanonUserContent({ promptText, materialized });
2008
+ const messageContent = await buildCanonUserContent({
2009
+ promptText,
2010
+ materialized: [...replyMedia.materialized, ...materialized],
2011
+ });
1986
2012
  session.enqueueInbound({
1987
2013
  type: 'user',
1988
2014
  message: {
@@ -2095,6 +2121,13 @@ export async function main() {
2095
2121
  const behavior = payload.behavior ?? hydrated.behavior;
2096
2122
  const activeSelfContextId = hydrated.activeSelfContextId;
2097
2123
  const selfContexts = hydrated.selfContexts;
2124
+ const replyMedia = await materializePromptReplyContext({
2125
+ replyContext: hydrated.replyContext,
2126
+ agentId,
2127
+ conversationId: convoId,
2128
+ logPrefix: `[canon-host] [${convoId.slice(0, 8)}]`,
2129
+ });
2130
+ const replyContext = replyMedia.replyContext;
2098
2131
  const participantContext = hydrated.participantContext;
2099
2132
  const autoReply = decideAutoReply(participantContext, behavior);
2100
2133
  if (!autoReply.allow) {
@@ -2131,9 +2164,13 @@ export async function main() {
2131
2164
  participantContext,
2132
2165
  behavior,
2133
2166
  selfContexts,
2167
+ replyContext,
2134
2168
  session,
2135
2169
  });
2136
- const messageContent = await buildCanonUserContent({ promptText, materialized });
2170
+ const messageContent = await buildCanonUserContent({
2171
+ promptText,
2172
+ materialized: [...replyMedia.materialized, ...materialized],
2173
+ });
2137
2174
  session.enqueueInbound({
2138
2175
  type: 'user',
2139
2176
  message: {
package/dist/server.js CHANGED
@@ -476,6 +476,9 @@ async function handleInboundMessage(payload) {
476
476
  }
477
477
  }
478
478
  const primaryAttachment = getPrimaryAttachment(m);
479
+ if (m.attachments?.length) {
480
+ meta.attachments_json = JSON.stringify(m.attachments);
481
+ }
479
482
  if (primaryAttachment?.url) {
480
483
  meta.attachment_kind = primaryAttachment.kind;
481
484
  meta.attachment_url = primaryAttachment.url;
@@ -489,6 +492,11 @@ async function handleInboundMessage(payload) {
489
492
  const primaryMaterialized = materialized.find((attachment) => attachment.index === 0);
490
493
  if (primaryMaterialized?.path)
491
494
  meta.attachment_path = primaryMaterialized.path;
495
+ if (materialized.length > 0) {
496
+ meta.attachment_paths = materialized
497
+ .map((attachment) => attachment.path)
498
+ .join('\n');
499
+ }
492
500
  const renderedContent = renderInboundContent(m, materialized);
493
501
  console.error(`[canon] Inbound message from ${meta.sender_name}: "${renderedContent}"`);
494
502
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/claude-code-plugin",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
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.3.1",
35
- "@canonmsg/core": "^0.17.2",
34
+ "@canonmsg/agent-sdk": "^1.4.1",
35
+ "@canonmsg/core": "^0.18.1",
36
36
  "@modelcontextprotocol/sdk": "^1.12.0"
37
37
  },
38
38
  "engines": {