@canonmsg/codex-plugin 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +2 -1
- package/dist/adapter.js +25 -4
- package/dist/host.js +74 -37
- package/dist/session-store.js +15 -0
- package/package.json +3 -3
package/dist/adapter.d.ts
CHANGED
|
@@ -64,7 +64,8 @@ export declare class CodexConversationAdapter {
|
|
|
64
64
|
setModel(model: string | null): void;
|
|
65
65
|
isRunning(): boolean;
|
|
66
66
|
interrupt(): Promise<void>;
|
|
67
|
-
runTurn(prompt: string, onEvent: (event: CodexEvent) => void, onLog?: (line: string) => void, imagePaths?: readonly string[]): Promise<CodexTurnResult>;
|
|
67
|
+
runTurn(prompt: string, onEvent: (event: CodexEvent) => void, onLog?: (line: string) => void, imagePaths?: readonly string[], extraAddDirs?: readonly string[]): Promise<CodexTurnResult>;
|
|
68
|
+
private buildAddDirs;
|
|
68
69
|
private buildArgs;
|
|
69
70
|
private canResumeWithCurrentPolicy;
|
|
70
71
|
private clearActiveProcess;
|
package/dist/adapter.js
CHANGED
|
@@ -51,11 +51,11 @@ export class CodexConversationAdapter {
|
|
|
51
51
|
this.child.kill('SIGKILL');
|
|
52
52
|
}, 5_000);
|
|
53
53
|
}
|
|
54
|
-
async runTurn(prompt, onEvent, onLog, imagePaths = []) {
|
|
54
|
+
async runTurn(prompt, onEvent, onLog, imagePaths = [], extraAddDirs = []) {
|
|
55
55
|
if (this.child) {
|
|
56
56
|
throw new Error('A Codex turn is already in progress for this conversation');
|
|
57
57
|
}
|
|
58
|
-
const args = this.buildArgs(prompt, imagePaths);
|
|
58
|
+
const args = this.buildArgs(prompt, imagePaths, extraAddDirs);
|
|
59
59
|
const child = spawn(this.codexBin, args, {
|
|
60
60
|
cwd: this.cwd,
|
|
61
61
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -147,7 +147,19 @@ export class CodexConversationAdapter {
|
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
buildAddDirs(extraAddDirs = []) {
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
const dirs = [];
|
|
153
|
+
for (const value of [...this.addDirs, ...extraAddDirs]) {
|
|
154
|
+
const trimmed = value.trim();
|
|
155
|
+
if (!trimmed || seen.has(trimmed))
|
|
156
|
+
continue;
|
|
157
|
+
seen.add(trimmed);
|
|
158
|
+
dirs.push(value);
|
|
159
|
+
}
|
|
160
|
+
return dirs;
|
|
161
|
+
}
|
|
162
|
+
buildArgs(prompt, imagePaths = [], extraAddDirs = []) {
|
|
151
163
|
if (this.threadId && this.canResumeWithCurrentPolicy()) {
|
|
152
164
|
const args = ['exec', 'resume', '--json', '--skip-git-repo-check'];
|
|
153
165
|
if (this.model) {
|
|
@@ -159,6 +171,9 @@ export class CodexConversationAdapter {
|
|
|
159
171
|
for (const configOverride of this.configOverrides) {
|
|
160
172
|
args.push('-c', configOverride);
|
|
161
173
|
}
|
|
174
|
+
for (const addDir of this.buildAddDirs(extraAddDirs)) {
|
|
175
|
+
args.push('--add-dir', addDir);
|
|
176
|
+
}
|
|
162
177
|
if (this.fullAuto) {
|
|
163
178
|
args.push('--full-auto');
|
|
164
179
|
}
|
|
@@ -168,6 +183,9 @@ export class CodexConversationAdapter {
|
|
|
168
183
|
for (const imagePath of imagePaths) {
|
|
169
184
|
args.push('-i', imagePath);
|
|
170
185
|
}
|
|
186
|
+
if (imagePaths.length > 0) {
|
|
187
|
+
args.push('--');
|
|
188
|
+
}
|
|
171
189
|
args.push(this.threadId, prompt);
|
|
172
190
|
return args;
|
|
173
191
|
}
|
|
@@ -189,7 +207,7 @@ export class CodexConversationAdapter {
|
|
|
189
207
|
if (this.codexProfile) {
|
|
190
208
|
args.push('-p', this.codexProfile);
|
|
191
209
|
}
|
|
192
|
-
for (const addDir of this.
|
|
210
|
+
for (const addDir of this.buildAddDirs(extraAddDirs)) {
|
|
193
211
|
args.push('--add-dir', addDir);
|
|
194
212
|
}
|
|
195
213
|
for (const configOverride of this.configOverrides) {
|
|
@@ -204,6 +222,9 @@ export class CodexConversationAdapter {
|
|
|
204
222
|
for (const imagePath of imagePaths) {
|
|
205
223
|
args.push('-i', imagePath);
|
|
206
224
|
}
|
|
225
|
+
if (imagePaths.length > 0) {
|
|
226
|
+
args.push('--');
|
|
227
|
+
}
|
|
207
228
|
args.push(prompt);
|
|
208
229
|
return args;
|
|
209
230
|
}
|
package/dist/host.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setDefaultResultOrder } from 'node:dns';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
4
5
|
import { parseArgs } from 'node:util';
|
|
5
6
|
import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
|
|
6
|
-
import { buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, createConversationMetadataLoader, createRuntimeStatePublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, loadRuntimeSessionState, markLocalRuntimeStopped, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, loadHostSessionConfig, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, shouldTriggerAgentTurn, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
7
|
+
import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, createConversationMetadataLoader, createRuntimeStatePublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, loadRuntimeSessionState, markLocalRuntimeStopped, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, loadHostSessionConfig, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, shouldTriggerAgentTurn, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
7
8
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
8
9
|
import { CodexConversationAdapter, } from './adapter.js';
|
|
9
10
|
import { clearStoredThreadId, buildCodexThreadPolicyFingerprint, loadStoredThreadId, saveStoredThreadId, } from './session-store.js';
|
|
@@ -64,26 +65,9 @@ function buildCodexRuntimeDescriptor(input) {
|
|
|
64
65
|
defaultPermissionMode: input.defaultPermissionMode,
|
|
65
66
|
streamingTextMode: 'snapshot',
|
|
66
67
|
actions: [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
description: 'Interrupt the current Codex exec turn.',
|
|
71
|
-
aliases: ['stop'],
|
|
72
|
-
category: 'turn',
|
|
73
|
-
placements: ['composer_slash', 'command_palette'],
|
|
74
|
-
availability: ['busy'],
|
|
75
|
-
dispatch: { kind: 'signal', signal: 'interrupt' },
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
id: 'stop-and-clear-queue',
|
|
79
|
-
label: 'Stop & clear queue',
|
|
80
|
-
description: 'Interrupt the current Codex exec turn and drop queued Canon messages.',
|
|
81
|
-
aliases: ['stop-clear', 'clear-queue'],
|
|
82
|
-
category: 'turn',
|
|
83
|
-
placements: ['composer_slash', 'command_palette', 'session_strip'],
|
|
84
|
-
availability: ['busy_with_queue'],
|
|
85
|
-
dispatch: { kind: 'signal', signal: 'stop_and_drop' },
|
|
86
|
-
},
|
|
68
|
+
RUNTIME_STOP_ACTION,
|
|
69
|
+
RUNTIME_STOP_AND_DROP_ACTION,
|
|
70
|
+
RUNTIME_NEW_SESSION_ACTION,
|
|
87
71
|
],
|
|
88
72
|
});
|
|
89
73
|
if (input.models.length > 0) {
|
|
@@ -207,6 +191,9 @@ function summarizeCommand(command) {
|
|
|
207
191
|
function sleep(ms) {
|
|
208
192
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
209
193
|
}
|
|
194
|
+
function uniqueStrings(values) {
|
|
195
|
+
return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
|
|
196
|
+
}
|
|
210
197
|
export async function main() {
|
|
211
198
|
setDefaultResultOrder('ipv4first');
|
|
212
199
|
const { values: args } = parseArgs({
|
|
@@ -448,6 +435,32 @@ export async function main() {
|
|
|
448
435
|
client.setTyping(conversationId, false).catch(() => { });
|
|
449
436
|
sessions.delete(conversationId);
|
|
450
437
|
}
|
|
438
|
+
async function resetRuntimeSession(session) {
|
|
439
|
+
const conversationId = session.conversationId;
|
|
440
|
+
session.resetRequested = true;
|
|
441
|
+
const droppedPrompts = session.queue.splice(0);
|
|
442
|
+
await markQueuedPromptsRejected(conversationId, droppedPrompts);
|
|
443
|
+
clearStoredThreadId(runtimeId, agentId, conversationId, session.environment.baseCwd, session.environment.mode);
|
|
444
|
+
session.adapter.clearThreadId();
|
|
445
|
+
session.activeSelfContextId = null;
|
|
446
|
+
session.state.lastError = undefined;
|
|
447
|
+
if (session.running) {
|
|
448
|
+
await session.adapter.interrupt();
|
|
449
|
+
session.turnState = 'interrupted';
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
session.turnState = 'idle';
|
|
453
|
+
session.currentTurnId = null;
|
|
454
|
+
session.currentTurnOpenedAt = null;
|
|
455
|
+
session.lastAcceptedIntent = null;
|
|
456
|
+
session.resetRequested = false;
|
|
457
|
+
}
|
|
458
|
+
stopVisibleWorkSignal(session);
|
|
459
|
+
clearStreaming(conversationId);
|
|
460
|
+
client.setTyping(conversationId, false).catch(() => { });
|
|
461
|
+
writeState(session);
|
|
462
|
+
writeTurn(session);
|
|
463
|
+
}
|
|
451
464
|
function evictOldestIdle() {
|
|
452
465
|
let oldest = null;
|
|
453
466
|
for (const session of sessions.values()) {
|
|
@@ -525,7 +538,9 @@ export async function main() {
|
|
|
525
538
|
turnState: 'idle',
|
|
526
539
|
currentTurnId: null,
|
|
527
540
|
currentTurnOpenedAt: null,
|
|
541
|
+
activeSelfContextId: null,
|
|
528
542
|
lastAcceptedIntent: null,
|
|
543
|
+
resetRequested: false,
|
|
529
544
|
lastActivity: Date.now(),
|
|
530
545
|
typingKeepaliveTimer: null,
|
|
531
546
|
closed: false,
|
|
@@ -553,8 +568,8 @@ export async function main() {
|
|
|
553
568
|
pendingSessionCreations.delete(conversationId);
|
|
554
569
|
}
|
|
555
570
|
}
|
|
556
|
-
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = []) {
|
|
557
|
-
const nextPrompt = { prompt, intent, sourceMessageId, markAccepted, imagePaths };
|
|
571
|
+
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = [], mediaAddDirs = []) {
|
|
572
|
+
const nextPrompt = { prompt, intent, sourceMessageId, markAccepted, imagePaths, mediaAddDirs };
|
|
558
573
|
if (toFront) {
|
|
559
574
|
session.queue.unshift(nextPrompt);
|
|
560
575
|
}
|
|
@@ -582,6 +597,7 @@ export async function main() {
|
|
|
582
597
|
const imagePaths = materialized
|
|
583
598
|
.map((attachment) => getCodexImagePath(attachment))
|
|
584
599
|
.filter((path) => path !== null);
|
|
600
|
+
const mediaAddDirs = uniqueStrings(materialized.map((attachment) => dirname(attachment.path)));
|
|
585
601
|
const content = renderInboundContent(input.message, materialized);
|
|
586
602
|
const hydrated = await loadHydratedInboundContext({
|
|
587
603
|
conversationId: input.conversationId,
|
|
@@ -591,11 +607,11 @@ export async function main() {
|
|
|
591
607
|
hydratedPage: input.hydratedPage,
|
|
592
608
|
});
|
|
593
609
|
const behavior = input.behavior ?? hydrated.behavior;
|
|
594
|
-
const
|
|
595
|
-
? hydrated.
|
|
596
|
-
: Array.isArray(input.
|
|
597
|
-
? input.
|
|
598
|
-
: hydrated.
|
|
610
|
+
const selfContexts = hydrated.hydratedFromPage
|
|
611
|
+
? hydrated.selfContexts
|
|
612
|
+
: Array.isArray(input.selfContexts)
|
|
613
|
+
? input.selfContexts
|
|
614
|
+
: hydrated.selfContexts;
|
|
599
615
|
const participantContext = hydrated.participantContext;
|
|
600
616
|
const autoReply = decideAutoReply(participantContext, behavior);
|
|
601
617
|
if (!autoReply.allow) {
|
|
@@ -612,6 +628,7 @@ export async function main() {
|
|
|
612
628
|
const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
|
|
613
629
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Failed to create session: ${message}`);
|
|
614
630
|
await client.sendMessage(input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
|
|
631
|
+
...(selfContexts[0]?.id ? { selfContextId: selfContexts[0].id } : {}),
|
|
615
632
|
metadata: {
|
|
616
633
|
turnSemantics: 'turn_complete',
|
|
617
634
|
turnComplete: true,
|
|
@@ -621,6 +638,7 @@ export async function main() {
|
|
|
621
638
|
return;
|
|
622
639
|
}
|
|
623
640
|
const turnMetadata = normalizeTurnMetadata(input.message.metadata);
|
|
641
|
+
session.activeSelfContextId = selfContexts[0]?.id ?? null;
|
|
624
642
|
const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
|
|
625
643
|
const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
|
|
626
644
|
const prompt = buildCanonPrompt({
|
|
@@ -628,18 +646,17 @@ export async function main() {
|
|
|
628
646
|
conversationId: input.conversationId,
|
|
629
647
|
participantContext,
|
|
630
648
|
behavior,
|
|
631
|
-
|
|
632
|
-
workSessions,
|
|
649
|
+
selfContexts,
|
|
633
650
|
});
|
|
634
651
|
if (session.running && deliveryIntent === 'interrupt') {
|
|
635
|
-
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths);
|
|
652
|
+
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs);
|
|
636
653
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Interrupting current turn for explicit human send-now`);
|
|
637
654
|
await session.adapter.interrupt().catch(() => { });
|
|
638
655
|
clearStreaming(input.conversationId);
|
|
639
656
|
client.setTyping(input.conversationId, false).catch(() => { });
|
|
640
657
|
return;
|
|
641
658
|
}
|
|
642
|
-
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths);
|
|
659
|
+
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs);
|
|
643
660
|
}
|
|
644
661
|
async function runNextTurn(session) {
|
|
645
662
|
if (session.running || session.closed)
|
|
@@ -669,9 +686,13 @@ export async function main() {
|
|
|
669
686
|
throw new ExecutionEnvironmentError(modelGuard, modelGuard);
|
|
670
687
|
}
|
|
671
688
|
const turnImagePaths = nextTurn.imagePaths ?? [];
|
|
689
|
+
const turnMediaAddDirs = nextTurn.mediaAddDirs ?? [];
|
|
672
690
|
const handleCodexEvent = (event) => {
|
|
673
691
|
session.lastActivity = Date.now();
|
|
674
692
|
if (event.type === 'thread.started') {
|
|
693
|
+
if (session.resetRequested) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
675
696
|
saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, event.threadId, session.environment.mode, session.policyFingerprint);
|
|
676
697
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Thread ${event.threadId}`);
|
|
677
698
|
return;
|
|
@@ -708,7 +729,7 @@ export async function main() {
|
|
|
708
729
|
clearStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, session.environment.mode);
|
|
709
730
|
session.adapter.clearThreadId();
|
|
710
731
|
};
|
|
711
|
-
const runTurnOnce = () => session.adapter.runTurn(nextTurn.prompt, handleCodexEvent, logCodexLine, turnImagePaths);
|
|
732
|
+
const runTurnOnce = () => session.adapter.runTurn(nextTurn.prompt, handleCodexEvent, logCodexLine, turnImagePaths, turnMediaAddDirs);
|
|
712
733
|
let result = await runTurnOnce();
|
|
713
734
|
if (!result.interrupted
|
|
714
735
|
&& !result.finalMessage
|
|
@@ -719,7 +740,7 @@ export async function main() {
|
|
|
719
740
|
clearStoredThread();
|
|
720
741
|
result = await runTurnOnce();
|
|
721
742
|
}
|
|
722
|
-
if (result.threadId) {
|
|
743
|
+
if (result.threadId && !session.resetRequested) {
|
|
723
744
|
saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, result.threadId, session.environment.mode, session.policyFingerprint);
|
|
724
745
|
}
|
|
725
746
|
if (!result.interrupted && result.finalMessage) {
|
|
@@ -727,6 +748,9 @@ export async function main() {
|
|
|
727
748
|
clearStoredThread();
|
|
728
749
|
}
|
|
729
750
|
await client.sendMessage(session.conversationId, result.finalMessage, {
|
|
751
|
+
...(session.activeSelfContextId
|
|
752
|
+
? { selfContextId: session.activeSelfContextId }
|
|
753
|
+
: {}),
|
|
730
754
|
metadata: {
|
|
731
755
|
turnId: session.currentTurnId,
|
|
732
756
|
turnSemantics: 'turn_complete',
|
|
@@ -745,6 +769,9 @@ export async function main() {
|
|
|
745
769
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Turn exited ${result.exitCode}: ${result.errorText}`);
|
|
746
770
|
}
|
|
747
771
|
await client.sendMessage(session.conversationId, userVisibleError, {
|
|
772
|
+
...(session.activeSelfContextId
|
|
773
|
+
? { selfContextId: session.activeSelfContextId }
|
|
774
|
+
: {}),
|
|
748
775
|
metadata: {
|
|
749
776
|
turnId: session.currentTurnId,
|
|
750
777
|
turnSemantics: 'turn_complete',
|
|
@@ -773,6 +800,9 @@ export async function main() {
|
|
|
773
800
|
session.state.lastError = message;
|
|
774
801
|
writeState(session);
|
|
775
802
|
await client.sendMessage(session.conversationId, message, {
|
|
803
|
+
...(session.activeSelfContextId
|
|
804
|
+
? { selfContextId: session.activeSelfContextId }
|
|
805
|
+
: {}),
|
|
776
806
|
metadata: {
|
|
777
807
|
turnId: session.currentTurnId,
|
|
778
808
|
turnSemantics: 'turn_complete',
|
|
@@ -794,6 +824,7 @@ export async function main() {
|
|
|
794
824
|
session.currentTurnId = null;
|
|
795
825
|
session.currentTurnOpenedAt = null;
|
|
796
826
|
session.lastAcceptedIntent = null;
|
|
827
|
+
session.resetRequested = false;
|
|
797
828
|
session.lastActivity = Date.now();
|
|
798
829
|
writeState(session);
|
|
799
830
|
writeTurn(session);
|
|
@@ -969,7 +1000,7 @@ export async function main() {
|
|
|
969
1000
|
senderName: message.senderName || message.senderId,
|
|
970
1001
|
isOwner: message.isOwner ?? (ownerId != null && message.senderId === ownerId),
|
|
971
1002
|
behavior: payload.behavior,
|
|
972
|
-
|
|
1003
|
+
selfContexts: payload.selfContexts,
|
|
973
1004
|
});
|
|
974
1005
|
if (message.id) {
|
|
975
1006
|
saveRuntimeSessionState(runtimeId, {
|
|
@@ -1076,7 +1107,7 @@ export async function main() {
|
|
|
1076
1107
|
senderName: latestMessage.senderId,
|
|
1077
1108
|
isOwner: ownerId != null && latestMessage.senderId === ownerId,
|
|
1078
1109
|
behavior: latestPage.behavior,
|
|
1079
|
-
|
|
1110
|
+
selfContexts: latestPage.selfContexts,
|
|
1080
1111
|
hydratedPage: latestPage,
|
|
1081
1112
|
});
|
|
1082
1113
|
if (latestMessage.id) {
|
|
@@ -1136,7 +1167,7 @@ export async function main() {
|
|
|
1136
1167
|
continue;
|
|
1137
1168
|
const signal = raw;
|
|
1138
1169
|
const timestamp = signal.updatedAt ?? 0;
|
|
1139
|
-
if ((signal.type !== 'interrupt' && signal.type !== 'stop_and_drop')
|
|
1170
|
+
if ((signal.type !== 'interrupt' && signal.type !== 'stop_and_drop' && signal.type !== 'new_session')
|
|
1140
1171
|
|| timestamp <= (lastSeenSignal.get(conversationId) ?? 0)) {
|
|
1141
1172
|
continue;
|
|
1142
1173
|
}
|
|
@@ -1144,6 +1175,12 @@ export async function main() {
|
|
|
1144
1175
|
const session = sessions.get(conversationId);
|
|
1145
1176
|
if (!session || session.closed)
|
|
1146
1177
|
continue;
|
|
1178
|
+
if (signal.type === 'new_session') {
|
|
1179
|
+
console.error(`[canon-codex] [${conversationId.slice(0, 8)}] new_session signal`);
|
|
1180
|
+
await resetRuntimeSession(session);
|
|
1181
|
+
await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1147
1184
|
if (!session.running && (signal.type !== 'stop_and_drop' || session.queue.length === 0)) {
|
|
1148
1185
|
await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
|
|
1149
1186
|
continue;
|
package/dist/session-store.js
CHANGED
|
@@ -84,11 +84,26 @@ export function saveStoredThreadId(runtimeId, agentId, conversationId, baseCwd,
|
|
|
84
84
|
}
|
|
85
85
|
export function clearStoredThreadId(runtimeId, agentId, conversationId, baseCwd, executionMode) {
|
|
86
86
|
if (runtimeId) {
|
|
87
|
+
const existing = baseCwd
|
|
88
|
+
? loadRuntimeSessionState(runtimeId, {
|
|
89
|
+
conversationId,
|
|
90
|
+
baseCwd,
|
|
91
|
+
executionMode,
|
|
92
|
+
})
|
|
93
|
+
: null;
|
|
87
94
|
clearRuntimeSessionState(runtimeId, {
|
|
88
95
|
conversationId,
|
|
89
96
|
baseCwd,
|
|
90
97
|
executionMode,
|
|
91
98
|
});
|
|
99
|
+
if (existing?.lastInboundMessageId && baseCwd) {
|
|
100
|
+
saveRuntimeSessionState(runtimeId, {
|
|
101
|
+
conversationId,
|
|
102
|
+
baseCwd,
|
|
103
|
+
...(executionMode ? { executionMode } : {}),
|
|
104
|
+
lastInboundMessageId: existing.lastInboundMessageId,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
92
107
|
return;
|
|
93
108
|
}
|
|
94
109
|
const store = loadStore();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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": "^1.
|
|
33
|
-
"@canonmsg/core": "^0.
|
|
32
|
+
"@canonmsg/agent-sdk": "^1.2.0",
|
|
33
|
+
"@canonmsg/core": "^0.16.0"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|