@canonmsg/codex-plugin 0.9.5 → 0.9.6
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 +1 -0
- package/dist/adapter.js +13 -12
- package/dist/host.js +72 -40
- package/dist/permission-mode.js +1 -5
- package/package.json +2 -2
package/dist/adapter.d.ts
CHANGED
|
@@ -66,5 +66,6 @@ export declare class CodexConversationAdapter {
|
|
|
66
66
|
interrupt(): Promise<void>;
|
|
67
67
|
runTurn(prompt: string, onEvent: (event: CodexEvent) => void, onLog?: (line: string) => void, imagePaths?: readonly string[]): Promise<CodexTurnResult>;
|
|
68
68
|
private buildArgs;
|
|
69
|
+
private canResumeWithCurrentPolicy;
|
|
69
70
|
private clearActiveProcess;
|
|
70
71
|
}
|
package/dist/adapter.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
|
+
import { isRecoverableCodexThreadError } from './error-format.js';
|
|
3
4
|
export class CodexConversationAdapter {
|
|
4
5
|
cwd;
|
|
5
6
|
codexBin;
|
|
@@ -121,6 +122,8 @@ export class CodexConversationAdapter {
|
|
|
121
122
|
if (isIgnorableCodexLog(trimmed))
|
|
122
123
|
return;
|
|
123
124
|
lastErrorText = trimmed;
|
|
125
|
+
if (isRecoverableCodexThreadError(trimmed))
|
|
126
|
+
return;
|
|
124
127
|
onLog?.(trimmed);
|
|
125
128
|
});
|
|
126
129
|
return await new Promise((resolve, reject) => {
|
|
@@ -145,7 +148,7 @@ export class CodexConversationAdapter {
|
|
|
145
148
|
});
|
|
146
149
|
}
|
|
147
150
|
buildArgs(prompt, imagePaths = []) {
|
|
148
|
-
if (this.threadId) {
|
|
151
|
+
if (this.threadId && this.canResumeWithCurrentPolicy()) {
|
|
149
152
|
const args = ['exec', 'resume', '--json', '--skip-git-repo-check'];
|
|
150
153
|
if (this.model) {
|
|
151
154
|
args.push('-m', this.model);
|
|
@@ -168,10 +171,12 @@ export class CodexConversationAdapter {
|
|
|
168
171
|
args.push(this.threadId, prompt);
|
|
169
172
|
return args;
|
|
170
173
|
}
|
|
174
|
+
if (this.threadId) {
|
|
175
|
+
this.threadId = null;
|
|
176
|
+
}
|
|
171
177
|
const args = ['exec', '--json', '--color', 'never', '-C', this.cwd, '--skip-git-repo-check'];
|
|
172
178
|
const execMode = resolveExecMode({
|
|
173
179
|
sandbox: this.sandbox,
|
|
174
|
-
approvalPolicy: this.legacyApprovalPolicy,
|
|
175
180
|
fullAuto: this.fullAuto,
|
|
176
181
|
bypassApprovalsAndSandbox: this.bypassApprovalsAndSandbox,
|
|
177
182
|
});
|
|
@@ -202,6 +207,12 @@ export class CodexConversationAdapter {
|
|
|
202
207
|
args.push(prompt);
|
|
203
208
|
return args;
|
|
204
209
|
}
|
|
210
|
+
canResumeWithCurrentPolicy() {
|
|
211
|
+
if (this.bypassApprovalsAndSandbox || this.fullAuto) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
return this.sandbox === null;
|
|
215
|
+
}
|
|
205
216
|
clearActiveProcess() {
|
|
206
217
|
if (this.interruptTimer) {
|
|
207
218
|
clearTimeout(this.interruptTimer);
|
|
@@ -240,18 +251,8 @@ function resolveExecMode(input) {
|
|
|
240
251
|
if (input.fullAuto) {
|
|
241
252
|
return { fullAuto: true, bypassApprovalsAndSandbox: false };
|
|
242
253
|
}
|
|
243
|
-
if (shouldTranslateLegacyApprovalMode(input)) {
|
|
244
|
-
return { fullAuto: true, bypassApprovalsAndSandbox: false };
|
|
245
|
-
}
|
|
246
254
|
return { fullAuto: false, bypassApprovalsAndSandbox: false };
|
|
247
255
|
}
|
|
248
|
-
function shouldTranslateLegacyApprovalMode(input) {
|
|
249
|
-
// Newer Codex CLI releases no longer accept --ask-for-approval for `exec`.
|
|
250
|
-
// Keep the compatibility shim isolated here so the rest of the adapter only
|
|
251
|
-
// deals with the supported execution switches.
|
|
252
|
-
return input.approvalPolicy === 'never'
|
|
253
|
-
&& (input.sandbox === 'workspace-write' || input.sandbox == null);
|
|
254
|
-
}
|
|
255
256
|
function isIgnorableCodexLog(line) {
|
|
256
257
|
return [
|
|
257
258
|
'Reading additional input from stdin...',
|
package/dist/host.js
CHANGED
|
@@ -145,6 +145,48 @@ function resolveExecutionFallbackReason(environment) {
|
|
|
145
145
|
? null
|
|
146
146
|
: environment.reason;
|
|
147
147
|
}
|
|
148
|
+
function stringArg(args, key) {
|
|
149
|
+
const value = args[key];
|
|
150
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
151
|
+
}
|
|
152
|
+
function boolArg(args, key) {
|
|
153
|
+
return args[key] === true;
|
|
154
|
+
}
|
|
155
|
+
function resolveCodexEffectiveRuntimePolicy(input) {
|
|
156
|
+
const model = input.config?.model ?? stringArg(input.args, 'model');
|
|
157
|
+
const permissionMode = input.config?.permissionMode ?? input.permissionEnvelope.defaultPermissionMode;
|
|
158
|
+
if (permissionMode
|
|
159
|
+
&& !input.permissionEnvelope.availablePermissionModes.some((option) => option.value === permissionMode)) {
|
|
160
|
+
throw new ExecutionEnvironmentError(`Permission mode "${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.');
|
|
161
|
+
}
|
|
162
|
+
const approvalOverride = mapCanonPermissionToCodex(permissionMode);
|
|
163
|
+
const defaultSandbox = (stringArg(input.args, 'sandbox') ?? null);
|
|
164
|
+
const defaultApprovalPolicy = (stringArg(input.args, 'ask-for-approval') ?? null);
|
|
165
|
+
const sandbox = approvalOverride ? approvalOverride.sandbox : defaultSandbox;
|
|
166
|
+
const approvalPolicy = approvalOverride ? null : defaultApprovalPolicy;
|
|
167
|
+
const fullAuto = approvalOverride ? approvalOverride.fullAuto : boolArg(input.args, 'full-auto');
|
|
168
|
+
const bypassApprovalsAndSandbox = approvalOverride
|
|
169
|
+
? approvalOverride.bypassApprovalsAndSandbox
|
|
170
|
+
: boolArg(input.args, 'dangerously-bypass-approvals-and-sandbox');
|
|
171
|
+
const fingerprint = buildCodexThreadPolicyFingerprint({
|
|
172
|
+
baseCwd: input.environment.baseCwd,
|
|
173
|
+
executionMode: input.environment.mode,
|
|
174
|
+
permissionMode: permissionMode ?? null,
|
|
175
|
+
sandbox,
|
|
176
|
+
approvalPolicy,
|
|
177
|
+
fullAuto,
|
|
178
|
+
bypassApprovalsAndSandbox,
|
|
179
|
+
});
|
|
180
|
+
return {
|
|
181
|
+
...(model ? { model } : {}),
|
|
182
|
+
...(permissionMode ? { permissionMode } : {}),
|
|
183
|
+
sandbox,
|
|
184
|
+
approvalPolicy,
|
|
185
|
+
fullAuto,
|
|
186
|
+
bypassApprovalsAndSandbox,
|
|
187
|
+
fingerprint,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
148
190
|
function buildCanonPrompt(input) {
|
|
149
191
|
return buildCanonHostPrompt({
|
|
150
192
|
hostLabel: 'Codex',
|
|
@@ -184,6 +226,11 @@ export async function main() {
|
|
|
184
226
|
},
|
|
185
227
|
strict: true,
|
|
186
228
|
});
|
|
229
|
+
if (typeof args['ask-for-approval'] === 'string') {
|
|
230
|
+
console.error('[canon-codex] --ask-for-approval is no longer supported by Canon. Use --full-auto, --sandbox, or Canon permission modes instead.');
|
|
231
|
+
process.exitCode = 1;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
187
234
|
workingDir = (typeof args.cwd === 'string' ? args.cwd : null) || process.cwd();
|
|
188
235
|
const workspaceDiscovery = buildConfiguredWorkspaceOptionsWithRoots({
|
|
189
236
|
primaryCwd: workingDir,
|
|
@@ -196,9 +243,6 @@ export async function main() {
|
|
|
196
243
|
for (const warning of workspaceDiscovery.warnings) {
|
|
197
244
|
console.error(`[canon-codex] ${warning}`);
|
|
198
245
|
}
|
|
199
|
-
if (typeof args['ask-for-approval'] === 'string') {
|
|
200
|
-
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.');
|
|
201
|
-
}
|
|
202
246
|
const codexBin = typeof args['codex-bin'] === 'string' ? args['codex-bin'] : 'codex';
|
|
203
247
|
const codexCliStatus = detectCodexCliVersion(codexBin);
|
|
204
248
|
if (codexCliStatus.version) {
|
|
@@ -311,10 +355,19 @@ export async function main() {
|
|
|
311
355
|
});
|
|
312
356
|
}
|
|
313
357
|
function writeState(session) {
|
|
358
|
+
const appliedAt = Date.now();
|
|
359
|
+
const controlState = {};
|
|
360
|
+
if (session.state.model !== undefined) {
|
|
361
|
+
controlState.model = { value: session.state.model, source: 'applied', appliedAt };
|
|
362
|
+
}
|
|
363
|
+
if (session.state.permissionMode !== undefined) {
|
|
364
|
+
controlState.permissionMode = { value: session.state.permissionMode, source: 'applied', appliedAt };
|
|
365
|
+
}
|
|
314
366
|
runtimeState.writeSessionState(session.conversationId, {
|
|
315
367
|
lastError: session.state.lastError,
|
|
316
368
|
model: session.state.model,
|
|
317
369
|
permissionMode: session.state.permissionMode,
|
|
370
|
+
controlState,
|
|
318
371
|
cwd: session.cwd,
|
|
319
372
|
executionMode: session.environment.mode,
|
|
320
373
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
@@ -433,38 +486,17 @@ export async function main() {
|
|
|
433
486
|
});
|
|
434
487
|
try {
|
|
435
488
|
const sessionCwd = environment.cwd;
|
|
436
|
-
const
|
|
437
|
-
|
|
489
|
+
const policy = resolveCodexEffectiveRuntimePolicy({
|
|
490
|
+
args,
|
|
491
|
+
config,
|
|
492
|
+
permissionEnvelope: codexPermissionEnvelope,
|
|
493
|
+
environment,
|
|
494
|
+
});
|
|
495
|
+
const modelGuard = buildCodexModelGuardMessage(policy.model, codexCliStatus);
|
|
438
496
|
if (modelGuard) {
|
|
439
497
|
throw new ExecutionEnvironmentError(modelGuard, modelGuard);
|
|
440
498
|
}
|
|
441
|
-
const
|
|
442
|
-
if (effectivePermissionMode
|
|
443
|
-
&& !codexPermissionEnvelope.availablePermissionModes.some((option) => option.value === effectivePermissionMode)) {
|
|
444
|
-
throw new ExecutionEnvironmentError(`Permission mode "${effectivePermissionMode}" 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.');
|
|
445
|
-
}
|
|
446
|
-
const approvalOverride = mapCanonPermissionToCodex(effectivePermissionMode);
|
|
447
|
-
const defaultSandbox = (typeof args.sandbox === 'string' ? args.sandbox : null);
|
|
448
|
-
const defaultFullAuto = Boolean(args['full-auto']);
|
|
449
|
-
const defaultBypass = Boolean(args['dangerously-bypass-approvals-and-sandbox']);
|
|
450
|
-
const legacyApprovalPolicy = (typeof args['ask-for-approval'] === 'string'
|
|
451
|
-
? args['ask-for-approval']
|
|
452
|
-
: null);
|
|
453
|
-
const effectiveSandbox = approvalOverride ? approvalOverride.sandbox : defaultSandbox;
|
|
454
|
-
const effectiveFullAuto = approvalOverride ? approvalOverride.fullAuto : defaultFullAuto;
|
|
455
|
-
const effectiveBypass = approvalOverride
|
|
456
|
-
? approvalOverride.bypassApprovalsAndSandbox
|
|
457
|
-
: defaultBypass;
|
|
458
|
-
const policyFingerprint = buildCodexThreadPolicyFingerprint({
|
|
459
|
-
baseCwd: environment.baseCwd,
|
|
460
|
-
executionMode: environment.mode,
|
|
461
|
-
permissionMode: effectivePermissionMode ?? null,
|
|
462
|
-
sandbox: effectiveSandbox,
|
|
463
|
-
approvalPolicy: approvalOverride ? null : legacyApprovalPolicy,
|
|
464
|
-
fullAuto: effectiveFullAuto,
|
|
465
|
-
bypassApprovalsAndSandbox: effectiveBypass,
|
|
466
|
-
});
|
|
467
|
-
const storedThreadId = loadStoredThreadId(runtimeId, agentId, conversationId, environment.baseCwd, environment.mode, policyFingerprint);
|
|
499
|
+
const storedThreadId = loadStoredThreadId(runtimeId, agentId, conversationId, environment.baseCwd, environment.mode, policy.fingerprint);
|
|
468
500
|
const session = {
|
|
469
501
|
conversationId,
|
|
470
502
|
cwd: sessionCwd,
|
|
@@ -473,23 +505,23 @@ export async function main() {
|
|
|
473
505
|
cwd: sessionCwd,
|
|
474
506
|
threadId: storedThreadId,
|
|
475
507
|
codexBin,
|
|
476
|
-
model:
|
|
477
|
-
sandbox:
|
|
478
|
-
approvalPolicy:
|
|
508
|
+
model: policy.model ?? null,
|
|
509
|
+
sandbox: policy.sandbox,
|
|
510
|
+
approvalPolicy: policy.approvalPolicy,
|
|
479
511
|
codexProfile: typeof args['codex-profile'] === 'string' ? args['codex-profile'] : null,
|
|
480
512
|
addDirs: args['add-dir'] ?? [],
|
|
481
513
|
configOverrides: args.config ?? [],
|
|
482
|
-
fullAuto:
|
|
483
|
-
bypassApprovalsAndSandbox:
|
|
514
|
+
fullAuto: policy.fullAuto,
|
|
515
|
+
bypassApprovalsAndSandbox: policy.bypassApprovalsAndSandbox,
|
|
484
516
|
}),
|
|
485
517
|
queue: [],
|
|
486
518
|
running: false,
|
|
487
519
|
state: {
|
|
488
|
-
model:
|
|
489
|
-
permissionMode:
|
|
520
|
+
model: policy.model,
|
|
521
|
+
permissionMode: policy.permissionMode,
|
|
490
522
|
state: 'idle',
|
|
491
523
|
},
|
|
492
|
-
policyFingerprint,
|
|
524
|
+
policyFingerprint: policy.fingerprint,
|
|
493
525
|
turnState: 'idle',
|
|
494
526
|
currentTurnId: null,
|
|
495
527
|
currentTurnOpenedAt: null,
|
package/dist/permission-mode.js
CHANGED
|
@@ -22,10 +22,6 @@ function codexOptionsThrough(mode) {
|
|
|
22
22
|
const index = CODEX_PERMISSION_OPTIONS.findIndex((option) => option.value === mode);
|
|
23
23
|
return index >= 0 ? CODEX_PERMISSION_OPTIONS.slice(0, index + 1) : [];
|
|
24
24
|
}
|
|
25
|
-
function isLegacyFullAuto(args) {
|
|
26
|
-
return args['ask-for-approval'] === 'never'
|
|
27
|
-
&& (args.sandbox === 'workspace-write' || args.sandbox == null);
|
|
28
|
-
}
|
|
29
25
|
export function deriveCodexPermissionEnvelope(args) {
|
|
30
26
|
if (args.sandbox === 'read-only') {
|
|
31
27
|
return {
|
|
@@ -46,7 +42,7 @@ export function deriveCodexPermissionEnvelope(args) {
|
|
|
46
42
|
availablePermissionModes: [...CODEX_PERMISSION_OPTIONS],
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
|
-
if (args['full-auto']
|
|
45
|
+
if (args['full-auto']) {
|
|
50
46
|
return {
|
|
51
47
|
defaultPermissionMode: 'full-auto',
|
|
52
48
|
availablePermissionModes: codexOptionsThrough('full-auto'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@canonmsg/agent-sdk": "^1.1.0",
|
|
33
|
-
"@canonmsg/core": "^0.15.
|
|
33
|
+
"@canonmsg/core": "^0.15.2"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|