@canonmsg/claude-code-plugin 0.20.0 → 0.21.1
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 +194 -141
- package/dist/session-defaults.d.ts +35 -0
- package/dist/session-defaults.js +124 -0
- package/dist/session-state.d.ts +46 -4
- package/dist/session-state.js +173 -17
- package/dist/subagent-activity.d.ts +11 -0
- package/dist/subagent-activity.js +51 -0
- package/package.json +4 -4
package/dist/host.js
CHANGED
|
@@ -36,7 +36,9 @@ import { ApprovalManager, readHostSessionConfig, resolveHostWorkspaceCwd, } from
|
|
|
36
36
|
import { runCli } from './cli-entry.js';
|
|
37
37
|
import { synthesizeClaudeApprovalDiff } from './approval-diff.js';
|
|
38
38
|
import { decideClaudeToolPermissionForMode, parseAllowedNonOwnerClaudeTools, } from './tool-policy.js';
|
|
39
|
-
import { buildClaudeFinalMessageId, createClaudeInputEnvelope, formatClaudeControlError,
|
|
39
|
+
import { applyClaudeSessionControl, buildClaudeFinalMessageId, buildClaudeInitialSessionState, createClaudeInputEnvelope, formatClaudeControlError, normalizeClaudeEffortLevel, parseClaudePermissionMode, resolveClaudeModelOptions, resetClaudeCompletedTurnState, shouldDeliverClaudeFinal, } from './session-state.js';
|
|
40
|
+
import { claudeThinkingToConfig, parseClaudeSessionDefaults, resolveEffectiveSessionSettings, } from './session-defaults.js';
|
|
41
|
+
import { mapClaudeSubagentActivity } from './subagent-activity.js';
|
|
40
42
|
function parseRuntimeVisibilityPreset(value) {
|
|
41
43
|
return value === 'normal' || value === 'minimal' || value === 'full' ? value : undefined;
|
|
42
44
|
}
|
|
@@ -87,6 +89,29 @@ let workspaceRootMetadata = [];
|
|
|
87
89
|
let runtimeModels = [];
|
|
88
90
|
const CLAUDE_METADATA_TTL_MS = 5 * 60 * 1000;
|
|
89
91
|
const allowedNonOwnerClaudeTools = parseAllowedNonOwnerClaudeTools(process.env.CANON_CLAUDE_NON_OWNER_ALLOWED_TOOLS);
|
|
92
|
+
const CLAUDE_THINKING_CONTROL_OPTIONS = [
|
|
93
|
+
{
|
|
94
|
+
value: 'adaptive',
|
|
95
|
+
label: 'Adaptive',
|
|
96
|
+
description: 'Let Claude decide when and how much to think.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
value: 'off',
|
|
100
|
+
label: 'Off',
|
|
101
|
+
description: 'Disable extended thinking for this session.',
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
const CLAUDE_ULTRACODE_CONTROL_OPTIONS = [
|
|
105
|
+
{
|
|
106
|
+
value: 'off',
|
|
107
|
+
label: 'Off',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
value: 'on',
|
|
111
|
+
label: 'On',
|
|
112
|
+
description: 'Enable Claude Code ultracode mode when the selected model supports it.',
|
|
113
|
+
},
|
|
114
|
+
];
|
|
90
115
|
let runtimeMetadata = {
|
|
91
116
|
account: null,
|
|
92
117
|
mcpServers: [],
|
|
@@ -211,15 +236,40 @@ function buildClaudeRuntimeDescriptor(input) {
|
|
|
211
236
|
RUNTIME_STOP_AND_DROP_ACTION,
|
|
212
237
|
];
|
|
213
238
|
commands.push(...buildNativeSlashCommandDescriptors(runtimeMetadata.commands, new Set(commands.flatMap((command) => [command.id, ...(command.aliases ?? [])]))));
|
|
239
|
+
const extraRuntimeControls = [
|
|
240
|
+
{
|
|
241
|
+
id: 'thinking',
|
|
242
|
+
label: 'Thinking',
|
|
243
|
+
description: 'Claude SDK thinking mode. Mid-session changes toggle thinking on or off; display/budget stay session-start scoped.',
|
|
244
|
+
options: CLAUDE_THINKING_CONTROL_OPTIONS,
|
|
245
|
+
defaultValue: input.defaults?.thinking ?? 'adaptive',
|
|
246
|
+
availability: 'setup_and_live',
|
|
247
|
+
liveBehavior: 'immediate',
|
|
248
|
+
selectionPolicy: 'inherit',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'ultracode',
|
|
252
|
+
label: 'Ultracode',
|
|
253
|
+
description: 'Enable Claude Code ultracode mode when the selected model supports it.',
|
|
254
|
+
options: CLAUDE_ULTRACODE_CONTROL_OPTIONS,
|
|
255
|
+
defaultValue: input.defaults?.ultracode ?? 'off',
|
|
256
|
+
availability: 'setup_and_live',
|
|
257
|
+
liveBehavior: 'immediate',
|
|
258
|
+
selectionPolicy: 'inherit',
|
|
259
|
+
},
|
|
260
|
+
];
|
|
214
261
|
return buildFirstPartyCodingRuntimeDescriptor({
|
|
215
262
|
clientType: 'claude-code',
|
|
216
263
|
models: input.models,
|
|
264
|
+
defaultModel: input.defaults?.model ?? input.models[0]?.value ?? null,
|
|
217
265
|
workspaces: input.workspaces,
|
|
218
266
|
workspaceRoots: input.workspaceRoots,
|
|
219
267
|
executionModes: input.executionModes,
|
|
220
268
|
permissionModes: CLAUDE_PERMISSION_MODE_OPTIONS,
|
|
221
269
|
defaultPermissionMode: 'default',
|
|
222
270
|
effortOptions: [...CLAUDE_EFFORT_OPTIONS],
|
|
271
|
+
defaultEffort: input.defaults?.effort,
|
|
272
|
+
extraRuntimeControls,
|
|
223
273
|
streamingTextMode: 'delta',
|
|
224
274
|
turnModes: [
|
|
225
275
|
{
|
|
@@ -245,26 +295,6 @@ function buildClaudeRuntimeDescriptor(input) {
|
|
|
245
295
|
commands,
|
|
246
296
|
});
|
|
247
297
|
}
|
|
248
|
-
const CLAUDE_EFFORT_VALUES = new Set(CLAUDE_EFFORT_OPTIONS.map((option) => option.value));
|
|
249
|
-
function parseClaudePermissionMode(value) {
|
|
250
|
-
if (value === 'acceptEdits'
|
|
251
|
-
|| value === 'auto'
|
|
252
|
-
|| value === 'bypassPermissions'
|
|
253
|
-
|| value === 'default'
|
|
254
|
-
|| value === 'dontAsk'
|
|
255
|
-
|| value === 'plan') {
|
|
256
|
-
return value;
|
|
257
|
-
}
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
function normalizeClaudeEffortLevel(value) {
|
|
261
|
-
return typeof value === 'string' && CLAUDE_EFFORT_VALUES.has(value)
|
|
262
|
-
? value
|
|
263
|
-
: undefined;
|
|
264
|
-
}
|
|
265
|
-
async function applyClaudeEffortLevel(q, effort) {
|
|
266
|
-
await q.applyFlagSettings({ effortLevel: effort });
|
|
267
|
-
}
|
|
268
298
|
let cachedClaudeCliPath;
|
|
269
299
|
// The SDK bundles a Bun-compiled `claude` Mach-O binary next to
|
|
270
300
|
// claude-agent-sdk-darwin-arm64. On some macOS setups that exact binary gets
|
|
@@ -354,6 +384,7 @@ async function detectRuntimeModels(cwd) {
|
|
|
354
384
|
options: {
|
|
355
385
|
systemPrompt: { type: 'preset', preset: 'claude_code' },
|
|
356
386
|
tools: { type: 'preset', preset: 'claude_code' },
|
|
387
|
+
model: 'default',
|
|
357
388
|
permissionMode: 'default',
|
|
358
389
|
cwd,
|
|
359
390
|
settings: { forceLoginMethod: 'claudeai' },
|
|
@@ -381,6 +412,7 @@ async function inspectClaudeRuntimeMetadata(cwd, activeQuery) {
|
|
|
381
412
|
options: {
|
|
382
413
|
systemPrompt: { type: 'preset', preset: 'claude_code' },
|
|
383
414
|
tools: { type: 'preset', preset: 'claude_code' },
|
|
415
|
+
model: 'default',
|
|
384
416
|
permissionMode: 'default',
|
|
385
417
|
cwd,
|
|
386
418
|
settings: { forceLoginMethod: 'claudeai' },
|
|
@@ -561,13 +593,22 @@ function buildClaudeRuntimeInfo(input) {
|
|
|
561
593
|
tier: 'diagnostic',
|
|
562
594
|
}
|
|
563
595
|
: null,
|
|
564
|
-
|
|
596
|
+
input.fallbackModel
|
|
565
597
|
? {
|
|
566
598
|
id: 'fallbackModel',
|
|
567
599
|
label: 'Fallback model',
|
|
568
|
-
value:
|
|
600
|
+
value: input.fallbackModel,
|
|
569
601
|
tier: 'diagnostic',
|
|
570
|
-
source: '
|
|
602
|
+
source: 'applied',
|
|
603
|
+
}
|
|
604
|
+
: null,
|
|
605
|
+
input.ultracodeEnabled
|
|
606
|
+
? {
|
|
607
|
+
id: 'ultracode',
|
|
608
|
+
label: 'Ultracode',
|
|
609
|
+
value: 'Enabled',
|
|
610
|
+
tier: 'detail',
|
|
611
|
+
source: 'applied',
|
|
571
612
|
}
|
|
572
613
|
: null,
|
|
573
614
|
{
|
|
@@ -808,16 +849,27 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
808
849
|
};
|
|
809
850
|
}
|
|
810
851
|
const claudeCliPath = resolveClaudeCliPath();
|
|
852
|
+
const initialThinkingConfig = claudeThinkingToConfig(config.sessionSettings.thinking);
|
|
811
853
|
const queryOptions = {
|
|
812
854
|
systemPrompt: { type: 'preset', preset: 'claude_code' },
|
|
813
855
|
tools: { type: 'preset', preset: 'claude_code' },
|
|
856
|
+
model: config.model ?? 'default',
|
|
814
857
|
permissionMode: parseClaudePermissionMode(config.permissionMode) ?? 'default',
|
|
815
858
|
allowDangerouslySkipPermissions: true,
|
|
816
859
|
cwd,
|
|
817
860
|
includePartialMessages: true,
|
|
818
861
|
maxTurns: 200,
|
|
819
862
|
...(config.effort ? { effort: config.effort } : {}),
|
|
820
|
-
|
|
863
|
+
...(initialThinkingConfig ? { thinking: initialThinkingConfig } : {}),
|
|
864
|
+
...(config.sessionSettings.fallbackModel ? { fallbackModel: config.sessionSettings.fallbackModel } : {}),
|
|
865
|
+
agentProgressSummaries: config.sessionSettings.agentProgressSummaries ?? true,
|
|
866
|
+
...(config.sessionSettings.agents ? { agents: config.sessionSettings.agents } : {}),
|
|
867
|
+
settings: {
|
|
868
|
+
forceLoginMethod: 'claudeai',
|
|
869
|
+
...(config.sessionSettings.ultracode
|
|
870
|
+
? { ultracode: config.sessionSettings.ultracode === 'on' }
|
|
871
|
+
: {}),
|
|
872
|
+
},
|
|
821
873
|
settingSources: ['project', 'local'],
|
|
822
874
|
...(claudeCliPath ? { pathToClaudeCodeExecutable: claudeCliPath } : {}),
|
|
823
875
|
stderr: (data) => {
|
|
@@ -1023,14 +1075,16 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1023
1075
|
cwd,
|
|
1024
1076
|
environment,
|
|
1025
1077
|
query: q,
|
|
1078
|
+
controlApplySeq: new Map(),
|
|
1026
1079
|
sendInput,
|
|
1027
1080
|
enqueueInbound: (msg, intent = 'queue', sourceMessageId, markAccepted = false, isOwnerTurn = false, artifactRoutingMode = 'disabled') => enqueueInboundMessage(msg, intent, sourceMessageId, markAccepted, isOwnerTurn, artifactRoutingMode),
|
|
1028
|
-
state: {
|
|
1029
|
-
model:
|
|
1081
|
+
state: buildClaudeInitialSessionState({
|
|
1082
|
+
model: config.model,
|
|
1030
1083
|
permissionMode: config.permissionMode,
|
|
1031
|
-
effort:
|
|
1032
|
-
|
|
1033
|
-
|
|
1084
|
+
effort: config.effort,
|
|
1085
|
+
fallbackModel: config.sessionSettings.fallbackModel,
|
|
1086
|
+
runtimeControlValues: config.sessionSettings.runtimeControlValues,
|
|
1087
|
+
}),
|
|
1034
1088
|
availableModels: config.availableModels,
|
|
1035
1089
|
streamingText: '',
|
|
1036
1090
|
streamingTimer: null,
|
|
@@ -1069,11 +1123,7 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1069
1123
|
capabilities: CLAUDE_RUNTIME_CAPABILITIES,
|
|
1070
1124
|
fallbackTurnId: `claude-${conversationId}`,
|
|
1071
1125
|
streamingThrottleMs: STREAMING_THROTTLE_MS,
|
|
1072
|
-
requestedControls: {
|
|
1073
|
-
model: config.model,
|
|
1074
|
-
permissionMode: config.permissionMode,
|
|
1075
|
-
effort: config.effort,
|
|
1076
|
-
},
|
|
1126
|
+
requestedControls: {},
|
|
1077
1127
|
}, {
|
|
1078
1128
|
session: () => session,
|
|
1079
1129
|
port,
|
|
@@ -1089,6 +1139,9 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1089
1139
|
formatControlError: formatClaudeControlError,
|
|
1090
1140
|
});
|
|
1091
1141
|
const { streamingOutput, flushStreaming, onStreamDelta, clearStreaming, getFinalTurnTrail, writeState, writeTurn, setRuntimeControlError, clearRuntimeControlError, refreshVisibleWorkSignal, startVisibleWorkSignal, stopVisibleWorkSignal, } = writers;
|
|
1142
|
+
// Initial values come from persisted session config and are effective for
|
|
1143
|
+
// this query. Publish immediately so controls do not linger as requested.
|
|
1144
|
+
writeState();
|
|
1092
1145
|
function isActiveTurnInterrupted() {
|
|
1093
1146
|
const turnKey = session.activeInput?.turnKey;
|
|
1094
1147
|
return Boolean(turnKey && session.interruptedTurnKeys.has(turnKey));
|
|
@@ -1213,6 +1266,7 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1213
1266
|
};
|
|
1214
1267
|
writeTurn();
|
|
1215
1268
|
// ── Process SDK output messages ──
|
|
1269
|
+
let assistantStreamOrdinal = 0;
|
|
1216
1270
|
const processMessages = async () => {
|
|
1217
1271
|
try {
|
|
1218
1272
|
for await (const msg of q) {
|
|
@@ -1243,7 +1297,10 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1243
1297
|
stopVisibleWorkSignal();
|
|
1244
1298
|
break;
|
|
1245
1299
|
}
|
|
1246
|
-
if (event?.type === '
|
|
1300
|
+
if (event?.type === 'message_start') {
|
|
1301
|
+
assistantStreamOrdinal += 1;
|
|
1302
|
+
}
|
|
1303
|
+
else if (event?.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
1247
1304
|
session.toolInProgress = true;
|
|
1248
1305
|
session.turnState = 'tool';
|
|
1249
1306
|
streamingOutput.addBlock({
|
|
@@ -1272,12 +1329,35 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1272
1329
|
stopVisibleWorkSignal();
|
|
1273
1330
|
writeTurn();
|
|
1274
1331
|
const blockIndex = typeof event.index === 'number' ? event.index : 'latest';
|
|
1275
|
-
|
|
1332
|
+
const messageOrdinal = assistantStreamOrdinal || 1;
|
|
1333
|
+
onStreamDelta(event.delta.text, `text:${session.currentTurnId ?? conversationId}:${messageOrdinal}:${blockIndex}`);
|
|
1276
1334
|
}
|
|
1277
1335
|
break;
|
|
1278
1336
|
}
|
|
1279
1337
|
case 'system': {
|
|
1280
1338
|
const subtype = msg.subtype;
|
|
1339
|
+
if (subtype === 'task_started'
|
|
1340
|
+
|| subtype === 'task_progress'
|
|
1341
|
+
|| subtype === 'task_updated'
|
|
1342
|
+
|| subtype === 'task_notification') {
|
|
1343
|
+
const activity = mapClaudeSubagentActivity(msg);
|
|
1344
|
+
if (activity && !isActiveTurnInterrupted()) {
|
|
1345
|
+
if (!session.currentTurnId) {
|
|
1346
|
+
session.currentTurnId = randomUUID();
|
|
1347
|
+
session.currentTurnOpenedAt = Date.now();
|
|
1348
|
+
}
|
|
1349
|
+
if (session.state.state === 'running' && session.turnState !== 'streaming') {
|
|
1350
|
+
session.turnState = 'thinking';
|
|
1351
|
+
startVisibleWorkSignal();
|
|
1352
|
+
writeTurn();
|
|
1353
|
+
}
|
|
1354
|
+
await streamingOutput.addBlock(activity).catch(() => { });
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
if (subtype === 'model_refusal_fallback') {
|
|
1358
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Model refusal fallback: `
|
|
1359
|
+
+ `${msg.original_model} → ${msg.fallback_model}`);
|
|
1360
|
+
}
|
|
1281
1361
|
if (subtype === 'session_state_changed') {
|
|
1282
1362
|
const state = msg.state;
|
|
1283
1363
|
session.state.state = state;
|
|
@@ -1289,6 +1369,7 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1289
1369
|
}
|
|
1290
1370
|
session.state.contextUsage = undefined;
|
|
1291
1371
|
session.pendingFinalText = null;
|
|
1372
|
+
assistantStreamOrdinal = 0;
|
|
1292
1373
|
session.toolInProgress = false;
|
|
1293
1374
|
session.turnState = 'thinking';
|
|
1294
1375
|
startVisibleWorkSignal();
|
|
@@ -1385,24 +1466,6 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1385
1466
|
const init = async () => {
|
|
1386
1467
|
processMessages().catch((err) => console.error(`[canon-host] [${conversationId.slice(0, 8)}] processMessages crashed:`, err));
|
|
1387
1468
|
await q.initializationResult();
|
|
1388
|
-
if (config.model) {
|
|
1389
|
-
try {
|
|
1390
|
-
await q.setModel(config.model);
|
|
1391
|
-
session.state.model = config.model;
|
|
1392
|
-
}
|
|
1393
|
-
catch (err) {
|
|
1394
|
-
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Failed to set initial model:`, err);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
if (config.effort) {
|
|
1398
|
-
try {
|
|
1399
|
-
await applyClaudeEffortLevel(q, config.effort);
|
|
1400
|
-
session.state.effort = config.effort;
|
|
1401
|
-
}
|
|
1402
|
-
catch (err) {
|
|
1403
|
-
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Failed to set initial effort:`, err);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
1469
|
if (!resumeSessionId) {
|
|
1407
1470
|
// New session — send seed message to activate streaming input mode
|
|
1408
1471
|
sendInput(createClaudeInputEnvelope({
|
|
@@ -1417,25 +1480,32 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1417
1480
|
},
|
|
1418
1481
|
}));
|
|
1419
1482
|
}
|
|
1420
|
-
// Refresh the canonical runtime descriptor
|
|
1483
|
+
// Refresh the canonical runtime descriptor from a neutral probe. The live
|
|
1484
|
+
// session may be initialized with a selected model, which can narrow the
|
|
1485
|
+
// SDK's cached model list.
|
|
1421
1486
|
try {
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1487
|
+
const discoveredModels = await detectRuntimeModels(cwd).catch((error) => {
|
|
1488
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Model discovery failed:`, error);
|
|
1489
|
+
return runtimeModels;
|
|
1490
|
+
});
|
|
1491
|
+
if (discoveredModels.length > 0) {
|
|
1492
|
+
runtimeModels = discoveredModels;
|
|
1493
|
+
}
|
|
1494
|
+
const modelList = resolveClaudeModelOptions({
|
|
1495
|
+
discovered: runtimeModels,
|
|
1426
1496
|
selectedModel: session.state.model,
|
|
1427
1497
|
});
|
|
1428
1498
|
session.availableModels = modelList;
|
|
1429
|
-
runtimeModels = modelList;
|
|
1430
1499
|
onRuntimeDescriptorUpdate({
|
|
1431
1500
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1432
|
-
defaultModel: modelList[0]?.value,
|
|
1501
|
+
defaultModel: config.sessionSettings.model ?? modelList[0]?.value,
|
|
1433
1502
|
defaultPermissionMode: 'default',
|
|
1434
1503
|
availableModels: modelList,
|
|
1435
1504
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1436
1505
|
availableExecutionModes: config.availableExecutionModes,
|
|
1437
1506
|
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1438
1507
|
models: modelList,
|
|
1508
|
+
defaults: config.sessionSettings,
|
|
1439
1509
|
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1440
1510
|
workspaceRoots: workspaceRootMetadata,
|
|
1441
1511
|
executionModes: config.availableExecutionModes,
|
|
@@ -1443,7 +1513,7 @@ function createSession(conversationId, environment, agentId, port, typingSignals
|
|
|
1443
1513
|
}),
|
|
1444
1514
|
});
|
|
1445
1515
|
writeState();
|
|
1446
|
-
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Models: ${
|
|
1516
|
+
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Models: ${modelList.map((m) => m.label).join(', ')}`);
|
|
1447
1517
|
}
|
|
1448
1518
|
catch {
|
|
1449
1519
|
writeState();
|
|
@@ -1488,7 +1558,8 @@ export async function main() {
|
|
|
1488
1558
|
for (const warning of workspaceDiscovery.warnings) {
|
|
1489
1559
|
console.error(`[canon-host] ${warning}`);
|
|
1490
1560
|
}
|
|
1491
|
-
const { agentName: profileAgentName, profile, lockHandle, } = resolveCanonAgent({ logPrefix: '[canon-host]', expectedClientType: 'claude-code' });
|
|
1561
|
+
const { agentName: profileAgentName, profile, sessionDefaults: rawSessionDefaults, lockHandle, } = resolveCanonAgent({ logPrefix: '[canon-host]', expectedClientType: 'claude-code' });
|
|
1562
|
+
const profileSessionDefaults = parseClaudeSessionDefaults(rawSessionDefaults);
|
|
1492
1563
|
activeLockHandle = lockHandle ?? null;
|
|
1493
1564
|
console.error(`[canon-host] Starting${profile ? ` (profile: ${profile})` : ''} in ${workingDir}`);
|
|
1494
1565
|
// ── Canon via the Bridge (bridge-execution-plan Phase 6) ──
|
|
@@ -1578,12 +1649,14 @@ export async function main() {
|
|
|
1578
1649
|
});
|
|
1579
1650
|
let runtimeDescriptor = {
|
|
1580
1651
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1652
|
+
defaultModel: profileSessionDefaults.model ?? runtimeModels[0]?.value,
|
|
1581
1653
|
defaultPermissionMode: 'default',
|
|
1582
1654
|
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
1583
1655
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1584
1656
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1585
1657
|
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1586
1658
|
models: runtimeModels,
|
|
1659
|
+
defaults: profileSessionDefaults,
|
|
1587
1660
|
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1588
1661
|
workspaceRoots: workspaceRootMetadata,
|
|
1589
1662
|
executionModes: hostAvailableExecutionModes,
|
|
@@ -1598,31 +1671,12 @@ export async function main() {
|
|
|
1598
1671
|
refreshFloorMs: HEARTBEAT_MS,
|
|
1599
1672
|
}, { port });
|
|
1600
1673
|
const { conversationCache, knownConversationIds, getConversationMeta, refreshKnownConversationIds, handleConversationUpdated, markGroupContextModeUsed, loadHydratedInboundContext, } = directory;
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
agentName: profileAgentName,
|
|
1605
|
-
cwd: workingDir,
|
|
1606
|
-
baseCwd: workingDir,
|
|
1607
|
-
});
|
|
1608
|
-
if (!streamConnected)
|
|
1674
|
+
const publishSessionSnapshotsForConversations = async (conversationIds) => {
|
|
1675
|
+
const ids = Array.from(new Set(conversationIds));
|
|
1676
|
+
if (!streamConnected || ids.length === 0)
|
|
1609
1677
|
return;
|
|
1610
|
-
await refreshKnownConversationIds().catch((error) => {
|
|
1611
|
-
console.error('[canon-host] Failed to refresh known conversations:', error);
|
|
1612
|
-
});
|
|
1613
|
-
const activeSessionQuery = sessions.values().next().value?.query ?? null;
|
|
1614
|
-
const metadata = await refreshClaudeRuntimeMetadata({
|
|
1615
|
-
cwd: workingDir,
|
|
1616
|
-
activeQuery: activeSessionQuery,
|
|
1617
|
-
}).catch((error) => {
|
|
1618
|
-
console.error('[canon-host] Failed to refresh Claude runtime metadata:', error);
|
|
1619
|
-
return runtimeMetadata;
|
|
1620
|
-
});
|
|
1621
|
-
await publishAgentRuntime(port, runtimeDescriptor).catch((error) => {
|
|
1622
|
-
console.error('[canon-host] Failed to publish agent runtime:', error);
|
|
1623
|
-
});
|
|
1624
1678
|
await port.publishHostSessionSnapshots({
|
|
1625
|
-
conversationIds:
|
|
1679
|
+
conversationIds: ids,
|
|
1626
1680
|
clientType: 'claude-code',
|
|
1627
1681
|
runtime: runtimeDescriptor,
|
|
1628
1682
|
workspaceOptions: workspaceOptions.map(({ id, cwd }) => ({ id, cwd })),
|
|
@@ -1636,6 +1690,9 @@ export async function main() {
|
|
|
1636
1690
|
...(session.state.model ? { model: session.state.model } : {}),
|
|
1637
1691
|
...(session.state.permissionMode ? { permissionMode: session.state.permissionMode } : {}),
|
|
1638
1692
|
...(session.state.effort ? { effort: session.state.effort } : {}),
|
|
1693
|
+
...(session.state.runtimeControlValues
|
|
1694
|
+
? { runtimeControlValues: session.state.runtimeControlValues }
|
|
1695
|
+
: {}),
|
|
1639
1696
|
...(workspaceId ? { workspaceId } : {}),
|
|
1640
1697
|
executionMode: session.environment.mode,
|
|
1641
1698
|
executionBranch: session.environment.branch ?? null,
|
|
@@ -1645,14 +1702,41 @@ export async function main() {
|
|
|
1645
1702
|
}).catch((error) => {
|
|
1646
1703
|
console.error('[canon-host] Failed to publish session snapshots:', error);
|
|
1647
1704
|
});
|
|
1705
|
+
};
|
|
1706
|
+
const publishRuntimeHeartbeat = async () => {
|
|
1707
|
+
heartbeatLocalRuntimeEntry(runtimeId, {
|
|
1708
|
+
agentId,
|
|
1709
|
+
agentName: profileAgentName,
|
|
1710
|
+
cwd: workingDir,
|
|
1711
|
+
baseCwd: workingDir,
|
|
1712
|
+
});
|
|
1713
|
+
if (!streamConnected)
|
|
1714
|
+
return;
|
|
1715
|
+
await refreshKnownConversationIds().catch((error) => {
|
|
1716
|
+
console.error('[canon-host] Failed to refresh known conversations:', error);
|
|
1717
|
+
});
|
|
1718
|
+
const activeSessionQuery = sessions.values().next().value?.query ?? null;
|
|
1719
|
+
const metadata = await refreshClaudeRuntimeMetadata({
|
|
1720
|
+
cwd: workingDir,
|
|
1721
|
+
activeQuery: activeSessionQuery,
|
|
1722
|
+
}).catch((error) => {
|
|
1723
|
+
console.error('[canon-host] Failed to refresh Claude runtime metadata:', error);
|
|
1724
|
+
return runtimeMetadata;
|
|
1725
|
+
});
|
|
1726
|
+
await publishAgentRuntime(port, runtimeDescriptor).catch((error) => {
|
|
1727
|
+
console.error('[canon-host] Failed to publish agent runtime:', error);
|
|
1728
|
+
});
|
|
1729
|
+
await publishSessionSnapshotsForConversations(knownConversationIds);
|
|
1648
1730
|
// Rebuild so the descriptor picks up native slash commands from the
|
|
1649
1731
|
// metadata refresh above — the initial descriptor is built before the
|
|
1650
1732
|
// command inventory has loaded.
|
|
1651
1733
|
if (runtimeDescriptor.runtimeDescriptor) {
|
|
1652
1734
|
runtimeDescriptor = {
|
|
1653
1735
|
...runtimeDescriptor,
|
|
1736
|
+
defaultModel: profileSessionDefaults.model ?? runtimeModels[0]?.value,
|
|
1654
1737
|
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1655
1738
|
models: runtimeModels,
|
|
1739
|
+
defaults: profileSessionDefaults,
|
|
1656
1740
|
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1657
1741
|
workspaceRoots: workspaceRootMetadata,
|
|
1658
1742
|
executionModes: hostAvailableExecutionModes,
|
|
@@ -1680,6 +1764,10 @@ export async function main() {
|
|
|
1680
1764
|
executionBranch: session?.environment.branch ?? null,
|
|
1681
1765
|
worktreePath: session?.environment.worktreePath ?? null,
|
|
1682
1766
|
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
1767
|
+
fallbackModel: session?.state.fallbackModel
|
|
1768
|
+
?? profileSessionDefaults.fallbackModel
|
|
1769
|
+
?? null,
|
|
1770
|
+
ultracodeEnabled: (session?.state.runtimeControlValues?.ultracode ?? profileSessionDefaults.ultracode) === 'on',
|
|
1683
1771
|
});
|
|
1684
1772
|
await port.publishRuntimeStatus({
|
|
1685
1773
|
conversationId,
|
|
@@ -1694,7 +1782,7 @@ export async function main() {
|
|
|
1694
1782
|
runtimeModels = await detectRuntimeModels(workingDir);
|
|
1695
1783
|
runtimeDescriptor = {
|
|
1696
1784
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1697
|
-
defaultModel: runtimeModels[0]?.value,
|
|
1785
|
+
defaultModel: profileSessionDefaults.model ?? runtimeModels[0]?.value,
|
|
1698
1786
|
defaultPermissionMode: 'default',
|
|
1699
1787
|
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
1700
1788
|
availableModels: runtimeModels,
|
|
@@ -1702,6 +1790,7 @@ export async function main() {
|
|
|
1702
1790
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1703
1791
|
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1704
1792
|
models: runtimeModels,
|
|
1793
|
+
defaults: profileSessionDefaults,
|
|
1705
1794
|
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1706
1795
|
workspaceRoots: workspaceRootMetadata,
|
|
1707
1796
|
executionModes: hostAvailableExecutionModes,
|
|
@@ -1712,12 +1801,14 @@ export async function main() {
|
|
|
1712
1801
|
catch {
|
|
1713
1802
|
runtimeDescriptor = {
|
|
1714
1803
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1804
|
+
defaultModel: profileSessionDefaults.model ?? runtimeModels[0]?.value,
|
|
1715
1805
|
defaultPermissionMode: 'default',
|
|
1716
1806
|
availablePermissionModes: [...CLAUDE_PERMISSION_MODE_OPTIONS],
|
|
1717
1807
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1718
1808
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1719
1809
|
runtimeDescriptor: buildClaudeRuntimeDescriptor({
|
|
1720
1810
|
models: runtimeModels,
|
|
1811
|
+
defaults: profileSessionDefaults,
|
|
1721
1812
|
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1722
1813
|
workspaceRoots: workspaceRootMetadata,
|
|
1723
1814
|
executionModes: hostAvailableExecutionModes,
|
|
@@ -1847,6 +1938,10 @@ export async function main() {
|
|
|
1847
1938
|
function createSessionForConversation(conversationId) {
|
|
1848
1939
|
return (async () => {
|
|
1849
1940
|
const config = await loadSessionConfig(port, conversationId);
|
|
1941
|
+
const sessionSettings = resolveEffectiveSessionSettings({
|
|
1942
|
+
profileDefaults: profileSessionDefaults,
|
|
1943
|
+
sessionConfig: config,
|
|
1944
|
+
});
|
|
1850
1945
|
const sessionExecutionMode = resolveSessionExecutionMode(config);
|
|
1851
1946
|
const workspaceCwd = resolveWorkspaceCwd(config);
|
|
1852
1947
|
const environment = prepareConversationEnvironment({
|
|
@@ -1894,9 +1989,10 @@ export async function main() {
|
|
|
1894
1989
|
runtimeDescriptor = descriptor;
|
|
1895
1990
|
void publishRuntimeHeartbeat();
|
|
1896
1991
|
}, {
|
|
1897
|
-
model:
|
|
1992
|
+
model: sessionSettings.model ?? runtimeModels[0]?.value,
|
|
1898
1993
|
permissionMode: config?.permissionMode ?? 'default',
|
|
1899
|
-
effort:
|
|
1994
|
+
effort: sessionSettings.effort,
|
|
1995
|
+
sessionSettings,
|
|
1900
1996
|
availableModels: runtimeModels,
|
|
1901
1997
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
1902
1998
|
runtimePresentation,
|
|
@@ -2252,62 +2348,19 @@ export async function main() {
|
|
|
2252
2348
|
},
|
|
2253
2349
|
});
|
|
2254
2350
|
// ── Control signal watcher ──
|
|
2255
|
-
// Session control signals (model, mode, effort).
|
|
2256
|
-
//
|
|
2257
|
-
// never replayed once a session exists.
|
|
2351
|
+
// Session control signals (model, mode, effort). The bridge has already
|
|
2352
|
+
// consumed the control notification, so every path must publish an answer.
|
|
2258
2353
|
async function applyRuntimeSessionControl(event) {
|
|
2259
2354
|
const convoId = event.conversationId;
|
|
2260
2355
|
const session = sessions.get(convoId);
|
|
2261
|
-
if (!session || session.closed)
|
|
2262
|
-
|
|
2356
|
+
if (!session || session.closed) {
|
|
2357
|
+
console.error(`[canon-host] [${convoId.slice(0, 8)}] Control received without live session; publishing persisted snapshot`);
|
|
2358
|
+
void publishSessionSnapshotsForConversations([convoId]);
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2263
2361
|
const ctrl = event.control;
|
|
2264
2362
|
console.error(`[canon-host] [${convoId.slice(0, 8)}] Control:`, ctrl);
|
|
2265
|
-
|
|
2266
|
-
try {
|
|
2267
|
-
await session.query.setModel(ctrl.model);
|
|
2268
|
-
session.state.model = ctrl.model;
|
|
2269
|
-
session.clearRuntimeControlError('model');
|
|
2270
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Model → ${ctrl.model}`);
|
|
2271
|
-
}
|
|
2272
|
-
catch (err) {
|
|
2273
|
-
session.setRuntimeControlError('model', ctrl.model, err);
|
|
2274
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to set model:`, err);
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
const nextPermissionMode = parseClaudePermissionMode(ctrl.permissionMode);
|
|
2278
|
-
if (ctrl.permissionMode && !nextPermissionMode) {
|
|
2279
|
-
session.setRuntimeControlError('permissionMode', ctrl.permissionMode, new Error(`Unsupported Claude permission mode: ${ctrl.permissionMode}`));
|
|
2280
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Unsupported mode: ${ctrl.permissionMode}`);
|
|
2281
|
-
}
|
|
2282
|
-
else if (nextPermissionMode && nextPermissionMode !== session.state.permissionMode) {
|
|
2283
|
-
try {
|
|
2284
|
-
await session.query.setPermissionMode(nextPermissionMode);
|
|
2285
|
-
session.state.permissionMode = nextPermissionMode;
|
|
2286
|
-
session.clearRuntimeControlError('permissionMode');
|
|
2287
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Mode → ${nextPermissionMode}`);
|
|
2288
|
-
}
|
|
2289
|
-
catch (err) {
|
|
2290
|
-
session.setRuntimeControlError('permissionMode', nextPermissionMode, err);
|
|
2291
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to set mode:`, err);
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
|
-
const nextEffort = normalizeClaudeEffortLevel(ctrl.effort);
|
|
2295
|
-
if (nextEffort && nextEffort !== session.state.effort) {
|
|
2296
|
-
try {
|
|
2297
|
-
await applyClaudeEffortLevel(session.query, nextEffort);
|
|
2298
|
-
session.state.effort = nextEffort;
|
|
2299
|
-
session.clearRuntimeControlError('effort');
|
|
2300
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Effort → ${nextEffort}`);
|
|
2301
|
-
}
|
|
2302
|
-
catch (err) {
|
|
2303
|
-
session.setRuntimeControlError('effort', nextEffort, err);
|
|
2304
|
-
console.error(`[canon-host] [${convoId.slice(0, 8)}] Failed to set effort:`, err);
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
else if (ctrl.effort && !nextEffort) {
|
|
2308
|
-
session.setRuntimeControlError('effort', String(ctrl.effort), new Error('Unsupported Claude thinking level.'));
|
|
2309
|
-
}
|
|
2310
|
-
session.writeState();
|
|
2363
|
+
applyClaudeSessionControl(session, ctrl);
|
|
2311
2364
|
}
|
|
2312
2365
|
// ── Heartbeat + idle cleanup + graceful shutdown (agent-host lifecycle) ──
|
|
2313
2366
|
lifecycle.startTimers();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AgentDefinition, EffortLevel, ThinkingConfig } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
export type ClaudeThinkingMode = 'adaptive' | 'off';
|
|
3
|
+
export type ClaudeUltracodeMode = 'on' | 'off';
|
|
4
|
+
export type ClaudeEffortLevel = Extract<EffortLevel, 'low' | 'medium' | 'high' | 'xhigh' | 'max'>;
|
|
5
|
+
export interface ClaudeSessionDefaults {
|
|
6
|
+
model?: string;
|
|
7
|
+
effort?: ClaudeEffortLevel;
|
|
8
|
+
thinking?: ClaudeThinkingMode;
|
|
9
|
+
ultracode?: ClaudeUltracodeMode;
|
|
10
|
+
fallbackModel?: string;
|
|
11
|
+
agentProgressSummaries?: boolean;
|
|
12
|
+
agents?: Record<string, AgentDefinition>;
|
|
13
|
+
}
|
|
14
|
+
export interface ClaudeSessionControlValues {
|
|
15
|
+
thinking?: ClaudeThinkingMode;
|
|
16
|
+
ultracode?: ClaudeUltracodeMode;
|
|
17
|
+
}
|
|
18
|
+
export interface ClaudeEffectiveSessionSettings extends ClaudeSessionDefaults {
|
|
19
|
+
runtimeControlValues: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
export declare const CLAUDE_EFFORT_LEVELS: readonly ClaudeEffortLevel[];
|
|
22
|
+
export declare function parseClaudeThinkingMode(value: unknown): ClaudeThinkingMode | undefined;
|
|
23
|
+
export declare function parseClaudeUltracodeMode(value: unknown): ClaudeUltracodeMode | undefined;
|
|
24
|
+
export declare function parseClaudeSessionDefaults(raw: unknown): ClaudeSessionDefaults;
|
|
25
|
+
export declare function parseClaudeSessionControlValues(values: Record<string, string> | null | undefined): ClaudeSessionControlValues;
|
|
26
|
+
export declare function claudeThinkingToConfig(mode: ClaudeThinkingMode | undefined): ThinkingConfig | undefined;
|
|
27
|
+
export declare function claudeThinkingToFlagValue(mode: ClaudeThinkingMode): boolean;
|
|
28
|
+
export declare function resolveEffectiveSessionSettings(input: {
|
|
29
|
+
profileDefaults?: ClaudeSessionDefaults;
|
|
30
|
+
sessionConfig?: {
|
|
31
|
+
model?: string;
|
|
32
|
+
effort?: ClaudeEffortLevel;
|
|
33
|
+
runtimeControlValues?: Record<string, string>;
|
|
34
|
+
} | null;
|
|
35
|
+
}): ClaudeEffectiveSessionSettings;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export const CLAUDE_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
2
|
+
const CLAUDE_EFFORT_VALUES = new Set(CLAUDE_EFFORT_LEVELS);
|
|
3
|
+
function optionalString(value) {
|
|
4
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
5
|
+
}
|
|
6
|
+
function optionalBoolean(value) {
|
|
7
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function parseClaudeThinkingMode(value) {
|
|
10
|
+
if (value === false || value === 'off' || value === 'disabled')
|
|
11
|
+
return 'off';
|
|
12
|
+
if (value === true || value === 'on' || value === 'adaptive' || value === 'enabled')
|
|
13
|
+
return 'adaptive';
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export function parseClaudeUltracodeMode(value) {
|
|
17
|
+
if (value === true || value === 'on' || value === 'enabled')
|
|
18
|
+
return 'on';
|
|
19
|
+
if (value === false || value === 'off' || value === 'disabled')
|
|
20
|
+
return 'off';
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function parseEffort(value) {
|
|
24
|
+
const normalized = optionalString(value);
|
|
25
|
+
if (!normalized)
|
|
26
|
+
return undefined;
|
|
27
|
+
return CLAUDE_EFFORT_VALUES.has(normalized)
|
|
28
|
+
? normalized
|
|
29
|
+
: undefined;
|
|
30
|
+
}
|
|
31
|
+
function parseStringArray(value) {
|
|
32
|
+
if (!Array.isArray(value))
|
|
33
|
+
return undefined;
|
|
34
|
+
const out = value
|
|
35
|
+
.map((entry) => optionalString(entry))
|
|
36
|
+
.filter((entry) => Boolean(entry));
|
|
37
|
+
return out.length > 0 ? out : undefined;
|
|
38
|
+
}
|
|
39
|
+
function parseAgentDefinition(value) {
|
|
40
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
41
|
+
return null;
|
|
42
|
+
const data = value;
|
|
43
|
+
const description = optionalString(data.description);
|
|
44
|
+
const prompt = optionalString(data.prompt);
|
|
45
|
+
if (!description || !prompt)
|
|
46
|
+
return null;
|
|
47
|
+
return {
|
|
48
|
+
description,
|
|
49
|
+
prompt,
|
|
50
|
+
...(parseStringArray(data.tools) ? { tools: parseStringArray(data.tools) } : {}),
|
|
51
|
+
...(parseStringArray(data.disallowedTools) ? { disallowedTools: parseStringArray(data.disallowedTools) } : {}),
|
|
52
|
+
...(optionalString(data.model) ? { model: optionalString(data.model) } : {}),
|
|
53
|
+
...(parseStringArray(data.skills) ? { skills: parseStringArray(data.skills) } : {}),
|
|
54
|
+
...(typeof data.maxTurns === 'number' && Number.isFinite(data.maxTurns) && data.maxTurns > 0
|
|
55
|
+
? { maxTurns: Math.floor(data.maxTurns) }
|
|
56
|
+
: {}),
|
|
57
|
+
...(typeof data.background === 'boolean' ? { background: data.background } : {}),
|
|
58
|
+
...(parseEffort(data.effort) ? { effort: parseEffort(data.effort) } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function parseAgents(value) {
|
|
62
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
63
|
+
return undefined;
|
|
64
|
+
const out = {};
|
|
65
|
+
for (const [rawName, rawAgent] of Object.entries(value)) {
|
|
66
|
+
const name = rawName.trim();
|
|
67
|
+
if (!name)
|
|
68
|
+
continue;
|
|
69
|
+
const agent = parseAgentDefinition(rawAgent);
|
|
70
|
+
if (agent)
|
|
71
|
+
out[name] = agent;
|
|
72
|
+
}
|
|
73
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
74
|
+
}
|
|
75
|
+
export function parseClaudeSessionDefaults(raw) {
|
|
76
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
77
|
+
return {};
|
|
78
|
+
const data = raw;
|
|
79
|
+
return {
|
|
80
|
+
...(optionalString(data.model) ? { model: optionalString(data.model) } : {}),
|
|
81
|
+
...(parseEffort(data.effort) ? { effort: parseEffort(data.effort) } : {}),
|
|
82
|
+
...(parseClaudeThinkingMode(data.thinking) ? { thinking: parseClaudeThinkingMode(data.thinking) } : {}),
|
|
83
|
+
...(parseClaudeUltracodeMode(data.ultracode) ? { ultracode: parseClaudeUltracodeMode(data.ultracode) } : {}),
|
|
84
|
+
...(optionalString(data.fallbackModel) ? { fallbackModel: optionalString(data.fallbackModel) } : {}),
|
|
85
|
+
...(optionalBoolean(data.agentProgressSummaries) !== undefined
|
|
86
|
+
? { agentProgressSummaries: optionalBoolean(data.agentProgressSummaries) }
|
|
87
|
+
: {}),
|
|
88
|
+
...(parseAgents(data.agents) ? { agents: parseAgents(data.agents) } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function parseClaudeSessionControlValues(values) {
|
|
92
|
+
return {
|
|
93
|
+
...(parseClaudeThinkingMode(values?.thinking) ? { thinking: parseClaudeThinkingMode(values?.thinking) } : {}),
|
|
94
|
+
...(parseClaudeUltracodeMode(values?.ultracode) ? { ultracode: parseClaudeUltracodeMode(values?.ultracode) } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function claudeThinkingToConfig(mode) {
|
|
98
|
+
if (mode === 'adaptive')
|
|
99
|
+
return { type: 'adaptive' };
|
|
100
|
+
if (mode === 'off')
|
|
101
|
+
return { type: 'disabled' };
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
export function claudeThinkingToFlagValue(mode) {
|
|
105
|
+
return mode !== 'off';
|
|
106
|
+
}
|
|
107
|
+
export function resolveEffectiveSessionSettings(input) {
|
|
108
|
+
const controlValues = parseClaudeSessionControlValues(input.sessionConfig?.runtimeControlValues);
|
|
109
|
+
const runtimeControlValues = {
|
|
110
|
+
...(input.profileDefaults?.thinking ? { thinking: input.profileDefaults.thinking } : {}),
|
|
111
|
+
...(input.profileDefaults?.ultracode ? { ultracode: input.profileDefaults.ultracode } : {}),
|
|
112
|
+
...(input.sessionConfig?.runtimeControlValues ?? {}),
|
|
113
|
+
...(controlValues.thinking ? { thinking: controlValues.thinking } : {}),
|
|
114
|
+
...(controlValues.ultracode ? { ultracode: controlValues.ultracode } : {}),
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
...(input.profileDefaults ?? {}),
|
|
118
|
+
...(input.sessionConfig?.model ? { model: input.sessionConfig.model } : {}),
|
|
119
|
+
...(input.sessionConfig?.effort ? { effort: input.sessionConfig.effort } : {}),
|
|
120
|
+
...(controlValues.thinking ? { thinking: controlValues.thinking } : {}),
|
|
121
|
+
...(controlValues.ultracode ? { ultracode: controlValues.ultracode } : {}),
|
|
122
|
+
runtimeControlValues,
|
|
123
|
+
};
|
|
124
|
+
}
|
package/dist/session-state.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
1
|
+
import type { Query, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
2
2
|
import type { DeliveryIntent, ModelOption, TurnLifecycleState } from '@canonmsg/core/contract';
|
|
3
3
|
import type { TurnArtifactSnapshot } from '@canonmsg/agent-host';
|
|
4
|
+
import { type ClaudeEffortLevel } from './session-defaults.js';
|
|
5
|
+
export type { ClaudeEffortLevel } from './session-defaults.js';
|
|
4
6
|
export type ClaudeInputKind = 'seed' | 'canon';
|
|
5
7
|
export type ClaudeArtifactRoutingMode = 'workspace-generated' | 'disabled';
|
|
6
8
|
export interface ClaudeInputEnvelope {
|
|
@@ -19,6 +21,47 @@ export interface ClaudeRuntimeControlError {
|
|
|
19
21
|
message: string;
|
|
20
22
|
updatedAt: number;
|
|
21
23
|
}
|
|
24
|
+
export type ClaudePermissionMode = Parameters<Query['setPermissionMode']>[0];
|
|
25
|
+
export interface ClaudeSessionControl {
|
|
26
|
+
model?: string;
|
|
27
|
+
permissionMode?: string;
|
|
28
|
+
effort?: string;
|
|
29
|
+
runtimeControlValues?: Record<string, string>;
|
|
30
|
+
updatedAt?: number;
|
|
31
|
+
updatedBy?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ClaudeHostSessionState {
|
|
34
|
+
model?: string;
|
|
35
|
+
permissionMode?: string;
|
|
36
|
+
effort?: string;
|
|
37
|
+
fallbackModel?: string;
|
|
38
|
+
runtimeControlValues?: Record<string, string>;
|
|
39
|
+
state?: 'idle' | 'running' | 'requires_action';
|
|
40
|
+
contextUsage?: {
|
|
41
|
+
percentage: number;
|
|
42
|
+
totalTokens: number;
|
|
43
|
+
maxTokens: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export declare function parseClaudePermissionMode(value: unknown): ClaudePermissionMode | null;
|
|
47
|
+
export declare function normalizeClaudeEffortLevel(value: unknown): ClaudeEffortLevel | undefined;
|
|
48
|
+
export declare function buildClaudeInitialSessionState(input: {
|
|
49
|
+
model?: string;
|
|
50
|
+
permissionMode?: string;
|
|
51
|
+
effort?: string;
|
|
52
|
+
fallbackModel?: string;
|
|
53
|
+
runtimeControlValues?: Record<string, string>;
|
|
54
|
+
}): ClaudeHostSessionState;
|
|
55
|
+
export interface ClaudeSessionControlTarget {
|
|
56
|
+
state: ClaudeHostSessionState;
|
|
57
|
+
controlApplySeq: Map<string, number>;
|
|
58
|
+
closed: boolean;
|
|
59
|
+
query: Pick<Query, 'setModel' | 'setPermissionMode' | 'applyFlagSettings'>;
|
|
60
|
+
writeState: () => void;
|
|
61
|
+
setRuntimeControlError: (controlId: string, value: string, error: unknown) => void;
|
|
62
|
+
clearRuntimeControlError: (controlId: string) => void;
|
|
63
|
+
}
|
|
64
|
+
export declare function applyClaudeSessionControl(target: ClaudeSessionControlTarget, ctrl: ClaudeSessionControl): void;
|
|
22
65
|
export interface ClaudePendingFinalDelivery {
|
|
23
66
|
turnKey: string;
|
|
24
67
|
text: string;
|
|
@@ -60,9 +103,8 @@ export declare function shouldDeliverClaudeFinal(input: {
|
|
|
60
103
|
interruptedTurnKeys: ReadonlySet<string>;
|
|
61
104
|
}): boolean;
|
|
62
105
|
export declare function resetClaudeCompletedTurnState(session: ClaudeCompletedTurnState): void;
|
|
63
|
-
export declare function
|
|
64
|
-
|
|
65
|
-
next: ReadonlyArray<ModelOption>;
|
|
106
|
+
export declare function resolveClaudeModelOptions(input: {
|
|
107
|
+
discovered: ReadonlyArray<ModelOption>;
|
|
66
108
|
selectedModel?: string | null;
|
|
67
109
|
}): ModelOption[];
|
|
68
110
|
export declare function formatClaudeControlError(error: unknown): string;
|
package/dist/session-state.js
CHANGED
|
@@ -1,4 +1,173 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { CLAUDE_EFFORT_LEVELS, claudeThinkingToFlagValue, parseClaudeThinkingMode, parseClaudeUltracodeMode, } from './session-defaults.js';
|
|
3
|
+
const CLAUDE_PERMISSION_MODE_VALUES = new Set([
|
|
4
|
+
'acceptEdits',
|
|
5
|
+
'auto',
|
|
6
|
+
'bypassPermissions',
|
|
7
|
+
'default',
|
|
8
|
+
'dontAsk',
|
|
9
|
+
'plan',
|
|
10
|
+
]);
|
|
11
|
+
const CLAUDE_EFFORT_VALUES = new Set(CLAUDE_EFFORT_LEVELS);
|
|
12
|
+
export function parseClaudePermissionMode(value) {
|
|
13
|
+
return typeof value === 'string' && CLAUDE_PERMISSION_MODE_VALUES.has(value)
|
|
14
|
+
? value
|
|
15
|
+
: null;
|
|
16
|
+
}
|
|
17
|
+
export function normalizeClaudeEffortLevel(value) {
|
|
18
|
+
return typeof value === 'string' && CLAUDE_EFFORT_VALUES.has(value)
|
|
19
|
+
? value
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
export function buildClaudeInitialSessionState(input) {
|
|
23
|
+
return {
|
|
24
|
+
...(input.model ? { model: input.model } : {}),
|
|
25
|
+
...(input.permissionMode ? { permissionMode: input.permissionMode } : {}),
|
|
26
|
+
...(input.effort ? { effort: input.effort } : {}),
|
|
27
|
+
...(input.fallbackModel ? { fallbackModel: input.fallbackModel } : {}),
|
|
28
|
+
...(input.runtimeControlValues ? { runtimeControlValues: input.runtimeControlValues } : {}),
|
|
29
|
+
state: 'idle',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function nextControlApplySeq(target, controlId) {
|
|
33
|
+
const nextSeq = (target.controlApplySeq.get(controlId) ?? 0) + 1;
|
|
34
|
+
target.controlApplySeq.set(controlId, nextSeq);
|
|
35
|
+
return nextSeq;
|
|
36
|
+
}
|
|
37
|
+
function applyOptimisticControl(input) {
|
|
38
|
+
const seq = nextControlApplySeq(input.target, input.controlId);
|
|
39
|
+
const applyPromise = (async () => input.apply())();
|
|
40
|
+
void applyPromise
|
|
41
|
+
.catch((error) => {
|
|
42
|
+
if (input.target.closed || input.target.controlApplySeq.get(input.controlId) !== seq) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
input.revert();
|
|
46
|
+
input.target.setRuntimeControlError(input.controlId, input.value, error);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function setRuntimeControlValue(state, controlId, value) {
|
|
50
|
+
state.runtimeControlValues = {
|
|
51
|
+
...(state.runtimeControlValues ?? {}),
|
|
52
|
+
[controlId]: value,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function applyClaudeSessionControl(target, ctrl) {
|
|
56
|
+
const nextModel = typeof ctrl.model === 'string' && ctrl.model.trim() ? ctrl.model.trim() : null;
|
|
57
|
+
if (ctrl.model !== undefined && !nextModel) {
|
|
58
|
+
target.setRuntimeControlError('model', String(ctrl.model), new Error('Unsupported Claude model.'));
|
|
59
|
+
}
|
|
60
|
+
else if (nextModel && nextModel !== target.state.model) {
|
|
61
|
+
const previous = target.state.model;
|
|
62
|
+
target.state.model = nextModel;
|
|
63
|
+
target.clearRuntimeControlError('model');
|
|
64
|
+
applyOptimisticControl({
|
|
65
|
+
target,
|
|
66
|
+
controlId: 'model',
|
|
67
|
+
value: nextModel,
|
|
68
|
+
apply: () => target.query.setModel(nextModel),
|
|
69
|
+
revert: () => {
|
|
70
|
+
if (previous === undefined)
|
|
71
|
+
delete target.state.model;
|
|
72
|
+
else
|
|
73
|
+
target.state.model = previous;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const nextPermissionMode = parseClaudePermissionMode(ctrl.permissionMode);
|
|
78
|
+
if (ctrl.permissionMode !== undefined && !nextPermissionMode) {
|
|
79
|
+
target.setRuntimeControlError('permissionMode', ctrl.permissionMode, new Error(`Unsupported Claude permission mode: ${ctrl.permissionMode}`));
|
|
80
|
+
}
|
|
81
|
+
else if (nextPermissionMode && nextPermissionMode !== target.state.permissionMode) {
|
|
82
|
+
// Claude Code applies permission-mode changes asynchronously. We publish
|
|
83
|
+
// the requested value immediately so Canon does not show a stale "requested"
|
|
84
|
+
// state while the SDK catches up, then revert if the current apply fails.
|
|
85
|
+
const previous = target.state.permissionMode;
|
|
86
|
+
target.state.permissionMode = nextPermissionMode;
|
|
87
|
+
target.clearRuntimeControlError('permissionMode');
|
|
88
|
+
applyOptimisticControl({
|
|
89
|
+
target,
|
|
90
|
+
controlId: 'permissionMode',
|
|
91
|
+
value: nextPermissionMode,
|
|
92
|
+
apply: () => target.query.setPermissionMode(nextPermissionMode),
|
|
93
|
+
revert: () => {
|
|
94
|
+
if (previous === undefined)
|
|
95
|
+
delete target.state.permissionMode;
|
|
96
|
+
else
|
|
97
|
+
target.state.permissionMode = previous;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const nextEffort = normalizeClaudeEffortLevel(ctrl.effort);
|
|
102
|
+
if (nextEffort && nextEffort !== target.state.effort) {
|
|
103
|
+
const previous = target.state.effort;
|
|
104
|
+
target.state.effort = nextEffort;
|
|
105
|
+
target.clearRuntimeControlError('effort');
|
|
106
|
+
applyOptimisticControl({
|
|
107
|
+
target,
|
|
108
|
+
controlId: 'effort',
|
|
109
|
+
value: nextEffort,
|
|
110
|
+
apply: () => target.query.applyFlagSettings({ effortLevel: nextEffort }),
|
|
111
|
+
revert: () => {
|
|
112
|
+
if (previous === undefined)
|
|
113
|
+
delete target.state.effort;
|
|
114
|
+
else
|
|
115
|
+
target.state.effort = previous;
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else if (ctrl.effort !== undefined && !nextEffort) {
|
|
120
|
+
target.setRuntimeControlError('effort', String(ctrl.effort), new Error('Unsupported Claude thinking level.'));
|
|
121
|
+
}
|
|
122
|
+
const runtimeControlValues = ctrl.runtimeControlValues ?? {};
|
|
123
|
+
if ('thinking' in runtimeControlValues) {
|
|
124
|
+
const nextThinking = parseClaudeThinkingMode(runtimeControlValues.thinking);
|
|
125
|
+
if (!nextThinking) {
|
|
126
|
+
target.setRuntimeControlError('thinking', String(runtimeControlValues.thinking), new Error('Unsupported Claude thinking mode.'));
|
|
127
|
+
}
|
|
128
|
+
else if (nextThinking !== target.state.runtimeControlValues?.thinking) {
|
|
129
|
+
const previous = target.state.runtimeControlValues?.thinking;
|
|
130
|
+
setRuntimeControlValue(target.state, 'thinking', nextThinking);
|
|
131
|
+
target.clearRuntimeControlError('thinking');
|
|
132
|
+
applyOptimisticControl({
|
|
133
|
+
target,
|
|
134
|
+
controlId: 'thinking',
|
|
135
|
+
value: nextThinking,
|
|
136
|
+
apply: () => target.query.applyFlagSettings({ alwaysThinkingEnabled: claudeThinkingToFlagValue(nextThinking) }),
|
|
137
|
+
revert: () => {
|
|
138
|
+
if (previous === undefined)
|
|
139
|
+
delete target.state.runtimeControlValues?.thinking;
|
|
140
|
+
else
|
|
141
|
+
setRuntimeControlValue(target.state, 'thinking', previous);
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if ('ultracode' in runtimeControlValues) {
|
|
147
|
+
const nextUltracode = parseClaudeUltracodeMode(runtimeControlValues.ultracode);
|
|
148
|
+
if (!nextUltracode) {
|
|
149
|
+
target.setRuntimeControlError('ultracode', String(runtimeControlValues.ultracode), new Error('Unsupported Claude ultracode mode.'));
|
|
150
|
+
}
|
|
151
|
+
else if (nextUltracode !== target.state.runtimeControlValues?.ultracode) {
|
|
152
|
+
const previous = target.state.runtimeControlValues?.ultracode;
|
|
153
|
+
setRuntimeControlValue(target.state, 'ultracode', nextUltracode);
|
|
154
|
+
target.clearRuntimeControlError('ultracode');
|
|
155
|
+
applyOptimisticControl({
|
|
156
|
+
target,
|
|
157
|
+
controlId: 'ultracode',
|
|
158
|
+
value: nextUltracode,
|
|
159
|
+
apply: () => target.query.applyFlagSettings({ ultracode: nextUltracode === 'on' }),
|
|
160
|
+
revert: () => {
|
|
161
|
+
if (previous === undefined)
|
|
162
|
+
delete target.state.runtimeControlValues?.ultracode;
|
|
163
|
+
else
|
|
164
|
+
setRuntimeControlValue(target.state, 'ultracode', previous);
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
target.writeState();
|
|
170
|
+
}
|
|
2
171
|
export function createClaudeInputEnvelope(input) {
|
|
3
172
|
const sourceMessageId = input.sourceMessageId ?? null;
|
|
4
173
|
const fallbackId = randomUUID();
|
|
@@ -46,32 +215,19 @@ export function resetClaudeCompletedTurnState(session) {
|
|
|
46
215
|
session.toolInProgress = false;
|
|
47
216
|
session.turnState = 'idle';
|
|
48
217
|
}
|
|
49
|
-
export function
|
|
218
|
+
export function resolveClaudeModelOptions(input) {
|
|
50
219
|
const byValue = new Map();
|
|
51
|
-
for (const option of input.
|
|
52
|
-
byValue.set(option.value, { ...option });
|
|
53
|
-
}
|
|
54
|
-
for (const option of input.next) {
|
|
220
|
+
for (const option of input.discovered) {
|
|
55
221
|
byValue.set(option.value, { ...option });
|
|
56
222
|
}
|
|
57
223
|
if (input.selectedModel && !byValue.has(input.selectedModel)) {
|
|
58
224
|
byValue.set(input.selectedModel, {
|
|
59
225
|
value: input.selectedModel,
|
|
60
226
|
label: input.selectedModel,
|
|
61
|
-
description: 'Selected for this Canon session but not returned by
|
|
227
|
+
description: 'Selected for this Canon session but not returned by Claude model discovery.',
|
|
62
228
|
});
|
|
63
229
|
}
|
|
64
|
-
|
|
65
|
-
return [...byValue.values()].map((option) => {
|
|
66
|
-
if (nextValues.has(option.value))
|
|
67
|
-
return option;
|
|
68
|
-
return {
|
|
69
|
-
...option,
|
|
70
|
-
description: option.description
|
|
71
|
-
? `${option.description} Previously advertised; not returned by the latest Claude model probe.`
|
|
72
|
-
: 'Previously advertised; not returned by the latest Claude model probe.',
|
|
73
|
-
};
|
|
74
|
-
});
|
|
230
|
+
return [...byValue.values()];
|
|
75
231
|
}
|
|
76
232
|
export function formatClaudeControlError(error) {
|
|
77
233
|
if (error instanceof Error && error.message.trim()) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SDKTaskNotificationMessage, SDKTaskProgressMessage, SDKTaskStartedMessage, SDKTaskUpdatedMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
type ClaudeTaskMessage = SDKTaskNotificationMessage | SDKTaskProgressMessage | SDKTaskStartedMessage | SDKTaskUpdatedMessage;
|
|
3
|
+
export interface ClaudeSubagentActivityBlock {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: 'status';
|
|
6
|
+
status: 'running' | 'completed' | 'failed';
|
|
7
|
+
title: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function mapClaudeSubagentActivity(message: ClaudeTaskMessage): ClaudeSubagentActivityBlock | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function titleForMessage(message) {
|
|
2
|
+
const subtype = message.subtype;
|
|
3
|
+
if (subtype === 'task_started') {
|
|
4
|
+
return message.subagent_type
|
|
5
|
+
? `Subagent started: ${message.subagent_type}`
|
|
6
|
+
: 'Subagent started';
|
|
7
|
+
}
|
|
8
|
+
if (subtype === 'task_progress') {
|
|
9
|
+
return message.subagent_type
|
|
10
|
+
? `Subagent progress: ${message.subagent_type}`
|
|
11
|
+
: 'Subagent progress';
|
|
12
|
+
}
|
|
13
|
+
if (subtype === 'task_updated')
|
|
14
|
+
return 'Subagent updated';
|
|
15
|
+
return `Subagent ${message.status}`;
|
|
16
|
+
}
|
|
17
|
+
function textForMessage(message) {
|
|
18
|
+
if (message.subtype === 'task_started')
|
|
19
|
+
return message.description;
|
|
20
|
+
if (message.subtype === 'task_progress')
|
|
21
|
+
return message.summary ?? message.description;
|
|
22
|
+
if (message.subtype === 'task_updated')
|
|
23
|
+
return message.patch.description ?? message.patch.error;
|
|
24
|
+
return message.summary;
|
|
25
|
+
}
|
|
26
|
+
function statusForMessage(message) {
|
|
27
|
+
if (message.subtype === 'task_notification') {
|
|
28
|
+
if (message.status === 'completed')
|
|
29
|
+
return 'completed';
|
|
30
|
+
return 'failed';
|
|
31
|
+
}
|
|
32
|
+
if (message.subtype === 'task_updated') {
|
|
33
|
+
if (message.patch.status === 'completed')
|
|
34
|
+
return 'completed';
|
|
35
|
+
if (message.patch.status === 'failed' || message.patch.status === 'killed')
|
|
36
|
+
return 'failed';
|
|
37
|
+
}
|
|
38
|
+
return 'running';
|
|
39
|
+
}
|
|
40
|
+
export function mapClaudeSubagentActivity(message) {
|
|
41
|
+
if ('skip_transcript' in message && message.skip_transcript)
|
|
42
|
+
return null;
|
|
43
|
+
const text = textForMessage(message);
|
|
44
|
+
return {
|
|
45
|
+
id: `task:${message.task_id}`,
|
|
46
|
+
kind: 'status',
|
|
47
|
+
status: statusForMessage(message),
|
|
48
|
+
title: titleForMessage(message),
|
|
49
|
+
...(text ? { text } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
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,10 +31,10 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "^0.3.200",
|
|
34
|
-
"@canonmsg/agent-host": "^0.
|
|
35
|
-
"@canonmsg/agent-sdk": "^3.4.
|
|
34
|
+
"@canonmsg/agent-host": "^0.4.0",
|
|
35
|
+
"@canonmsg/agent-sdk": "^3.4.4",
|
|
36
36
|
"@canonmsg/bridge": "^0.2.0",
|
|
37
|
-
"@canonmsg/core": "^3.
|
|
37
|
+
"@canonmsg/core": "^3.2.0",
|
|
38
38
|
"@canonmsg/framework": "^0.2.0",
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
40
40
|
},
|