@canonmsg/claude-code-plugin 0.9.6 → 0.10.0
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 +8 -0
- package/dist/host-runtime.d.ts +8 -2
- package/dist/host-runtime.js +14 -1
- package/dist/host.js +325 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -32,6 +32,14 @@ Send a message to your agent in the Canon app. Claude Code responds with full ca
|
|
|
32
32
|
canon-claude --cwd /path/to/project
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
Advertise multiple project choices to the Canon app:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
canon-claude --cwd ~/dev --workspace ~/dev/canon --workspace ~/dev/yumyumv2
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`--cwd` is the default workspace. Each `--workspace` value appears as a selectable workspace during session creation. Worktree mode creates a per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-workspace mode runs directly in the selected directory.
|
|
42
|
+
|
|
35
43
|
## Multiple agents
|
|
36
44
|
|
|
37
45
|
```bash
|
package/dist/host-runtime.d.ts
CHANGED
|
@@ -98,17 +98,23 @@ export declare function publishHostSessionSnapshots(input: {
|
|
|
98
98
|
liveSessionConfigByConversation?: ReadonlyMap<string, {
|
|
99
99
|
model?: string;
|
|
100
100
|
permissionMode?: string;
|
|
101
|
+
effort?: string;
|
|
102
|
+
runtimeControlValues?: Record<string, string>;
|
|
101
103
|
workspaceId?: string;
|
|
102
104
|
executionMode?: SessionWorkspaceConfig['executionMode'];
|
|
103
105
|
executionBranch?: string | null;
|
|
104
106
|
}>;
|
|
105
107
|
}): Promise<void>;
|
|
106
|
-
export declare function readHostSessionConfig<TExtra extends string = never>(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial<Record<TExtra, string>>
|
|
108
|
+
export declare function readHostSessionConfig<TExtra extends string = never>(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial<Record<TExtra, string>> & {
|
|
109
|
+
runtimeControlValues?: Record<string, string>;
|
|
110
|
+
}) | null;
|
|
107
111
|
export declare function loadHostSessionConfig<TExtra extends string = never>(input: {
|
|
108
112
|
conversationId: string;
|
|
109
113
|
agentId: string;
|
|
110
114
|
extraStringFields?: readonly TExtra[];
|
|
111
|
-
}): Promise<(SessionWorkspaceConfig & Partial<Record<TExtra, string>>
|
|
115
|
+
}): Promise<(SessionWorkspaceConfig & Partial<Record<TExtra, string>> & {
|
|
116
|
+
runtimeControlValues?: Record<string, string>;
|
|
117
|
+
}) | null>;
|
|
112
118
|
export declare function resolveHostWorkspaceCwd(input: {
|
|
113
119
|
workspaceOptions: HostWorkspaceResolverOption[];
|
|
114
120
|
config: {
|
package/dist/host-runtime.js
CHANGED
|
@@ -145,7 +145,7 @@ export async function publishHostSessionSnapshots(input) {
|
|
|
145
145
|
const persistedConfig = await loadHostSessionConfig({
|
|
146
146
|
conversationId,
|
|
147
147
|
agentId: input.agentId,
|
|
148
|
-
extraStringFields: ['permissionMode'],
|
|
148
|
+
extraStringFields: ['permissionMode', 'effort'],
|
|
149
149
|
});
|
|
150
150
|
const liveConfig = input.liveSessionConfigByConversation?.get(conversationId) ?? null;
|
|
151
151
|
const mergedConfig = {
|
|
@@ -163,6 +163,10 @@ export async function publishHostSessionSnapshots(input) {
|
|
|
163
163
|
sessionConfig: {
|
|
164
164
|
...(mergedConfig.model ? { model: mergedConfig.model } : {}),
|
|
165
165
|
...(mergedConfig.permissionMode ? { permissionMode: mergedConfig.permissionMode } : {}),
|
|
166
|
+
...(mergedConfig.effort ? { effort: mergedConfig.effort } : {}),
|
|
167
|
+
...(mergedConfig.runtimeControlValues
|
|
168
|
+
? { runtimeControlValues: mergedConfig.runtimeControlValues }
|
|
169
|
+
: {}),
|
|
166
170
|
...(mergedConfig.workspaceId ? { workspaceId: mergedConfig.workspaceId } : {}),
|
|
167
171
|
...(mergedConfig.executionMode ? { executionMode: mergedConfig.executionMode } : {}),
|
|
168
172
|
},
|
|
@@ -184,6 +188,8 @@ export async function publishHostSessionSnapshots(input) {
|
|
|
184
188
|
hostMode: true,
|
|
185
189
|
model: snapshot.model ?? null,
|
|
186
190
|
permissionMode: snapshot.permissionMode ?? null,
|
|
191
|
+
effort: snapshot.effort ?? null,
|
|
192
|
+
runtimeControlValues: snapshot.runtimeControlValues ?? null,
|
|
187
193
|
workspaceId: snapshot.workspaceId ?? null,
|
|
188
194
|
executionMode: snapshot.executionMode ?? null,
|
|
189
195
|
executionBranch,
|
|
@@ -205,9 +211,16 @@ export function readHostSessionConfig(raw, extraStringFields = []) {
|
|
|
205
211
|
const value = normalizeOptionalString(data[field]);
|
|
206
212
|
return value ? [[field, value]] : [];
|
|
207
213
|
}));
|
|
214
|
+
const runtimeControlValues = Object.fromEntries(Object.entries(data.runtimeControlValues && typeof data.runtimeControlValues === 'object'
|
|
215
|
+
? data.runtimeControlValues
|
|
216
|
+
: {}).flatMap(([key, value]) => {
|
|
217
|
+
const normalizedValue = normalizeOptionalString(value);
|
|
218
|
+
return normalizedValue ? [[key, normalizedValue]] : [];
|
|
219
|
+
}));
|
|
208
220
|
return {
|
|
209
221
|
...(baseConfig ?? {}),
|
|
210
222
|
...extraConfig,
|
|
223
|
+
...(Object.keys(runtimeControlValues).length > 0 ? { runtimeControlValues } : {}),
|
|
211
224
|
};
|
|
212
225
|
}
|
|
213
226
|
export async function loadHostSessionConfig(input) {
|
package/dist/host.js
CHANGED
|
@@ -24,9 +24,9 @@ import { parseArgs } from 'node:util';
|
|
|
24
24
|
import { randomUUID } from 'node:crypto';
|
|
25
25
|
import { execFileSync } from 'node:child_process';
|
|
26
26
|
import { existsSync } from 'node:fs';
|
|
27
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
27
|
+
import { query, } from '@anthropic-ai/claude-agent-sdk';
|
|
28
28
|
import { isAnthropicImageAttachment, materializeMessageMedia, toAnthropicImageBlock, } from '@canonmsg/agent-sdk';
|
|
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';
|
|
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, writeRuntimeInfo, writeSessionState, clearSessionState, writeTurnState, releaseConversationEnvironment, } from '@canonmsg/core';
|
|
30
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';
|
|
@@ -34,6 +34,87 @@ import { runCli } from './cli-entry.js';
|
|
|
34
34
|
let workingDir = process.cwd();
|
|
35
35
|
let workspaceOptions = [];
|
|
36
36
|
let runtimeModels = [];
|
|
37
|
+
const CLAUDE_METADATA_TTL_MS = 5 * 60 * 1000;
|
|
38
|
+
let runtimeMetadata = {
|
|
39
|
+
account: null,
|
|
40
|
+
mcpServers: [],
|
|
41
|
+
plugins: [],
|
|
42
|
+
loadedAt: 0,
|
|
43
|
+
};
|
|
44
|
+
let runtimeMetadataRefresh = null;
|
|
45
|
+
function buildClaudeRuntimeDescriptor(input) {
|
|
46
|
+
return {
|
|
47
|
+
coreControls: [
|
|
48
|
+
{
|
|
49
|
+
id: 'model',
|
|
50
|
+
label: 'Model',
|
|
51
|
+
options: input.models,
|
|
52
|
+
defaultValue: input.models[0]?.value ?? null,
|
|
53
|
+
availability: 'setup_and_live',
|
|
54
|
+
liveBehavior: 'immediate',
|
|
55
|
+
selectionPolicy: 'inherit',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'workspace',
|
|
59
|
+
label: 'Workspace',
|
|
60
|
+
options: input.workspaces.map((workspace) => ({
|
|
61
|
+
value: workspace.id,
|
|
62
|
+
label: workspace.label,
|
|
63
|
+
})),
|
|
64
|
+
defaultValue: input.workspaces[0]?.id ?? null,
|
|
65
|
+
availability: 'setup',
|
|
66
|
+
liveBehavior: 'none',
|
|
67
|
+
selectionPolicy: 'inherit',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'executionMode',
|
|
71
|
+
label: 'Execution mode',
|
|
72
|
+
options: input.executionModes.map((mode) => ({
|
|
73
|
+
value: mode,
|
|
74
|
+
label: mode === 'worktree' ? 'Isolated worktree' : 'Use shared workspace',
|
|
75
|
+
description: mode === 'worktree'
|
|
76
|
+
? 'Creates or reuses a per-conversation git worktree under ~/.canon/conversation-worktrees when the selected workspace is a git repo.'
|
|
77
|
+
: 'Runs directly in the selected workspace. Canon records usage, but does not create a separate checkout.',
|
|
78
|
+
})),
|
|
79
|
+
defaultValue: null,
|
|
80
|
+
availability: 'setup',
|
|
81
|
+
liveBehavior: 'none',
|
|
82
|
+
selectionPolicy: 'required_explicit',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
runtimeControls: [
|
|
86
|
+
{
|
|
87
|
+
id: 'permissionMode',
|
|
88
|
+
label: 'Permission mode',
|
|
89
|
+
options: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
90
|
+
defaultValue: 'default',
|
|
91
|
+
availability: 'setup_and_live',
|
|
92
|
+
liveBehavior: 'immediate',
|
|
93
|
+
selectionPolicy: 'inherit',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'effort',
|
|
97
|
+
label: 'Thinking level',
|
|
98
|
+
options: [
|
|
99
|
+
{ value: 'low', label: 'Low' },
|
|
100
|
+
{ value: 'medium', label: 'Medium' },
|
|
101
|
+
{ value: 'high', label: 'High' },
|
|
102
|
+
],
|
|
103
|
+
defaultValue: 'medium',
|
|
104
|
+
availability: 'setup_and_live',
|
|
105
|
+
liveBehavior: 'immediate',
|
|
106
|
+
selectionPolicy: 'inherit',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
supportsInterrupt: true,
|
|
110
|
+
streamingTextMode: 'delta',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function normalizeClaudeEffortLevel(value) {
|
|
114
|
+
return value === 'low' || value === 'medium' || value === 'high'
|
|
115
|
+
? value
|
|
116
|
+
: undefined;
|
|
117
|
+
}
|
|
37
118
|
let cachedClaudeCliPath;
|
|
38
119
|
// The SDK bundles a Bun-compiled `claude` Mach-O binary next to
|
|
39
120
|
// claude-agent-sdk-darwin-arm64. On some macOS setups that exact binary gets
|
|
@@ -83,11 +164,12 @@ async function loadSessionConfig(conversationId, agentId) {
|
|
|
83
164
|
const config = await loadHostSessionConfig({
|
|
84
165
|
conversationId,
|
|
85
166
|
agentId,
|
|
86
|
-
extraStringFields: ['permissionMode'],
|
|
167
|
+
extraStringFields: ['permissionMode', 'effort'],
|
|
87
168
|
});
|
|
88
169
|
return {
|
|
89
170
|
...config,
|
|
90
171
|
permissionMode: config?.permissionMode,
|
|
172
|
+
effort: normalizeClaudeEffortLevel(config?.effort),
|
|
91
173
|
};
|
|
92
174
|
}
|
|
93
175
|
// Default to 'locked' (shared workspace) when no mode has been picked. The
|
|
@@ -103,6 +185,14 @@ function resolveWorkspaceCwd(config) {
|
|
|
103
185
|
defaultCwd: workingDir,
|
|
104
186
|
});
|
|
105
187
|
}
|
|
188
|
+
function resolveExecutionFallbackReason(environment) {
|
|
189
|
+
if (!environment?.reason || environment.mode !== 'locked') {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return environment.reason === 'Sharing the base workspace (locked mode)'
|
|
193
|
+
? null
|
|
194
|
+
: environment.reason;
|
|
195
|
+
}
|
|
106
196
|
async function detectRuntimeModels(cwd) {
|
|
107
197
|
const emptyPrompt = {
|
|
108
198
|
async *[Symbol.asyncIterator]() { },
|
|
@@ -130,6 +220,166 @@ async function detectRuntimeModels(cwd) {
|
|
|
130
220
|
catch { }
|
|
131
221
|
}
|
|
132
222
|
}
|
|
223
|
+
async function inspectClaudeRuntimeMetadata(cwd, activeQuery) {
|
|
224
|
+
const emptyPrompt = {
|
|
225
|
+
async *[Symbol.asyncIterator]() { },
|
|
226
|
+
};
|
|
227
|
+
const claudeCliPath = resolveClaudeCliPath();
|
|
228
|
+
const probe = activeQuery ?? query({
|
|
229
|
+
prompt: emptyPrompt,
|
|
230
|
+
options: {
|
|
231
|
+
systemPrompt: { type: 'preset', preset: 'claude_code' },
|
|
232
|
+
tools: { type: 'preset', preset: 'claude_code' },
|
|
233
|
+
permissionMode: 'default',
|
|
234
|
+
cwd,
|
|
235
|
+
settings: { forceLoginMethod: 'claudeai' },
|
|
236
|
+
...(claudeCliPath ? { pathToClaudeCodeExecutable: claudeCliPath } : {}),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
try {
|
|
240
|
+
const [init, mcpServers, pluginInfo] = await Promise.all([
|
|
241
|
+
probe.initializationResult(),
|
|
242
|
+
probe.mcpServerStatus().catch(() => []),
|
|
243
|
+
probe.reloadPlugins().catch(() => null),
|
|
244
|
+
]);
|
|
245
|
+
return {
|
|
246
|
+
account: init.account ?? null,
|
|
247
|
+
mcpServers: pluginInfo?.mcpServers ?? mcpServers,
|
|
248
|
+
plugins: pluginInfo?.plugins ?? [],
|
|
249
|
+
loadedAt: Date.now(),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
if (!activeQuery) {
|
|
254
|
+
try {
|
|
255
|
+
probe.close();
|
|
256
|
+
}
|
|
257
|
+
catch { }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function refreshClaudeRuntimeMetadata(input) {
|
|
262
|
+
if (!input.force
|
|
263
|
+
&& runtimeMetadata.loadedAt > 0
|
|
264
|
+
&& Date.now() - runtimeMetadata.loadedAt < CLAUDE_METADATA_TTL_MS) {
|
|
265
|
+
return runtimeMetadata;
|
|
266
|
+
}
|
|
267
|
+
if (runtimeMetadataRefresh) {
|
|
268
|
+
return runtimeMetadataRefresh;
|
|
269
|
+
}
|
|
270
|
+
runtimeMetadataRefresh = inspectClaudeRuntimeMetadata(input.cwd, input.activeQuery ?? null)
|
|
271
|
+
.then((metadata) => {
|
|
272
|
+
runtimeMetadata = metadata;
|
|
273
|
+
return metadata;
|
|
274
|
+
})
|
|
275
|
+
.finally(() => {
|
|
276
|
+
runtimeMetadataRefresh = null;
|
|
277
|
+
});
|
|
278
|
+
return runtimeMetadataRefresh;
|
|
279
|
+
}
|
|
280
|
+
function mapClaudeMcpStatus(status) {
|
|
281
|
+
switch (status) {
|
|
282
|
+
case 'connected':
|
|
283
|
+
return 'ready';
|
|
284
|
+
case 'needs-auth':
|
|
285
|
+
return 'auth_needed';
|
|
286
|
+
case 'disabled':
|
|
287
|
+
return 'configured';
|
|
288
|
+
case 'failed':
|
|
289
|
+
return 'error';
|
|
290
|
+
default:
|
|
291
|
+
return 'unknown';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function buildClaudeRuntimeInfo(input) {
|
|
295
|
+
const { descriptor, metadata } = input;
|
|
296
|
+
const statusItems = [
|
|
297
|
+
metadata.account?.email
|
|
298
|
+
? {
|
|
299
|
+
id: 'account',
|
|
300
|
+
label: 'Account',
|
|
301
|
+
value: metadata.account.email,
|
|
302
|
+
tone: 'success',
|
|
303
|
+
}
|
|
304
|
+
: null,
|
|
305
|
+
metadata.account?.subscriptionType
|
|
306
|
+
? {
|
|
307
|
+
id: 'subscription',
|
|
308
|
+
label: 'Plan',
|
|
309
|
+
value: metadata.account.subscriptionType,
|
|
310
|
+
}
|
|
311
|
+
: null,
|
|
312
|
+
metadata.account?.apiProvider
|
|
313
|
+
? {
|
|
314
|
+
id: 'provider',
|
|
315
|
+
label: 'Provider',
|
|
316
|
+
value: metadata.account.apiProvider,
|
|
317
|
+
}
|
|
318
|
+
: null,
|
|
319
|
+
metadata.account?.tokenSource || metadata.account?.apiKeySource
|
|
320
|
+
? {
|
|
321
|
+
id: 'auth',
|
|
322
|
+
label: 'Auth',
|
|
323
|
+
value: metadata.account.tokenSource ?? metadata.account?.apiKeySource ?? 'connected',
|
|
324
|
+
}
|
|
325
|
+
: null,
|
|
326
|
+
runtimeModels[0]
|
|
327
|
+
? {
|
|
328
|
+
id: 'fallbackModel',
|
|
329
|
+
label: 'Fallback model',
|
|
330
|
+
value: runtimeModels[0].label,
|
|
331
|
+
}
|
|
332
|
+
: null,
|
|
333
|
+
{
|
|
334
|
+
id: 'toolPolicy',
|
|
335
|
+
label: 'Tool policy',
|
|
336
|
+
value: 'Claude Code preset',
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: 'browser',
|
|
340
|
+
label: 'Browser tools',
|
|
341
|
+
value: 'Runtime policy',
|
|
342
|
+
},
|
|
343
|
+
].filter((item) => Boolean(item));
|
|
344
|
+
return {
|
|
345
|
+
descriptor,
|
|
346
|
+
surfaceMode: 'host',
|
|
347
|
+
statusItems,
|
|
348
|
+
inventories: [
|
|
349
|
+
{
|
|
350
|
+
id: 'mcp',
|
|
351
|
+
label: 'MCP',
|
|
352
|
+
entries: metadata.mcpServers.map((server) => ({
|
|
353
|
+
id: server.name,
|
|
354
|
+
label: server.name,
|
|
355
|
+
status: mapClaudeMcpStatus(server.status),
|
|
356
|
+
description: server.error
|
|
357
|
+
?? server.serverInfo?.version
|
|
358
|
+
?? server.scope
|
|
359
|
+
?? undefined,
|
|
360
|
+
})),
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: 'plugins',
|
|
364
|
+
label: 'Plugins',
|
|
365
|
+
entries: metadata.plugins.map((plugin) => ({
|
|
366
|
+
id: plugin.name,
|
|
367
|
+
label: plugin.name,
|
|
368
|
+
status: 'configured',
|
|
369
|
+
description: plugin.source ?? plugin.path,
|
|
370
|
+
})),
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
execution: {
|
|
374
|
+
resolvedWorkspaceLabel: input.workspaceLabel,
|
|
375
|
+
resolvedCwd: input.resolvedCwd,
|
|
376
|
+
executionMode: input.executionMode,
|
|
377
|
+
executionBranch: input.executionBranch,
|
|
378
|
+
worktreePath: input.worktreePath,
|
|
379
|
+
fallbackReason: input.fallbackReason,
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
133
383
|
// ── Constants ───────────────────────────────────────────────────────
|
|
134
384
|
const MAX_SESSIONS = 5;
|
|
135
385
|
const IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
@@ -468,6 +718,7 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
468
718
|
cwd,
|
|
469
719
|
executionMode: session.environment.mode,
|
|
470
720
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
721
|
+
...(session.environment.worktreePath ? { worktreePath: session.environment.worktreePath } : {}),
|
|
471
722
|
hostMode: true,
|
|
472
723
|
isActive: true,
|
|
473
724
|
}).catch(() => { });
|
|
@@ -668,6 +919,15 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
668
919
|
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Failed to set initial model:`, err);
|
|
669
920
|
}
|
|
670
921
|
}
|
|
922
|
+
if (config.effort) {
|
|
923
|
+
try {
|
|
924
|
+
await q.applyFlagSettings({ effortLevel: config.effort });
|
|
925
|
+
session.state.effort = config.effort;
|
|
926
|
+
}
|
|
927
|
+
catch (err) {
|
|
928
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Failed to set initial effort:`, err);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
671
931
|
if (!resumeSessionId) {
|
|
672
932
|
// New session — send seed message to activate streaming input mode
|
|
673
933
|
sendInput({
|
|
@@ -699,6 +959,7 @@ function createSession(conversationId, environment, agentId, client, onSessionEn
|
|
|
699
959
|
cwd,
|
|
700
960
|
executionMode: session.environment.mode,
|
|
701
961
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
962
|
+
...(session.environment.worktreePath ? { worktreePath: session.environment.worktreePath } : {}),
|
|
702
963
|
hostMode: true,
|
|
703
964
|
isActive: true,
|
|
704
965
|
}).catch(() => { });
|
|
@@ -759,6 +1020,11 @@ export async function main() {
|
|
|
759
1020
|
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
760
1021
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
761
1022
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1023
|
+
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1024
|
+
models: runtimeModels,
|
|
1025
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1026
|
+
executionModes: hostAvailableExecutionModes,
|
|
1027
|
+
}),
|
|
762
1028
|
};
|
|
763
1029
|
const conversationCache = new Map();
|
|
764
1030
|
const knownConversationIds = new Set();
|
|
@@ -769,6 +1035,14 @@ export async function main() {
|
|
|
769
1035
|
await refreshKnownConversationIds().catch((error) => {
|
|
770
1036
|
console.error('[canon-host] Failed to refresh known conversations:', error);
|
|
771
1037
|
});
|
|
1038
|
+
const activeSessionQuery = sessions.values().next().value?.query ?? null;
|
|
1039
|
+
const metadata = await refreshClaudeRuntimeMetadata({
|
|
1040
|
+
cwd: workingDir,
|
|
1041
|
+
activeQuery: activeSessionQuery,
|
|
1042
|
+
}).catch((error) => {
|
|
1043
|
+
console.error('[canon-host] Failed to refresh Claude runtime metadata:', error);
|
|
1044
|
+
return runtimeMetadata;
|
|
1045
|
+
});
|
|
772
1046
|
await publishAgentRuntime(agentId, runtimeDescriptor).catch((error) => {
|
|
773
1047
|
console.error('[canon-host] Failed to publish agent runtime:', error);
|
|
774
1048
|
});
|
|
@@ -786,6 +1060,7 @@ export async function main() {
|
|
|
786
1060
|
{
|
|
787
1061
|
...(session.state.model ? { model: session.state.model } : {}),
|
|
788
1062
|
...(session.state.permissionMode ? { permissionMode: session.state.permissionMode } : {}),
|
|
1063
|
+
...(session.state.effort ? { effort: session.state.effort } : {}),
|
|
789
1064
|
...(workspaceId ? { workspaceId } : {}),
|
|
790
1065
|
executionMode: session.environment.mode,
|
|
791
1066
|
executionBranch: session.environment.branch ?? null,
|
|
@@ -795,6 +1070,30 @@ export async function main() {
|
|
|
795
1070
|
}).catch((error) => {
|
|
796
1071
|
console.error('[canon-host] Failed to publish session snapshots:', error);
|
|
797
1072
|
});
|
|
1073
|
+
await Promise.all(Array.from(knownConversationIds).map(async (conversationId) => {
|
|
1074
|
+
const session = sessions.get(conversationId);
|
|
1075
|
+
const workspaceId = session
|
|
1076
|
+
? resolveWorkspaceIdForBaseCwd(session.environment.baseCwd)
|
|
1077
|
+
: runtimeDescriptor.defaultWorkspaceId;
|
|
1078
|
+
const workspace = workspaceOptions.find((option) => option.id === workspaceId) ?? null;
|
|
1079
|
+
const payload = buildClaudeRuntimeInfo({
|
|
1080
|
+
descriptor: runtimeDescriptor.runtimeDescriptor ?? buildClaudeRuntimeDescriptor({
|
|
1081
|
+
models: runtimeModels,
|
|
1082
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1083
|
+
executionModes: hostAvailableExecutionModes,
|
|
1084
|
+
}),
|
|
1085
|
+
metadata,
|
|
1086
|
+
workspaceLabel: workspace?.label ?? workspaceId ?? null,
|
|
1087
|
+
resolvedCwd: session?.cwd ?? workspace?.cwd ?? workingDir,
|
|
1088
|
+
executionMode: session?.environment.mode ?? null,
|
|
1089
|
+
executionBranch: session?.environment.branch ?? null,
|
|
1090
|
+
worktreePath: session?.environment.worktreePath ?? null,
|
|
1091
|
+
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
1092
|
+
});
|
|
1093
|
+
await writeRuntimeInfo(conversationId, agentId, payload);
|
|
1094
|
+
})).catch((error) => {
|
|
1095
|
+
console.error('[canon-host] Failed to publish runtime info:', error);
|
|
1096
|
+
});
|
|
798
1097
|
};
|
|
799
1098
|
// ── Load conversations (informational — sessions created on demand) ──
|
|
800
1099
|
try {
|
|
@@ -807,6 +1106,11 @@ export async function main() {
|
|
|
807
1106
|
availableModels: runtimeModels,
|
|
808
1107
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
809
1108
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1109
|
+
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1110
|
+
models: runtimeModels,
|
|
1111
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1112
|
+
executionModes: hostAvailableExecutionModes,
|
|
1113
|
+
}),
|
|
810
1114
|
};
|
|
811
1115
|
}
|
|
812
1116
|
catch {
|
|
@@ -816,6 +1120,11 @@ export async function main() {
|
|
|
816
1120
|
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
817
1121
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
818
1122
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1123
|
+
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1124
|
+
models: runtimeModels,
|
|
1125
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1126
|
+
executionModes: hostAvailableExecutionModes,
|
|
1127
|
+
}),
|
|
819
1128
|
};
|
|
820
1129
|
}
|
|
821
1130
|
try {
|
|
@@ -958,6 +1267,7 @@ export async function main() {
|
|
|
958
1267
|
}, {
|
|
959
1268
|
model: config?.model ?? runtimeModels[0]?.value,
|
|
960
1269
|
permissionMode: config?.permissionMode ?? 'default',
|
|
1270
|
+
effort: config?.effort,
|
|
961
1271
|
availableModels: runtimeModels,
|
|
962
1272
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
963
1273
|
}, resumeId);
|
|
@@ -1139,12 +1449,16 @@ export async function main() {
|
|
|
1139
1449
|
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to set mode:`, err);
|
|
1140
1450
|
}
|
|
1141
1451
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
effortLevel:
|
|
1146
|
-
|
|
1147
|
-
|
|
1452
|
+
const nextEffort = normalizeClaudeEffortLevel(ctrl.effort);
|
|
1453
|
+
if (nextEffort && nextEffort !== session.state.effort) {
|
|
1454
|
+
try {
|
|
1455
|
+
await session.query.applyFlagSettings({ effortLevel: nextEffort });
|
|
1456
|
+
session.state.effort = nextEffort;
|
|
1457
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Effort → ${nextEffort}`);
|
|
1458
|
+
}
|
|
1459
|
+
catch (err) {
|
|
1460
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to set effort:`, err);
|
|
1461
|
+
}
|
|
1148
1462
|
}
|
|
1149
1463
|
writeSessionState(convoId, agentId, {
|
|
1150
1464
|
...session.state,
|
|
@@ -1152,6 +1466,7 @@ export async function main() {
|
|
|
1152
1466
|
cwd: session.cwd,
|
|
1153
1467
|
executionMode: session.environment.mode,
|
|
1154
1468
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
1469
|
+
...(session.environment.worktreePath ? { worktreePath: session.environment.worktreePath } : {}),
|
|
1155
1470
|
hostMode: true,
|
|
1156
1471
|
isActive: true,
|
|
1157
1472
|
}).catch(() => { });
|
|
@@ -1213,6 +1528,7 @@ export async function main() {
|
|
|
1213
1528
|
cwd: session.cwd,
|
|
1214
1529
|
executionMode: session.environment.mode,
|
|
1215
1530
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
1531
|
+
...(session.environment.worktreePath ? { worktreePath: session.environment.worktreePath } : {}),
|
|
1216
1532
|
hostMode: true,
|
|
1217
1533
|
isActive: true,
|
|
1218
1534
|
}).catch(() => { });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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": "^0.
|
|
35
|
-
"@canonmsg/core": "^0.
|
|
34
|
+
"@canonmsg/agent-sdk": "^0.9.0",
|
|
35
|
+
"@canonmsg/core": "^0.8.0",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|