@canonmsg/codex-plugin 0.29.2 → 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/codex-app-tools.js +8 -48
- package/dist/host.d.ts +63 -3
- package/dist/host.js +29 -75
- package/package.json +3 -3
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/codex-app-tools.js
CHANGED
|
@@ -142,31 +142,6 @@ const CODEX_NO_REPLY_TOOL = tool(CODEX_NO_REPLY_TOOL_NAME, 'End your turn withou
|
|
|
142
142
|
},
|
|
143
143
|
}, false);
|
|
144
144
|
export const CODEX_APP_DYNAMIC_TOOLS = [
|
|
145
|
-
tool('automation_update', 'Create, update, view, or delete Codex app automations. Canon exposes the name for compatibility, but does not manage Desktop automations.', {
|
|
146
|
-
type: 'object',
|
|
147
|
-
additionalProperties: false,
|
|
148
|
-
properties: {
|
|
149
|
-
id: { type: 'string' },
|
|
150
|
-
mode: { type: 'string' },
|
|
151
|
-
kind: { type: 'string' },
|
|
152
|
-
name: { type: 'string' },
|
|
153
|
-
prompt: { type: 'string' },
|
|
154
|
-
rrule: { type: 'string' },
|
|
155
|
-
cwds: {
|
|
156
|
-
anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
|
|
157
|
-
},
|
|
158
|
-
destination: { type: 'string' },
|
|
159
|
-
executionEnvironment: { type: 'string' },
|
|
160
|
-
localEnvironmentConfigPath: { type: ['string', 'null'] },
|
|
161
|
-
model: { type: 'string' },
|
|
162
|
-
reasoningEffort: { type: 'string' },
|
|
163
|
-
targetThreadId: { type: 'string' },
|
|
164
|
-
status: { type: 'string' },
|
|
165
|
-
},
|
|
166
|
-
}),
|
|
167
|
-
tool('navigate_to_codex_page', 'Navigate the Codex Desktop UI. Canon exposes the name for compatibility, but has no Codex Desktop page to navigate.', { type: 'object', additionalProperties: true, properties: {} }),
|
|
168
|
-
tool('read_thread_terminal', 'Read the Codex Desktop terminal output for this thread. Canon exposes the name for compatibility, but has no Desktop terminal pane.', emptyObjectSchema, false),
|
|
169
|
-
tool('load_workspace_dependencies', 'Locate bundled Desktop workspace dependency runtimes. Canon exposes the name for compatibility, but does not provide Desktop bundle paths.', emptyObjectSchema, false),
|
|
170
145
|
tool('fork_thread', 'Fork a Codex thread. Omit threadId to fork the calling thread. Canon supports same-directory forks.', {
|
|
171
146
|
type: 'object',
|
|
172
147
|
additionalProperties: false,
|
|
@@ -175,17 +150,6 @@ export const CODEX_APP_DYNAMIC_TOOLS = [
|
|
|
175
150
|
environment: forkEnvironmentSchema,
|
|
176
151
|
},
|
|
177
152
|
}),
|
|
178
|
-
tool('handoff_thread', 'Move a Codex thread between a checkout and worktree. Canon exposes the name for compatibility, but does not manage Desktop handoffs.', {
|
|
179
|
-
type: 'object',
|
|
180
|
-
additionalProperties: false,
|
|
181
|
-
properties: { threadId: { type: 'string' } },
|
|
182
|
-
required: ['threadId'],
|
|
183
|
-
}),
|
|
184
|
-
tool('get_handoff_status', 'Read Codex Desktop handoff status. Canon exposes the name for compatibility, but does not manage Desktop handoffs.', {
|
|
185
|
-
type: 'object',
|
|
186
|
-
additionalProperties: false,
|
|
187
|
-
properties: { threadId: { type: 'string' } },
|
|
188
|
-
}),
|
|
189
153
|
tool('list_projects', 'List Canon workspaces available to Codex app tools.', emptyObjectSchema),
|
|
190
154
|
tool('create_thread', 'Create a separate Codex thread only when the user explicitly asks for a new or separate thread. Canon supports local project targets.', {
|
|
191
155
|
type: 'object',
|
|
@@ -224,15 +188,6 @@ export const CODEX_APP_DYNAMIC_TOOLS = [
|
|
|
224
188
|
},
|
|
225
189
|
required: ['threadId', 'prompt'],
|
|
226
190
|
}),
|
|
227
|
-
tool('set_thread_pinned', 'Pin or unpin a Codex thread. Canon exposes the name for compatibility, but pinned state is Desktop-only.', {
|
|
228
|
-
type: 'object',
|
|
229
|
-
additionalProperties: false,
|
|
230
|
-
properties: {
|
|
231
|
-
threadId: { type: 'string' },
|
|
232
|
-
pinned: { type: 'boolean' },
|
|
233
|
-
},
|
|
234
|
-
required: ['threadId', 'pinned'],
|
|
235
|
-
}),
|
|
236
191
|
tool('set_thread_archived', 'Archive or unarchive a Codex thread.', {
|
|
237
192
|
type: 'object',
|
|
238
193
|
additionalProperties: false,
|
|
@@ -278,7 +233,6 @@ export function filterCodexCommunicationTools(tools, outboundPolicy) {
|
|
|
278
233
|
*/
|
|
279
234
|
export const CODEX_DETACHED_THREAD_DYNAMIC_TOOLS = CODEX_APP_DYNAMIC_TOOLS.filter((entry) => (entry.name !== CANON_RUNTIME_CONTROL_TOOL_NAME
|
|
280
235
|
&& entry.name !== CODEX_NO_REPLY_TOOL_NAME));
|
|
281
|
-
const CODEX_APP_TOOL_NAMES = new Set(CODEX_APP_DYNAMIC_TOOLS.map((entry) => String(entry.name)));
|
|
282
236
|
const UNSUPPORTED_TOOLS = new Map([
|
|
283
237
|
['automation_update', 'Canon does not manage Codex Desktop automations.'],
|
|
284
238
|
['navigate_to_codex_page', 'Canon has no Codex Desktop page to navigate.'],
|
|
@@ -288,6 +242,12 @@ const UNSUPPORTED_TOOLS = new Map([
|
|
|
288
242
|
['get_handoff_status', 'Canon does not manage Codex Desktop handoffs.'],
|
|
289
243
|
['set_thread_pinned', 'Pinned thread state is Codex Desktop-only.'],
|
|
290
244
|
]);
|
|
245
|
+
// Historical calls must still reach their specific rejection, even though
|
|
246
|
+
// unsupported tools are no longer advertised to the model.
|
|
247
|
+
const RECOGNIZED_CODEX_APP_TOOL_NAMES = new Set([
|
|
248
|
+
...CODEX_APP_DYNAMIC_TOOLS.map((entry) => entry.name),
|
|
249
|
+
...UNSUPPORTED_TOOLS.keys(),
|
|
250
|
+
]);
|
|
291
251
|
export function isCodexAppToolCall(params) {
|
|
292
252
|
const namespace = typeof params.namespace === 'string' ? params.namespace : null;
|
|
293
253
|
const rawTool = typeof params.tool === 'string' ? params.tool.trim() : '';
|
|
@@ -296,7 +256,7 @@ export function isCodexAppToolCall(params) {
|
|
|
296
256
|
return rawTool.startsWith('codex_app.');
|
|
297
257
|
return namespace === 'codex_app'
|
|
298
258
|
|| rawTool.startsWith('codex_app.')
|
|
299
|
-
|| (toolName ?
|
|
259
|
+
|| (toolName ? RECOGNIZED_CODEX_APP_TOOL_NAMES.has(toolName) : false);
|
|
300
260
|
}
|
|
301
261
|
export function deniedCodexAppToolResult(reason) {
|
|
302
262
|
return toolResult(false, { error: reason });
|
|
@@ -397,7 +357,7 @@ export function classifyCodexAppToolRequest(input) {
|
|
|
397
357
|
}
|
|
398
358
|
export async function handleCodexAppToolCall(runtime, params) {
|
|
399
359
|
const toolName = normalizeToolName(params.tool);
|
|
400
|
-
if (!toolName || !
|
|
360
|
+
if (!toolName || !RECOGNIZED_CODEX_APP_TOOL_NAMES.has(toolName)) {
|
|
401
361
|
return toolResult(false, { error: `Unsupported codex_app tool: ${String(params.tool ?? 'unknown')}` });
|
|
402
362
|
}
|
|
403
363
|
if (toolName === CODEX_NO_REPLY_TOOL_NAME) {
|
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'],
|
|
@@ -272,14 +289,6 @@ export function resolveWorkspaceCwd() {
|
|
|
272
289
|
defaultCwd: workingDir,
|
|
273
290
|
});
|
|
274
291
|
}
|
|
275
|
-
function resolveExecutionFallbackReason(environment) {
|
|
276
|
-
if (!environment?.reason || environment.mode !== 'locked') {
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
return environment.reason === 'Sharing the base workspace (locked mode)'
|
|
280
|
-
? null
|
|
281
|
-
: environment.reason;
|
|
282
|
-
}
|
|
283
292
|
function stringArg(args, key) {
|
|
284
293
|
const value = args[key];
|
|
285
294
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
@@ -810,9 +819,6 @@ export async function main() {
|
|
|
810
819
|
client,
|
|
811
820
|
conversationCache,
|
|
812
821
|
});
|
|
813
|
-
function resolveWorkspaceIdForBaseCwd(baseCwd) {
|
|
814
|
-
return workspaceOptions.find((option) => option.cwd === baseCwd)?.id;
|
|
815
|
-
}
|
|
816
822
|
async function refreshKnownConversationIds(force = false) {
|
|
817
823
|
if (!force && Date.now() - lastKnownConversationRefreshAt < HEARTBEAT_MS) {
|
|
818
824
|
return;
|
|
@@ -1349,6 +1355,7 @@ export async function main() {
|
|
|
1349
1355
|
function enqueuePrompt(session, prompt, intent = 'queue', toFront = false, sourceMessageId, markAccepted = false, imagePaths = [], mediaAddDirs = [], planMode = false, turn = {}) {
|
|
1350
1356
|
const nextPrompt = {
|
|
1351
1357
|
prompt,
|
|
1358
|
+
skillInvocationText: turn.skillInvocationText,
|
|
1352
1359
|
intent,
|
|
1353
1360
|
sourceMessageId,
|
|
1354
1361
|
markAccepted,
|
|
@@ -1895,6 +1902,7 @@ export async function main() {
|
|
|
1895
1902
|
if (session.running && deliveryIntent === 'interrupt') {
|
|
1896
1903
|
enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, {
|
|
1897
1904
|
...resolveCodexTurnModes(participantContext, input.message),
|
|
1905
|
+
skillInvocationText: input.message.senderType === 'human' ? input.message.text ?? undefined : undefined,
|
|
1898
1906
|
replyAuthority: input.replyAuthority ?? null,
|
|
1899
1907
|
});
|
|
1900
1908
|
console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Interrupting current turn for explicit human send-now`);
|
|
@@ -1906,6 +1914,7 @@ export async function main() {
|
|
|
1906
1914
|
}
|
|
1907
1915
|
enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, {
|
|
1908
1916
|
...resolveCodexTurnModes(participantContext, input.message),
|
|
1917
|
+
skillInvocationText: input.message.senderType === 'human' ? input.message.text ?? undefined : undefined,
|
|
1909
1918
|
replyAuthority: input.replyAuthority ?? null,
|
|
1910
1919
|
});
|
|
1911
1920
|
}
|
|
@@ -2137,6 +2146,7 @@ export async function main() {
|
|
|
2137
2146
|
};
|
|
2138
2147
|
const runTurnOnce = () => session.adapter.runTurn(turnPrompt, handleCodexEvent, logCodexLine, turnImagePaths, turnMediaAddDirs, {
|
|
2139
2148
|
planMode: nextTurn.planMode,
|
|
2149
|
+
skillInvocationText: nextTurn.skillInvocationText,
|
|
2140
2150
|
onServerRequest: (request) => handleCodexServerRequest(session, request, nextTurn.requestingUserId ?? null, nextTurn.sourceMessageId ?? null),
|
|
2141
2151
|
});
|
|
2142
2152
|
let result = await runTurnOnce();
|
|
@@ -2590,71 +2600,15 @@ export async function main() {
|
|
|
2590
2600
|
if (signal.aborted)
|
|
2591
2601
|
return;
|
|
2592
2602
|
const results = await Promise.allSettled(Array.from(knownConversationIds).map(async (conversationId) => {
|
|
2593
|
-
const session = sessions.get(conversationId);
|
|
2594
|
-
const workspaceId = session
|
|
2595
|
-
? resolveWorkspaceIdForBaseCwd(session.environment.baseCwd)
|
|
2596
|
-
: runtimeDescriptor.defaultWorkspaceId;
|
|
2597
|
-
const workspace = workspaceOptions.find((option) => option.id === workspaceId) ?? null;
|
|
2598
2603
|
const descriptor = runtimeDescriptor.runtimeDescriptor;
|
|
2599
2604
|
if (!descriptor)
|
|
2600
2605
|
return;
|
|
2601
|
-
|
|
2602
|
-
descriptor,
|
|
2606
|
+
await runtimeState.writeRuntimeInfo(conversationId, {
|
|
2603
2607
|
surfaceMode: 'host',
|
|
2604
|
-
// The
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
? {}
|
|
2609
|
-
: { warning: "Approvals can't block on this Codex CLI — update Codex to enable the app-server transport and approval gates." }),
|
|
2610
|
-
statusItems: [
|
|
2611
|
-
{
|
|
2612
|
-
id: 'transport',
|
|
2613
|
-
label: 'Transport',
|
|
2614
|
-
value: useAppServer ? 'app-server' : 'exec --json',
|
|
2615
|
-
},
|
|
2616
|
-
{
|
|
2617
|
-
id: 'streaming',
|
|
2618
|
-
label: 'Live output',
|
|
2619
|
-
value: useAppServer
|
|
2620
|
-
? 'Plans, questions, approvals, tools, and message deltas'
|
|
2621
|
-
: 'Thinking, tools, and completed-message previews',
|
|
2622
|
-
},
|
|
2623
|
-
{
|
|
2624
|
-
id: 'codex-cli',
|
|
2625
|
-
label: 'Codex CLI',
|
|
2626
|
-
value: codexCliStatus.version ?? (codexCliStatus.raw ?? 'Version unknown'),
|
|
2627
|
-
tone: codexCliStatus.version ? 'default' : 'warning',
|
|
2628
|
-
},
|
|
2629
|
-
{
|
|
2630
|
-
id: 'nativeActions',
|
|
2631
|
-
label: 'Native actions',
|
|
2632
|
-
value: useAppServer ? 'Enabled' : 'Limited until app-server transport',
|
|
2633
|
-
...(useAppServer ? {} : { tone: 'warning' }),
|
|
2634
|
-
},
|
|
2635
|
-
{
|
|
2636
|
-
id: 'mediaOut',
|
|
2637
|
-
label: 'Media out',
|
|
2638
|
-
value: 'Generated media artifacts',
|
|
2639
|
-
},
|
|
2640
|
-
],
|
|
2641
|
-
execution: {
|
|
2642
|
-
resolvedWorkspaceLabel: workspace?.label ?? workspaceId ?? null,
|
|
2643
|
-
resolvedCwd: session?.cwd ?? workspace?.cwd ?? workingDir,
|
|
2644
|
-
workspaceRootId: workspace?.workspaceRootId ?? null,
|
|
2645
|
-
workspaceRelativePath: workspace?.workspaceRelativePath ?? null,
|
|
2646
|
-
executionMode: session?.environment.mode ?? null,
|
|
2647
|
-
executionBranch: session?.environment.branch ?? null,
|
|
2648
|
-
worktreePath: session?.environment.worktreePath ?? null,
|
|
2649
|
-
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
2650
|
-
},
|
|
2651
|
-
notes: [
|
|
2652
|
-
useAppServer
|
|
2653
|
-
? 'This Codex host uses the app-server transport, so Canon can route native plan mode, runtime questions, approvals, and live turn updates.'
|
|
2654
|
-
: 'This Codex host uses the current exec --json transport, so Canon can show thinking, tool activity, and completed assistant-message previews, but not native plan questions or structured approvals.',
|
|
2655
|
-
],
|
|
2656
|
-
};
|
|
2657
|
-
await runtimeState.writeRuntimeInfo(conversationId, payload);
|
|
2608
|
+
// The shared publisher sanitizes configuration while retaining commands.
|
|
2609
|
+
descriptor,
|
|
2610
|
+
facts: buildCodexSessionFacts(sessions.get(conversationId)),
|
|
2611
|
+
});
|
|
2658
2612
|
}));
|
|
2659
2613
|
const failure = results.find((result) => result.status === 'rejected');
|
|
2660
2614
|
if (failure?.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",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"prepack": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@canonmsg/agent-sdk": "^10.2.
|
|
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.
|
|
35
|
+
"@canonmsg/core": "^12.3.1",
|
|
36
36
|
"@canonmsg/rich-cards": "^0.10.4"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|