@canonmsg/codex-plugin 0.9.6 → 0.9.8
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 +32 -14
- 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,6 +1,7 @@
|
|
|
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
7
|
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';
|
|
@@ -207,6 +208,9 @@ function summarizeCommand(command) {
|
|
|
207
208
|
function sleep(ms) {
|
|
208
209
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
209
210
|
}
|
|
211
|
+
function uniqueStrings(values) {
|
|
212
|
+
return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
|
|
213
|
+
}
|
|
210
214
|
export async function main() {
|
|
211
215
|
setDefaultResultOrder('ipv4first');
|
|
212
216
|
const { values: args } = parseArgs({
|
|
@@ -525,6 +529,7 @@ export async function main() {
|
|
|
525
529
|
turnState: 'idle',
|
|
526
530
|
currentTurnId: null,
|
|
527
531
|
currentTurnOpenedAt: null,
|
|
532
|
+
activeSelfContextId: null,
|
|
528
533
|
lastAcceptedIntent: null,
|
|
529
534
|
lastActivity: Date.now(),
|
|
530
535
|
typingKeepaliveTimer: null,
|
|
@@ -553,8 +558,8 @@ export async function main() {
|
|
|
553
558
|
pendingSessionCreations.delete(conversationId);
|
|
554
559
|
}
|
|
555
560
|
}
|
|
556
|
-
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = []) {
|
|
557
|
-
const nextPrompt = { prompt, intent, sourceMessageId, markAccepted, imagePaths };
|
|
561
|
+
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = [], mediaAddDirs = []) {
|
|
562
|
+
const nextPrompt = { prompt, intent, sourceMessageId, markAccepted, imagePaths, mediaAddDirs };
|
|
558
563
|
if (toFront) {
|
|
559
564
|
session.queue.unshift(nextPrompt);
|
|
560
565
|
}
|
|
@@ -582,6 +587,7 @@ export async function main() {
|
|
|
582
587
|
const imagePaths = materialized
|
|
583
588
|
.map((attachment) => getCodexImagePath(attachment))
|
|
584
589
|
.filter((path) => path !== null);
|
|
590
|
+
const mediaAddDirs = uniqueStrings(materialized.map((attachment) => dirname(attachment.path)));
|
|
585
591
|
const content = renderInboundContent(input.message, materialized);
|
|
586
592
|
const hydrated = await loadHydratedInboundContext({
|
|
587
593
|
conversationId: input.conversationId,
|
|
@@ -591,11 +597,11 @@ export async function main() {
|
|
|
591
597
|
hydratedPage: input.hydratedPage,
|
|
592
598
|
});
|
|
593
599
|
const behavior = input.behavior ?? hydrated.behavior;
|
|
594
|
-
const
|
|
595
|
-
? hydrated.
|
|
596
|
-
: Array.isArray(input.
|
|
597
|
-
? input.
|
|
598
|
-
: hydrated.
|
|
600
|
+
const selfContexts = hydrated.hydratedFromPage
|
|
601
|
+
? hydrated.selfContexts
|
|
602
|
+
: Array.isArray(input.selfContexts)
|
|
603
|
+
? input.selfContexts
|
|
604
|
+
: hydrated.selfContexts;
|
|
599
605
|
const participantContext = hydrated.participantContext;
|
|
600
606
|
const autoReply = decideAutoReply(participantContext, behavior);
|
|
601
607
|
if (!autoReply.allow) {
|
|
@@ -612,14 +618,17 @@ export async function main() {
|
|
|
612
618
|
const userMessage = error instanceof ExecutionEnvironmentError ? error.userMessage : message;
|
|
613
619
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Failed to create session: ${message}`);
|
|
614
620
|
await client.sendMessage(input.conversationId, `I couldn't start a coding session for this workspace: ${userMessage}`, {
|
|
621
|
+
...(selfContexts[0]?.id ? { selfContextId: selfContexts[0].id } : {}),
|
|
615
622
|
metadata: {
|
|
616
623
|
turnSemantics: 'turn_complete',
|
|
617
624
|
turnComplete: true,
|
|
625
|
+
replyBehavior: 'suppress_auto_reply',
|
|
618
626
|
},
|
|
619
627
|
}).catch(() => { });
|
|
620
628
|
return;
|
|
621
629
|
}
|
|
622
630
|
const turnMetadata = normalizeTurnMetadata(input.message.metadata);
|
|
631
|
+
session.activeSelfContextId = selfContexts[0]?.id ?? null;
|
|
623
632
|
const deliveryIntent = turnMetadata?.deliveryIntent ?? 'queue';
|
|
624
633
|
const shouldMarkAccepted = turnMetadata?.inboundDisposition === 'queued';
|
|
625
634
|
const prompt = buildCanonPrompt({
|
|
@@ -627,18 +636,17 @@ export async function main() {
|
|
|
627
636
|
conversationId: input.conversationId,
|
|
628
637
|
participantContext,
|
|
629
638
|
behavior,
|
|
630
|
-
|
|
631
|
-
workSessions,
|
|
639
|
+
selfContexts,
|
|
632
640
|
});
|
|
633
641
|
if (session.running && deliveryIntent === 'interrupt') {
|
|
634
|
-
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths);
|
|
642
|
+
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs);
|
|
635
643
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Interrupting current turn for explicit human send-now`);
|
|
636
644
|
await session.adapter.interrupt().catch(() => { });
|
|
637
645
|
clearStreaming(input.conversationId);
|
|
638
646
|
client.setTyping(input.conversationId, false).catch(() => { });
|
|
639
647
|
return;
|
|
640
648
|
}
|
|
641
|
-
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths);
|
|
649
|
+
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs);
|
|
642
650
|
}
|
|
643
651
|
async function runNextTurn(session) {
|
|
644
652
|
if (session.running || session.closed)
|
|
@@ -668,6 +676,7 @@ export async function main() {
|
|
|
668
676
|
throw new ExecutionEnvironmentError(modelGuard, modelGuard);
|
|
669
677
|
}
|
|
670
678
|
const turnImagePaths = nextTurn.imagePaths ?? [];
|
|
679
|
+
const turnMediaAddDirs = nextTurn.mediaAddDirs ?? [];
|
|
671
680
|
const handleCodexEvent = (event) => {
|
|
672
681
|
session.lastActivity = Date.now();
|
|
673
682
|
if (event.type === 'thread.started') {
|
|
@@ -707,7 +716,7 @@ export async function main() {
|
|
|
707
716
|
clearStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, session.environment.mode);
|
|
708
717
|
session.adapter.clearThreadId();
|
|
709
718
|
};
|
|
710
|
-
const runTurnOnce = () => session.adapter.runTurn(nextTurn.prompt, handleCodexEvent, logCodexLine, turnImagePaths);
|
|
719
|
+
const runTurnOnce = () => session.adapter.runTurn(nextTurn.prompt, handleCodexEvent, logCodexLine, turnImagePaths, turnMediaAddDirs);
|
|
711
720
|
let result = await runTurnOnce();
|
|
712
721
|
if (!result.interrupted
|
|
713
722
|
&& !result.finalMessage
|
|
@@ -726,6 +735,9 @@ export async function main() {
|
|
|
726
735
|
clearStoredThread();
|
|
727
736
|
}
|
|
728
737
|
await client.sendMessage(session.conversationId, result.finalMessage, {
|
|
738
|
+
...(session.activeSelfContextId
|
|
739
|
+
? { selfContextId: session.activeSelfContextId }
|
|
740
|
+
: {}),
|
|
729
741
|
metadata: {
|
|
730
742
|
turnId: session.currentTurnId,
|
|
731
743
|
turnSemantics: 'turn_complete',
|
|
@@ -744,6 +756,9 @@ export async function main() {
|
|
|
744
756
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Turn exited ${result.exitCode}: ${result.errorText}`);
|
|
745
757
|
}
|
|
746
758
|
await client.sendMessage(session.conversationId, userVisibleError, {
|
|
759
|
+
...(session.activeSelfContextId
|
|
760
|
+
? { selfContextId: session.activeSelfContextId }
|
|
761
|
+
: {}),
|
|
747
762
|
metadata: {
|
|
748
763
|
turnId: session.currentTurnId,
|
|
749
764
|
turnSemantics: 'turn_complete',
|
|
@@ -772,6 +787,9 @@ export async function main() {
|
|
|
772
787
|
session.state.lastError = message;
|
|
773
788
|
writeState(session);
|
|
774
789
|
await client.sendMessage(session.conversationId, message, {
|
|
790
|
+
...(session.activeSelfContextId
|
|
791
|
+
? { selfContextId: session.activeSelfContextId }
|
|
792
|
+
: {}),
|
|
775
793
|
metadata: {
|
|
776
794
|
turnId: session.currentTurnId,
|
|
777
795
|
turnSemantics: 'turn_complete',
|
|
@@ -968,7 +986,7 @@ export async function main() {
|
|
|
968
986
|
senderName: message.senderName || message.senderId,
|
|
969
987
|
isOwner: message.isOwner ?? (ownerId != null && message.senderId === ownerId),
|
|
970
988
|
behavior: payload.behavior,
|
|
971
|
-
|
|
989
|
+
selfContexts: payload.selfContexts,
|
|
972
990
|
});
|
|
973
991
|
if (message.id) {
|
|
974
992
|
saveRuntimeSessionState(runtimeId, {
|
|
@@ -1075,7 +1093,7 @@ export async function main() {
|
|
|
1075
1093
|
senderName: latestMessage.senderId,
|
|
1076
1094
|
isOwner: ownerId != null && latestMessage.senderId === ownerId,
|
|
1077
1095
|
behavior: latestPage.behavior,
|
|
1078
|
-
|
|
1096
|
+
selfContexts: latestPage.selfContexts,
|
|
1079
1097
|
hydratedPage: latestPage,
|
|
1080
1098
|
});
|
|
1081
1099
|
if (latestMessage.id) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
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.1.
|
|
33
|
-
"@canonmsg/core": "^0.15.
|
|
32
|
+
"@canonmsg/agent-sdk": "^1.1.4",
|
|
33
|
+
"@canonmsg/core": "^0.15.4"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|