@canonmsg/codex-plugin 0.9.0 → 0.9.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/README.md CHANGED
@@ -22,7 +22,15 @@ canon-codex --cwd /path/to/project
22
22
 
23
23
  Registration saves a Canon profile in `~/.canon/agents.json`, the same shared profile store used by the Claude Code integration and supported by the OpenClaw plugin.
24
24
 
25
- If the terminal closes or the machine restarts, the agent goes offline until you start the host again. To bring back the same registered agent, rerun `canon-codex --cwd /path/to/project`. Do not run registration again unless Canon tells you the saved API key is invalid. If you registered multiple profiles, relaunch the same one with `CANON_AGENT=<profile> canon-codex --cwd /path/to/project`.
25
+ If the terminal closes or the machine restarts, the agent goes offline until you start the host again. Install `@canonmsg/local-agents` and run `canon-necromance` to list every recorded local agent from newest to oldest, then revive one in the foreground:
26
+
27
+ ```bash
28
+ npm install -g @canonmsg/local-agents
29
+ canon-necromance
30
+ canon-necromance revive my-codex
31
+ ```
32
+
33
+ Do not run registration again unless Canon tells you the saved API key is invalid. If you registered multiple profiles, relaunch the same one with `CANON_AGENT=<profile> canon-codex --cwd /path/to/project`. Keep the revived terminal open; closing it takes this local agent offline.
26
34
 
27
35
  You do not need a git repo for host mode. The plugin passes `--skip-git-repo-check` to Codex, so any readable working directory is valid.
28
36
 
@@ -99,7 +107,7 @@ If `canon-codex` starts but cannot find the `codex` binary, either fix your `PAT
99
107
  canon-codex --cwd /path/to/project --codex-bin /absolute/path/to/codex
100
108
  ```
101
109
 
102
- If Canon rejects authenticated requests with `401 Invalid API key`, the stored Canon profile needs a fresh key. Rerun registration for the same profile to overwrite `~/.canon/agents.json`, then restart the host:
110
+ If Canon rejects authenticated requests with `401 Invalid API key`, the stored Canon profile needs a fresh key. Reconnect the same profile to overwrite `~/.canon/agents.json`, then restart or revive the host:
103
111
 
104
112
  ```bash
105
113
  canon-codex-register --name "My Codex" --description "My local coding agent" --phone "+15551234567" --profile my-codex
@@ -68,8 +68,6 @@ export function renderCanonHostInboundContent(message, materialized) {
68
68
  }
69
69
  function describeContactCard(card) {
70
70
  const parts = [`${card.userType} · userId: ${card.userId}`];
71
- if (card.accessLevel)
72
- parts.push(`access: ${card.accessLevel}`);
73
71
  if (card.ownerName)
74
72
  parts.push(`owner: ${card.ownerName}`);
75
73
  if (card.about)
package/dist/host.js CHANGED
@@ -3,7 +3,7 @@ import { setDefaultResultOrder } from 'node:dns';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { parseArgs } from 'node:util';
5
5
  import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
6
- import { buildConfiguredWorkspaceOptionsWithRoots, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, clearSessionState, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfile, initRTDBAuth, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, releaseLock, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, writeRuntimeInfo, shouldTriggerAgentTurn, writeSessionState, writeTurnState, } from '@canonmsg/core';
6
+ import { buildConfiguredWorkspaceOptionsWithRoots, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, clearSessionState, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, loadRuntimeSessionState, markLocalRuntimeStopped, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, writeRuntimeInfo, shouldTriggerAgentTurn, saveRuntimeSessionState, writeSessionState, writeTurnState, upsertLocalRuntimeEntry, } from '@canonmsg/core';
7
7
  import { buildCanonHostPrompt, buildHydratedInboundContext, createConversationMetadataLoader, loadHostSessionConfig, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, } from './host-runtime.js';
8
8
  import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
9
9
  import { CodexConversationAdapter, } from './adapter.js';
@@ -218,9 +218,9 @@ export async function main() {
218
218
  if (typeof args['ask-for-approval'] === 'string') {
219
219
  console.error('[canon-codex] Note: newer Codex CLI releases do not accept --ask-for-approval for `codex exec`; Canon will translate compatible legacy usage when possible.');
220
220
  }
221
- const { apiKey, agentId: profileAgentId, profile } = resolveCanonAgent({ logPrefix: '[canon-codex]' });
221
+ const { apiKey, agentId: profileAgentId, agentName: profileAgentName, profile, baseUrl, lockHandle, } = resolveCanonAgent({ logPrefix: '[canon-codex]', expectedClientType: 'codex' });
222
222
  console.error(`[canon-codex] Starting${profile ? ` (profile: ${profile})` : ''} in ${workingDir}`);
223
- const client = new CanonClient(apiKey);
223
+ const client = new CanonClient(apiKey, baseUrl);
224
224
  initRTDBAuth(client);
225
225
  let agentId;
226
226
  let ownerId = null;
@@ -240,6 +240,34 @@ export async function main() {
240
240
  }
241
241
  console.error(`[canon-codex] Authenticated as ${agentId}`);
242
242
  }
243
+ const launchArgs = [...process.argv.slice(2)];
244
+ if (!launchArgs.some((arg) => arg === '--cwd' || arg.startsWith('--cwd='))) {
245
+ launchArgs.push('--cwd', workingDir);
246
+ }
247
+ const runtimeId = buildLocalRuntimeId({
248
+ runtime: 'codex',
249
+ profile,
250
+ cwd: workingDir,
251
+ launchCommand: ['canon-codex', ...launchArgs],
252
+ });
253
+ upsertLocalRuntimeEntry({
254
+ id: runtimeId,
255
+ runtime: 'codex',
256
+ profile,
257
+ agentId,
258
+ agentName: profileAgentName,
259
+ cwd: workingDir,
260
+ baseCwd: workingDir,
261
+ workspaceRoots: workspaceRoots.map((root) => root.cwd),
262
+ workspaces: workspaceOptions.map((workspace) => workspace.cwd),
263
+ launchCommand: ['canon-codex', ...launchArgs],
264
+ pid: process.pid,
265
+ status: profile ? 'running' : 'manual',
266
+ reviveCapability: profile ? 'revivable' : 'manual',
267
+ surfaceMode: 'host',
268
+ lastStartedAt: new Date().toISOString(),
269
+ lastHeartbeatAt: new Date().toISOString(),
270
+ });
243
271
  const sessions = new Map();
244
272
  const pendingSessionCreations = new Map();
245
273
  const conversationCache = new Map();
@@ -409,7 +437,7 @@ export async function main() {
409
437
  try {
410
438
  const sessionCwd = environment.cwd;
411
439
  const sessionModel = config?.model ?? (typeof args.model === 'string' ? args.model : undefined);
412
- const storedThreadId = loadStoredThreadId(agentId, conversationId, sessionCwd);
440
+ const storedThreadId = loadStoredThreadId(runtimeId, agentId, conversationId, environment.baseCwd, environment.mode);
413
441
  if (config?.permissionMode
414
442
  && !codexPermissionEnvelope.availablePermissionModes.some((option) => option.value === config.permissionMode)) {
415
443
  throw new ExecutionEnvironmentError(`Permission mode "${config.permissionMode}" is not supported by this Codex host.`, 'This Canon host was started with stricter approval settings. Choose one of the advertised permission modes or restart the host with more permissive flags.');
@@ -587,7 +615,7 @@ export async function main() {
587
615
  const result = await session.adapter.runTurn(nextTurn.prompt, (event) => {
588
616
  session.lastActivity = Date.now();
589
617
  if (event.type === 'thread.started') {
590
- saveStoredThreadId(agentId, session.conversationId, session.cwd, event.threadId);
618
+ saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, event.threadId, session.environment.mode);
591
619
  console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Thread ${event.threadId}`);
592
620
  return;
593
621
  }
@@ -621,7 +649,7 @@ export async function main() {
621
649
  console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] ${line}`);
622
650
  }, turnImagePaths);
623
651
  if (result.threadId) {
624
- saveStoredThreadId(agentId, session.conversationId, session.cwd, result.threadId);
652
+ saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, result.threadId, session.environment.mode);
625
653
  }
626
654
  if (!result.interrupted && result.finalMessage) {
627
655
  await client.sendMessage(session.conversationId, result.finalMessage, {
@@ -677,7 +705,9 @@ export async function main() {
677
705
  },
678
706
  }).catch(() => { });
679
707
  await handoffFinalMessage(session.conversationId);
680
- clearStoredThreadId(agentId, session.conversationId);
708
+ if (error instanceof Error && /invalid|not found|unknown thread/i.test(error.message)) {
709
+ clearStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, session.environment.mode);
710
+ }
681
711
  console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Turn failed:`, error);
682
712
  }
683
713
  finally {
@@ -721,6 +751,12 @@ export async function main() {
721
751
  }),
722
752
  };
723
753
  const publishRuntimeHeartbeat = async () => {
754
+ heartbeatLocalRuntimeEntry(runtimeId, {
755
+ agentId,
756
+ agentName: profileAgentName,
757
+ cwd: workingDir,
758
+ baseCwd: workingDir,
759
+ });
724
760
  if (!streamConnected)
725
761
  return;
726
762
  await refreshKnownConversationIds().catch((error) => {
@@ -821,6 +857,13 @@ export async function main() {
821
857
  behavior: payload.behavior,
822
858
  workSessions: payload.workSessions,
823
859
  });
860
+ if (message.id) {
861
+ saveRuntimeSessionState(runtimeId, {
862
+ conversationId: payload.conversationId,
863
+ baseCwd: workingDir,
864
+ lastInboundMessageId: message.id,
865
+ });
866
+ }
824
867
  },
825
868
  onConnected: () => {
826
869
  streamConnected = true;
@@ -885,34 +928,51 @@ export async function main() {
885
928
  clearTurnState(conversation.id, agentId).catch(() => { });
886
929
  }
887
930
  for (const conversation of conversations) {
888
- if (!conversation.lastMessage || conversation.lastMessage.senderId === agentId)
889
- continue;
890
- const latestPage = await client.getMessagesPage(conversation.id, 1);
891
- const latestMessage = latestPage.messages[0];
892
- if (!latestMessage || latestMessage.senderId === agentId)
893
- continue;
894
- const senderTurnState = latestMessage.senderType === 'ai_agent'
895
- ? await loadSenderRuntimeState(conversation.id, latestMessage.senderId)
896
- : null;
897
- const triggerDecision = shouldTriggerAgentTurn({
898
- senderType: latestMessage.senderType ?? 'human',
899
- metadata: latestMessage.metadata,
900
- senderTurnState,
901
- });
902
- if (!triggerDecision.allow) {
903
- console.error(`[canon-codex] [${conversation.id.slice(0, 8)}] Skipping startup recovery for suppressed message (${triggerDecision.reason})`);
904
- continue;
905
- }
906
- console.error(`[canon-codex] [${conversation.id.slice(0, 8)}] Recovering latest inbound message on startup`);
907
- await enqueueInboundMessage({
931
+ const cursor = loadRuntimeSessionState(runtimeId, {
908
932
  conversationId: conversation.id,
909
- message: latestMessage,
910
- senderName: latestMessage.senderId,
911
- isOwner: ownerId != null && latestMessage.senderId === ownerId,
912
- behavior: latestPage.behavior,
913
- workSessions: latestPage.workSessions,
914
- hydratedPage: latestPage,
915
- });
933
+ baseCwd: workingDir,
934
+ })?.lastInboundMessageId;
935
+ const latestPage = await client.getMessagesPage(conversation.id, 25);
936
+ const inboundMessages = latestPage.messages
937
+ .filter((message) => message.senderId !== agentId)
938
+ .sort((a, b) => String(a.createdAt ?? '').localeCompare(String(b.createdAt ?? '')));
939
+ const cursorIndex = cursor
940
+ ? inboundMessages.findIndex((message) => message.id === cursor)
941
+ : -1;
942
+ const messagesToRecover = cursorIndex >= 0
943
+ ? inboundMessages.slice(cursorIndex + 1)
944
+ : inboundMessages.slice(-1);
945
+ for (const latestMessage of messagesToRecover) {
946
+ const senderTurnState = latestMessage.senderType === 'ai_agent'
947
+ ? await loadSenderRuntimeState(conversation.id, latestMessage.senderId)
948
+ : null;
949
+ const triggerDecision = shouldTriggerAgentTurn({
950
+ senderType: latestMessage.senderType ?? 'human',
951
+ metadata: latestMessage.metadata,
952
+ senderTurnState,
953
+ });
954
+ if (!triggerDecision.allow) {
955
+ console.error(`[canon-codex] [${conversation.id.slice(0, 8)}] Skipping startup recovery for suppressed message (${triggerDecision.reason})`);
956
+ continue;
957
+ }
958
+ console.error(`[canon-codex] [${conversation.id.slice(0, 8)}] Recovering inbound message on startup`);
959
+ await enqueueInboundMessage({
960
+ conversationId: conversation.id,
961
+ message: latestMessage,
962
+ senderName: latestMessage.senderId,
963
+ isOwner: ownerId != null && latestMessage.senderId === ownerId,
964
+ behavior: latestPage.behavior,
965
+ workSessions: latestPage.workSessions,
966
+ hydratedPage: latestPage,
967
+ });
968
+ if (latestMessage.id) {
969
+ saveRuntimeSessionState(runtimeId, {
970
+ conversationId: conversation.id,
971
+ baseCwd: workingDir,
972
+ lastInboundMessageId: latestMessage.id,
973
+ });
974
+ }
975
+ }
916
976
  }
917
977
  }
918
978
  catch (error) {
@@ -1011,20 +1071,18 @@ export async function main() {
1011
1071
  await session.adapter.interrupt().catch(() => { });
1012
1072
  closeSession(session.conversationId);
1013
1073
  }
1014
- const activeProfile = getActiveProfile();
1015
- if (activeProfile)
1016
- releaseLock(activeProfile);
1074
+ markLocalRuntimeStopped(runtimeId);
1075
+ (lockHandle ?? getActiveProfileLock())?.release();
1017
1076
  process.exit(0);
1018
1077
  };
1019
1078
  process.on('SIGINT', shutdown);
1020
1079
  process.on('SIGTERM', shutdown);
1080
+ process.on('SIGHUP', shutdown);
1021
1081
  console.error('[canon-codex] Ready — sessions created on demand');
1022
1082
  await new Promise(() => { });
1023
1083
  }
1024
1084
  runCli(import.meta.url, main, (error) => {
1025
1085
  console.error('[canon-codex] Fatal error:', error);
1026
- const activeProfile = getActiveProfile();
1027
- if (activeProfile)
1028
- releaseLock(activeProfile);
1086
+ getActiveProfileLock()?.release();
1029
1087
  process.exit(1);
1030
1088
  });
package/dist/register.js CHANGED
@@ -1,13 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { setDefaultResultOrder } from 'node:dns';
3
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
- import { homedir } from 'node:os';
5
- import { join } from 'node:path';
3
+ import { readFileSync } from 'node:fs';
6
4
  import { parseArgs } from 'node:util';
7
- import { registerAndWaitForApproval } from '@canonmsg/core';
5
+ import { ackRegistrationApproval, clearPendingRegistration, getOrCreatePendingRegistration, registerAndWaitForApproval, updatePendingRegistration, upsertAgentProfile, AGENTS_PATH, } from '@canonmsg/core';
8
6
  import { runCli } from './cli-entry.js';
9
- const CANON_DIR = join(homedir(), '.canon');
10
- const AGENTS_PATH = join(CANON_DIR, 'agents.json');
11
7
  export async function main() {
12
8
  setDefaultResultOrder('ipv4first');
13
9
  const { values } = parseArgs({
@@ -34,6 +30,7 @@ export async function main() {
34
30
  // No existing profile state.
35
31
  }
36
32
  console.log(`Registering Codex agent "${values.name}" (profile: ${profileName})...`);
33
+ const pending = getOrCreatePendingRegistration(profileName, 'codex');
37
34
  const result = await registerAndWaitForApproval({
38
35
  name: values.name,
39
36
  description: values.description,
@@ -42,8 +39,14 @@ export async function main() {
42
39
  clientType: 'codex',
43
40
  baseUrl: values['base-url'],
44
41
  requestedAgentId: existingAgentId,
42
+ localRegistrationId: pending.localRegistrationId,
45
43
  }, {
46
- onSubmitted: (requestId) => {
44
+ onSubmitted: (requestId, pollToken) => {
45
+ updatePendingRegistration(profileName, {
46
+ requestId,
47
+ pollToken,
48
+ clientType: 'codex',
49
+ });
47
50
  console.log(`Registration submitted (request ID: ${requestId}).`);
48
51
  console.log('Waiting for approval in Canon app...');
49
52
  },
@@ -54,24 +57,26 @@ export async function main() {
54
57
  console.log('');
55
58
  switch (result.status) {
56
59
  case 'approved': {
57
- mkdirSync(CANON_DIR, { recursive: true });
58
- let profiles = {};
59
- try {
60
- profiles = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
60
+ if (!result.apiKey || !result.agentId || !result.agentName) {
61
+ console.error('Approval completed but Canon did not return a usable API key. Run this command again to resume key pickup.');
62
+ process.exit(1);
61
63
  }
62
- catch {
63
- // File does not exist yet.
64
- }
65
- profiles[profileName] = {
64
+ upsertAgentProfile(profileName, {
66
65
  apiKey: result.apiKey,
67
66
  agentId: result.agentId,
68
67
  agentName: result.agentName,
69
68
  registeredAt: new Date().toISOString(),
70
- };
71
- writeFileSync(AGENTS_PATH, JSON.stringify(profiles, null, 2));
69
+ clientType: 'codex',
70
+ ...(typeof values['base-url'] === 'string' ? { baseUrl: values['base-url'] } : {}),
71
+ });
72
+ if (result.requestId) {
73
+ await ackRegistrationApproval(values['base-url'], result.requestId, result.pollToken);
74
+ }
75
+ clearPendingRegistration(profileName);
72
76
  console.log(`Approved! Agent: ${result.agentName} (${result.agentId})`);
73
77
  console.log(`Saved as profile "${profileName}" in ~/.canon/agents.json`);
74
- console.log('Start it with: canon-codex --cwd /path/to/project');
78
+ console.log('Start it with: CANON_AGENT=' + profileName + ' canon-codex --cwd /path/to/project');
79
+ console.log('Keep that terminal open. Closing it takes this local agent offline.');
75
80
  break;
76
81
  }
77
82
  case 'rejected':
@@ -1,3 +1,4 @@
1
- export declare function loadStoredThreadId(agentId: string, conversationId: string, cwd: string): string | null;
2
- export declare function saveStoredThreadId(agentId: string, conversationId: string, cwd: string, threadId: string): void;
3
- export declare function clearStoredThreadId(agentId: string, conversationId: string): void;
1
+ import { type ExecutionEnvironmentMode } from '@canonmsg/core';
2
+ export declare function loadStoredThreadId(runtimeId: string | null, agentId: string, conversationId: string, baseCwd: string, executionMode?: ExecutionEnvironmentMode): string | null;
3
+ export declare function saveStoredThreadId(runtimeId: string | null, agentId: string, conversationId: string, baseCwd: string, threadId: string, executionMode?: ExecutionEnvironmentMode): void;
4
+ export declare function clearStoredThreadId(runtimeId: string | null, agentId: string, conversationId: string, baseCwd?: string, executionMode?: ExecutionEnvironmentMode): void;
@@ -1,6 +1,6 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { CANON_DIR } from '@canonmsg/core';
3
+ import { CANON_DIR, clearRuntimeSessionState, loadRuntimeSessionState, saveRuntimeSessionState, } from '@canonmsg/core';
4
4
  const STORE_PATH = join(CANON_DIR, 'codex-sessions.json');
5
5
  function loadStore() {
6
6
  try {
@@ -14,24 +14,50 @@ function saveStore(store) {
14
14
  mkdirSync(CANON_DIR, { recursive: true });
15
15
  writeFileSync(STORE_PATH, JSON.stringify(store, null, 2));
16
16
  }
17
- export function loadStoredThreadId(agentId, conversationId, cwd) {
17
+ export function loadStoredThreadId(runtimeId, agentId, conversationId, baseCwd, executionMode) {
18
+ if (runtimeId) {
19
+ const state = loadRuntimeSessionState(runtimeId, {
20
+ conversationId,
21
+ baseCwd,
22
+ executionMode,
23
+ });
24
+ if (state?.threadId)
25
+ return state.threadId;
26
+ }
18
27
  const store = loadStore();
19
28
  const record = store.agents[agentId]?.[conversationId];
20
- if (!record || record.cwd !== cwd)
29
+ if (!record || record.cwd !== baseCwd)
21
30
  return null;
22
31
  return record.threadId;
23
32
  }
24
- export function saveStoredThreadId(agentId, conversationId, cwd, threadId) {
33
+ export function saveStoredThreadId(runtimeId, agentId, conversationId, baseCwd, threadId, executionMode) {
34
+ if (runtimeId) {
35
+ saveRuntimeSessionState(runtimeId, {
36
+ conversationId,
37
+ baseCwd,
38
+ executionMode,
39
+ threadId,
40
+ });
41
+ return;
42
+ }
25
43
  const store = loadStore();
26
44
  store.agents[agentId] ??= {};
27
45
  store.agents[agentId][conversationId] = {
28
46
  threadId,
29
- cwd,
47
+ cwd: baseCwd,
30
48
  updatedAt: new Date().toISOString(),
31
49
  };
32
50
  saveStore(store);
33
51
  }
34
- export function clearStoredThreadId(agentId, conversationId) {
52
+ export function clearStoredThreadId(runtimeId, agentId, conversationId, baseCwd, executionMode) {
53
+ if (runtimeId) {
54
+ clearRuntimeSessionState(runtimeId, {
55
+ conversationId,
56
+ baseCwd,
57
+ executionMode,
58
+ });
59
+ return;
60
+ }
35
61
  const store = loadStore();
36
62
  if (!store.agents[agentId]?.[conversationId])
37
63
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,8 +29,8 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@canonmsg/agent-sdk": "^0.9.2",
33
- "@canonmsg/core": "^0.10.0"
32
+ "@canonmsg/agent-sdk": "^0.10.1",
33
+ "@canonmsg/core": "^0.13.0"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.0.0"