@canonmsg/codex-plugin 0.29.3 → 0.29.4
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 -0
- package/dist/app-server-adapter.d.ts +5 -0
- package/dist/app-server-adapter.js +26 -7
- package/dist/host.d.ts +63 -3
- package/dist/host.js +27 -9
- package/package.json +2 -2
package/dist/adapter.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface CodexServerRequest {
|
|
|
45
45
|
params: Record<string, unknown>;
|
|
46
46
|
}
|
|
47
47
|
export interface CodexRunTurnOptions {
|
|
48
|
+
/** Original human text, before Canon adds conversation/reply context. */
|
|
49
|
+
skillInvocationText?: string;
|
|
48
50
|
planMode?: boolean;
|
|
49
51
|
onServerRequest?: (request: CodexServerRequest) => Promise<unknown>;
|
|
50
52
|
}
|
|
@@ -36,6 +36,7 @@ export declare class CodexAppServerAdapter {
|
|
|
36
36
|
private threadId;
|
|
37
37
|
private loadedThreadId;
|
|
38
38
|
private resolvedModel;
|
|
39
|
+
private observedSettings;
|
|
39
40
|
private resolvedReasoningEffort;
|
|
40
41
|
private currentTurnId;
|
|
41
42
|
private requestSeq;
|
|
@@ -67,6 +68,10 @@ export declare class CodexAppServerAdapter {
|
|
|
67
68
|
});
|
|
68
69
|
getThreadId(): string | null;
|
|
69
70
|
getResolvedModel(): string | null;
|
|
71
|
+
getObservedSettings(): Readonly<{
|
|
72
|
+
model?: string;
|
|
73
|
+
effort?: string;
|
|
74
|
+
}>;
|
|
70
75
|
getResolvedReasoningEffort(): string | null;
|
|
71
76
|
clearThreadId(): void;
|
|
72
77
|
setModel(model: string | null): void;
|
|
@@ -17,6 +17,8 @@ export class CodexAppServerAdapter {
|
|
|
17
17
|
threadId;
|
|
18
18
|
loadedThreadId = null;
|
|
19
19
|
resolvedModel = null;
|
|
20
|
+
// Public observations never inherit requested settings or fallback defaults.
|
|
21
|
+
observedSettings = {};
|
|
20
22
|
resolvedReasoningEffort;
|
|
21
23
|
currentTurnId = null;
|
|
22
24
|
requestSeq = 1;
|
|
@@ -53,16 +55,21 @@ export class CodexAppServerAdapter {
|
|
|
53
55
|
getResolvedModel() {
|
|
54
56
|
return this.resolvedModel ?? this.model;
|
|
55
57
|
}
|
|
58
|
+
getObservedSettings() {
|
|
59
|
+
return { ...this.observedSettings };
|
|
60
|
+
}
|
|
56
61
|
getResolvedReasoningEffort() {
|
|
57
62
|
return this.resolvedReasoningEffort ?? this.reasoningEffort;
|
|
58
63
|
}
|
|
59
64
|
clearThreadId() {
|
|
65
|
+
this.observedSettings = {};
|
|
60
66
|
this.threadId = null;
|
|
61
67
|
this.loadedThreadId = null;
|
|
62
68
|
this.resolvedModel = null;
|
|
63
69
|
this.resolvedReasoningEffort = this.reasoningEffort;
|
|
64
70
|
}
|
|
65
71
|
setModel(model) {
|
|
72
|
+
this.observedSettings = {};
|
|
66
73
|
this.model = model;
|
|
67
74
|
this.resolvedModel = model;
|
|
68
75
|
}
|
|
@@ -78,6 +85,7 @@ export class CodexAppServerAdapter {
|
|
|
78
85
|
const next = effort && effort.trim() ? effort.trim() : null;
|
|
79
86
|
if (next === this.reasoningEffort && next === this.resolvedReasoningEffort)
|
|
80
87
|
return;
|
|
88
|
+
this.observedSettings = { model: this.observedSettings.model };
|
|
81
89
|
this.reasoningEffort = next;
|
|
82
90
|
this.resolvedReasoningEffort = next;
|
|
83
91
|
if (this.threadId && this.loadedThreadId === this.threadId) {
|
|
@@ -110,6 +118,7 @@ export class CodexAppServerAdapter {
|
|
|
110
118
|
return await this.sendRequest(method, params);
|
|
111
119
|
}
|
|
112
120
|
close() {
|
|
121
|
+
this.observedSettings = {};
|
|
113
122
|
this.child?.kill('SIGTERM');
|
|
114
123
|
this.child = null;
|
|
115
124
|
this.initialized = false;
|
|
@@ -171,7 +180,7 @@ export class CodexAppServerAdapter {
|
|
|
171
180
|
turnPromise.catch(() => { });
|
|
172
181
|
const turnStarted = await this.sendRequest('turn/start', {
|
|
173
182
|
threadId: this.threadId,
|
|
174
|
-
input: await this.buildTurnInput(prompt, imagePaths),
|
|
183
|
+
input: await this.buildTurnInput(prompt, imagePaths, options.skillInvocationText),
|
|
175
184
|
...(this.model ? { model: this.model } : {}),
|
|
176
185
|
...this.sandboxPolicyPayload(_extraAddDirs),
|
|
177
186
|
collaborationMode: {
|
|
@@ -216,7 +225,9 @@ export class CodexAppServerAdapter {
|
|
|
216
225
|
}
|
|
217
226
|
rememberResolvedModel(result) {
|
|
218
227
|
const thread = result.thread;
|
|
219
|
-
|
|
228
|
+
const model = readString(thread, 'model') ?? readString(result, 'model');
|
|
229
|
+
this.observedSettings = { ...this.observedSettings, model: model ?? undefined };
|
|
230
|
+
this.resolvedModel = model ?? this.resolvedModel;
|
|
220
231
|
}
|
|
221
232
|
sandboxPolicyPayload(extraAddDirs) {
|
|
222
233
|
if (this.bypassApprovalsAndSandbox || this.sandbox === 'danger-full-access') {
|
|
@@ -290,22 +301,27 @@ export class CodexAppServerAdapter {
|
|
|
290
301
|
} while (cursor);
|
|
291
302
|
return models;
|
|
292
303
|
}
|
|
293
|
-
async buildTurnInput(prompt, imagePaths) {
|
|
294
|
-
const skillPrompt = parseSkillSlashPrompt(prompt);
|
|
304
|
+
async buildTurnInput(prompt, imagePaths, skillInvocationText) {
|
|
305
|
+
const skillPrompt = parseSkillSlashPrompt(skillInvocationText ?? prompt);
|
|
295
306
|
const input = [];
|
|
296
307
|
if (skillPrompt) {
|
|
297
308
|
const skills = await this.listSkills().catch(() => []);
|
|
298
309
|
const selected = skills.find((skill) => skill.name.toLowerCase() === skillPrompt.name.toLowerCase());
|
|
299
310
|
if (selected) {
|
|
300
311
|
input.push({ type: 'skill', name: selected.name, path: selected.path });
|
|
301
|
-
|
|
302
|
-
|
|
312
|
+
// Resolve the native skill from the actual message, retaining the
|
|
313
|
+
// framed prompt as context instead of parsing quoted/history text.
|
|
314
|
+
const text = skillInvocationText === undefined ? skillPrompt.prompt : prompt;
|
|
315
|
+
if (text.trim()) {
|
|
316
|
+
input.push({ type: 'text', text, text_elements: [] });
|
|
303
317
|
}
|
|
304
318
|
}
|
|
305
319
|
else {
|
|
306
320
|
input.push({
|
|
307
321
|
type: 'text',
|
|
308
|
-
text:
|
|
322
|
+
text: skillInvocationText === undefined
|
|
323
|
+
? `$${skillPrompt.name}${skillPrompt.prompt.trim() ? ` ${skillPrompt.prompt.trim()}` : ''}`
|
|
324
|
+
: prompt,
|
|
309
325
|
text_elements: [],
|
|
310
326
|
});
|
|
311
327
|
}
|
|
@@ -333,6 +349,7 @@ export class CodexAppServerAdapter {
|
|
|
333
349
|
this.currentOnLog?.(trimmed);
|
|
334
350
|
});
|
|
335
351
|
child.on('close', (code) => {
|
|
352
|
+
this.observedSettings = {};
|
|
336
353
|
const message = `Codex app-server exited${code === null ? '' : ` with code ${code}`}`;
|
|
337
354
|
this.initialized = false;
|
|
338
355
|
this.child = null;
|
|
@@ -428,6 +445,7 @@ export class CodexAppServerAdapter {
|
|
|
428
445
|
const settings = isRecord(params.threadSettings) ? params.threadSettings : {};
|
|
429
446
|
const model = readString(settings, 'model') ?? null;
|
|
430
447
|
const effort = readString(settings, 'effort') ?? null;
|
|
448
|
+
this.observedSettings = { model: model ?? undefined, effort: effort ?? undefined };
|
|
431
449
|
this.resolvedModel = model ?? this.resolvedModel;
|
|
432
450
|
this.resolvedReasoningEffort = effort;
|
|
433
451
|
this.currentOnEvent?.({ type: 'settings.updated', model, effort });
|
|
@@ -435,6 +453,7 @@ export class CodexAppServerAdapter {
|
|
|
435
453
|
}
|
|
436
454
|
if (method === 'model/rerouted') {
|
|
437
455
|
const model = readString(params, 'toModel') ?? null;
|
|
456
|
+
this.observedSettings = { model: model ?? undefined };
|
|
438
457
|
this.resolvedModel = model ?? this.resolvedModel;
|
|
439
458
|
this.currentOnEvent?.({
|
|
440
459
|
type: 'settings.updated',
|
package/dist/host.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type TurnArtifactRoutingDecision, type TurnArtifactRoutingMode, type RecoveryCheckpointTracker } from '@canonmsg/coding-agent-host';
|
|
3
|
-
import { type CanonRuntimeCommandDescriptor, type CanonRuntimeDescriptor, type CanonRuntimePresentationPolicy, type ExecutionEnvironmentMode, type PreparedExecutionEnvironment, type WorkspaceOption, type CanonWorkspaceRootMetadata, type RuntimeStreamingPayload, type TurnLifecycleState, type TurnOutputBlock, type TurnVerbosity, type TurnVerbosityConfig } from '@canonmsg/core';
|
|
4
|
-
import {
|
|
5
|
-
import { type
|
|
3
|
+
import { type CanonRuntimeCommandDescriptor, type CanonRuntimeDescriptor, type CanonRuntimeFact, type CanonRuntimePresentationPolicy, type ExecutionEnvironmentMode, type PreparedExecutionEnvironment, type WorkspaceOption, type CanonWorkspaceRootMetadata, type DeliveryIntent, type RuntimeStreamingPayload, type TurnLifecycleState, type TurnOutputBlock, type TurnVerbosity, type TurnVerbosityConfig } from '@canonmsg/core';
|
|
4
|
+
import type { AgentReplyAuthorityV1 } from '@canonmsg/backend-contracts';
|
|
5
|
+
import { CodexConversationAdapter, type CodexSandboxMode } from './adapter.js';
|
|
6
|
+
import { CodexAppServerAdapter, type CodexSkillMetadata } from './app-server-adapter.js';
|
|
6
7
|
import { deriveCodexPermissionEnvelope } from './permission-mode.js';
|
|
8
|
+
import { type CommandBlockTracker } from './turn-activity.js';
|
|
7
9
|
import { type CodexControlOption } from './model-catalog.js';
|
|
10
|
+
type CodexAdapter = CodexConversationAdapter | CodexAppServerAdapter;
|
|
8
11
|
interface HostSessionState {
|
|
9
12
|
lastError?: string;
|
|
10
13
|
model?: string;
|
|
@@ -21,6 +24,63 @@ export declare function buildCodexInitialSessionState(input: {
|
|
|
21
24
|
permissionMode?: string;
|
|
22
25
|
effort?: string | null;
|
|
23
26
|
}): HostSessionState;
|
|
27
|
+
type ArtifactRoutingMode = 'workspace-generated' | 'disabled';
|
|
28
|
+
interface Session {
|
|
29
|
+
conversationId: string;
|
|
30
|
+
cwd: string;
|
|
31
|
+
environment: PreparedExecutionEnvironment;
|
|
32
|
+
adapter: CodexAdapter;
|
|
33
|
+
queue: Array<{
|
|
34
|
+
prompt: string;
|
|
35
|
+
skillInvocationText?: string;
|
|
36
|
+
planMode?: boolean;
|
|
37
|
+
intent: DeliveryIntent;
|
|
38
|
+
sourceMessageId?: string | null;
|
|
39
|
+
markAccepted?: boolean;
|
|
40
|
+
imagePaths?: string[];
|
|
41
|
+
mediaAddDirs?: string[];
|
|
42
|
+
artifactRoutingMode?: ArtifactRoutingMode;
|
|
43
|
+
canUseCodexAppTools?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Resolved when this message arrived and carried here, so a prompt that
|
|
46
|
+
* waits in the queue still runs under the answer it arrived with. Absent on
|
|
47
|
+
* a continuation prompt (a plan-review result), which inherits whatever the
|
|
48
|
+
* conversation last resolved.
|
|
49
|
+
*/
|
|
50
|
+
turnVerbosity?: TurnVerbosity;
|
|
51
|
+
requestingUserId: string | null;
|
|
52
|
+
replyAuthority: AgentReplyAuthorityV1 | null;
|
|
53
|
+
}>;
|
|
54
|
+
running: boolean;
|
|
55
|
+
state: HostSessionState;
|
|
56
|
+
policyFingerprint: string;
|
|
57
|
+
turnState: TurnLifecycleState;
|
|
58
|
+
currentTurnId: string | null;
|
|
59
|
+
currentTurnOpenedAt: number | null;
|
|
60
|
+
currentTurnUpdatedAt: number | null;
|
|
61
|
+
currentTurnCanUseCodexAppTools: boolean;
|
|
62
|
+
currentReplyAuthority: AgentReplyAuthorityV1 | null;
|
|
63
|
+
/** Cancels Canon interactions created by the currently running Codex turn. */
|
|
64
|
+
currentTurnAbortController: AbortController | null;
|
|
65
|
+
/**
|
|
66
|
+
* The verbosity the RUNNING turn was opened with. Promoted off the queue
|
|
67
|
+
* entry at `runNextTurn` and never re-read mid-turn, so the live writer, the
|
|
68
|
+
* turn-state writer and the final's trail cannot disagree with each other.
|
|
69
|
+
*/
|
|
70
|
+
turnVerbosity: TurnVerbosity;
|
|
71
|
+
/** The running turn called `no_reply`: end it without posting anything. */
|
|
72
|
+
currentTurnSilenced: boolean;
|
|
73
|
+
activeSelfContextId: string | null;
|
|
74
|
+
lastAcceptedIntent: DeliveryIntent | null;
|
|
75
|
+
resetRequested: boolean;
|
|
76
|
+
lastActivity: number;
|
|
77
|
+
typingKeepaliveTimer: ReturnType<typeof setInterval> | null;
|
|
78
|
+
closed: boolean;
|
|
79
|
+
turnLiveText: string;
|
|
80
|
+
turnBlocks: TurnOutputBlock[];
|
|
81
|
+
turnCommandBlocks: CommandBlockTracker;
|
|
82
|
+
}
|
|
83
|
+
export declare function buildCodexSessionFacts(session?: Pick<Session, 'closed' | 'adapter'>): CanonRuntimeFact[];
|
|
24
84
|
export declare function createCodexRecoveryCheckpointTracker(persist: (messageId: string) => boolean): RecoveryCheckpointTracker;
|
|
25
85
|
/** Conservative fallback used only when native app-server discovery is unavailable. */
|
|
26
86
|
export declare const CODEX_EFFORT_OPTIONS: readonly CodexControlOption[];
|
package/dist/host.js
CHANGED
|
@@ -69,6 +69,23 @@ export function buildCodexInitialSessionState(input) {
|
|
|
69
69
|
state: 'idle',
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
+
export function buildCodexSessionFacts(session) {
|
|
73
|
+
if (!session || session.closed)
|
|
74
|
+
return [];
|
|
75
|
+
const adapter = session.adapter;
|
|
76
|
+
const appServer = adapter instanceof CodexAppServerAdapter;
|
|
77
|
+
const facts = [{
|
|
78
|
+
id: 'runtime', label: 'Runtime', value: appServer ? 'Codex app-server' : 'Codex CLI', group: 'runtime',
|
|
79
|
+
}];
|
|
80
|
+
if (appServer) {
|
|
81
|
+
const observed = adapter.getObservedSettings();
|
|
82
|
+
if (observed.model)
|
|
83
|
+
facts.push({ id: 'model', label: 'Model', value: observed.model, group: 'model' });
|
|
84
|
+
if (observed.effort)
|
|
85
|
+
facts.push({ id: 'reasoning', label: 'Reasoning', value: observed.effort, group: 'model' });
|
|
86
|
+
}
|
|
87
|
+
return facts;
|
|
88
|
+
}
|
|
72
89
|
const MAX_SESSIONS = 12;
|
|
73
90
|
export function createCodexRecoveryCheckpointTracker(persist) {
|
|
74
91
|
return createRecoveryCheckpointTracker(persist);
|
|
@@ -133,10 +150,10 @@ export function buildCodexRuntimeDescriptor(input) {
|
|
|
133
150
|
const commands = [
|
|
134
151
|
{
|
|
135
152
|
id: 'runtime-status',
|
|
136
|
-
label: '
|
|
137
|
-
description: 'Open
|
|
153
|
+
label: 'Session info',
|
|
154
|
+
description: 'Open Canon session information.',
|
|
138
155
|
primitive: 'runtime.status',
|
|
139
|
-
aliases: ['
|
|
156
|
+
aliases: ['session-info'],
|
|
140
157
|
category: 'details',
|
|
141
158
|
placements: ['composer_slash', 'command_palette'],
|
|
142
159
|
availability: ['always'],
|
|
@@ -1338,6 +1355,7 @@ export async function main() {
|
|
|
1338
1355
|
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = [], mediaAddDirs = [], planMode = false, turn = {}) {
|
|
1339
1356
|
const nextPrompt = {
|
|
1340
1357
|
prompt,
|
|
1358
|
+
skillInvocationText: turn.skillInvocationText,
|
|
1341
1359
|
intent,
|
|
1342
1360
|
sourceMessageId,
|
|
1343
1361
|
markAccepted,
|
|
@@ -1884,6 +1902,7 @@ export async function main() {
|
|
|
1884
1902
|
if (session.running && deliveryIntent === 'interrupt') {
|
|
1885
1903
|
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, {
|
|
1886
1904
|
...resolveCodexTurnModes(participantContext, input.message),
|
|
1905
|
+
skillInvocationText: input.message.senderType === 'human' ? input.message.text ?? undefined : undefined,
|
|
1887
1906
|
replyAuthority: input.replyAuthority ?? null,
|
|
1888
1907
|
});
|
|
1889
1908
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Interrupting current turn for explicit human send-now`);
|
|
@@ -1895,6 +1914,7 @@ export async function main() {
|
|
|
1895
1914
|
}
|
|
1896
1915
|
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, {
|
|
1897
1916
|
...resolveCodexTurnModes(participantContext, input.message),
|
|
1917
|
+
skillInvocationText: input.message.senderType === 'human' ? input.message.text ?? undefined : undefined,
|
|
1898
1918
|
replyAuthority: input.replyAuthority ?? null,
|
|
1899
1919
|
});
|
|
1900
1920
|
}
|
|
@@ -2126,6 +2146,7 @@ export async function main() {
|
|
|
2126
2146
|
};
|
|
2127
2147
|
const runTurnOnce = () => session.adapter.runTurn(turnPrompt, handleCodexEvent, logCodexLine, turnImagePaths, turnMediaAddDirs, {
|
|
2128
2148
|
planMode: nextTurn.planMode,
|
|
2149
|
+
skillInvocationText: nextTurn.skillInvocationText,
|
|
2129
2150
|
onServerRequest: (request) => handleCodexServerRequest(session, request, nextTurn.requestingUserId ?? null, nextTurn.sourceMessageId ?? null),
|
|
2130
2151
|
});
|
|
2131
2152
|
let result = await runTurnOnce();
|
|
@@ -2584,12 +2605,9 @@ export async function main() {
|
|
|
2584
2605
|
return;
|
|
2585
2606
|
await runtimeState.writeRuntimeInfo(conversationId, {
|
|
2586
2607
|
surfaceMode: 'host',
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
supportsInputInterrupt: descriptor.supportsInputInterrupt,
|
|
2591
|
-
streamingTextMode: descriptor.streamingTextMode,
|
|
2592
|
-
},
|
|
2608
|
+
// The shared publisher sanitizes configuration while retaining commands.
|
|
2609
|
+
descriptor,
|
|
2610
|
+
facts: buildCodexSessionFacts(sessions.get(conversationId)),
|
|
2593
2611
|
});
|
|
2594
2612
|
}));
|
|
2595
2613
|
const failure = results.find((result) => result.status === 'rejected');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.4",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@canonmsg/agent-sdk": "^10.2.1",
|
|
33
33
|
"@canonmsg/agent-tools": "^0.9.0",
|
|
34
34
|
"@canonmsg/coding-agent-host": "^0.7.0",
|
|
35
|
-
"@canonmsg/core": "^12.3.
|
|
35
|
+
"@canonmsg/core": "^12.3.1",
|
|
36
36
|
"@canonmsg/rich-cards": "^0.10.4"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|