@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 +10 -2
- package/dist/host-runtime.js +0 -2
- package/dist/host.js +98 -40
- package/dist/register.js +23 -18
- package/dist/session-store.d.ts +4 -3
- package/dist/session-store.js +32 -6
- package/package.json +3 -3
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.
|
|
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.
|
|
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
|
package/dist/host-runtime.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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':
|
package/dist/session-store.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
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;
|
package/dist/session-store.js
CHANGED
|
@@ -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,
|
|
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 !==
|
|
29
|
+
if (!record || record.cwd !== baseCwd)
|
|
21
30
|
return null;
|
|
22
31
|
return record.threadId;
|
|
23
32
|
}
|
|
24
|
-
export function saveStoredThreadId(agentId, conversationId,
|
|
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.
|
|
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.
|
|
33
|
-
"@canonmsg/core": "^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"
|