@canonmsg/codex-plugin 0.18.7 → 0.18.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 -0
- package/dist/app-server-adapter.d.ts +13 -0
- package/dist/app-server-adapter.js +94 -4
- package/dist/host.d.ts +3 -0
- package/dist/host.js +80 -47
- package/package.json +3 -3
package/dist/adapter.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { CodexApprovalPolicy, CodexEvent, CodexRunTurnOptions, CodexSandboxMode, CodexTurnResult } from './adapter.js';
|
|
2
|
+
export interface CodexSkillMetadata {
|
|
3
|
+
name: string;
|
|
4
|
+
path: string;
|
|
5
|
+
scope?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
shortDescription?: string;
|
|
8
|
+
displayName?: string;
|
|
9
|
+
}
|
|
2
10
|
export declare class CodexAppServerAdapter {
|
|
3
11
|
private readonly cwd;
|
|
4
12
|
private readonly codexBin;
|
|
@@ -26,6 +34,7 @@ export declare class CodexAppServerAdapter {
|
|
|
26
34
|
private currentErrorText;
|
|
27
35
|
private interrupted;
|
|
28
36
|
private initialized;
|
|
37
|
+
private skillsCache;
|
|
29
38
|
private messageTextByItem;
|
|
30
39
|
private planText;
|
|
31
40
|
constructor(opts: {
|
|
@@ -62,6 +71,10 @@ export declare class CodexAppServerAdapter {
|
|
|
62
71
|
private rememberResolvedModel;
|
|
63
72
|
private sandboxPolicyPayload;
|
|
64
73
|
private buildWritableRoots;
|
|
74
|
+
listSkills(options?: {
|
|
75
|
+
forceReload?: boolean;
|
|
76
|
+
}): Promise<CodexSkillMetadata[]>;
|
|
77
|
+
private buildTurnInput;
|
|
65
78
|
private ensureStarted;
|
|
66
79
|
private handleLine;
|
|
67
80
|
private handleServerRequest;
|
|
@@ -27,6 +27,7 @@ export class CodexAppServerAdapter {
|
|
|
27
27
|
currentErrorText = null;
|
|
28
28
|
interrupted = false;
|
|
29
29
|
initialized = false;
|
|
30
|
+
skillsCache = null;
|
|
30
31
|
messageTextByItem = new Map();
|
|
31
32
|
planText = '';
|
|
32
33
|
constructor(opts) {
|
|
@@ -142,10 +143,7 @@ export class CodexAppServerAdapter {
|
|
|
142
143
|
turnPromise.catch(() => { });
|
|
143
144
|
const turnStarted = await this.sendRequest('turn/start', {
|
|
144
145
|
threadId: this.threadId,
|
|
145
|
-
input:
|
|
146
|
-
{ type: 'text', text: prompt, text_elements: [] },
|
|
147
|
-
...imagePaths.map((path) => ({ type: 'localImage', path })),
|
|
148
|
-
],
|
|
146
|
+
input: await this.buildTurnInput(prompt, imagePaths),
|
|
149
147
|
...(this.model ? { model: this.model } : {}),
|
|
150
148
|
...this.sandboxPolicyPayload(_extraAddDirs),
|
|
151
149
|
collaborationMode: {
|
|
@@ -226,6 +224,45 @@ export class CodexAppServerAdapter {
|
|
|
226
224
|
}
|
|
227
225
|
return roots;
|
|
228
226
|
}
|
|
227
|
+
async listSkills(options = {}) {
|
|
228
|
+
await this.ensureStarted();
|
|
229
|
+
if (!options.forceReload && this.skillsCache) {
|
|
230
|
+
return this.skillsCache;
|
|
231
|
+
}
|
|
232
|
+
const result = await this.sendRequest('skills/list', {
|
|
233
|
+
cwds: [this.cwd],
|
|
234
|
+
...(options.forceReload ? { forceReload: true } : {}),
|
|
235
|
+
});
|
|
236
|
+
const parsed = parseSkillsListResponse(result);
|
|
237
|
+
this.skillsCache = parsed;
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
async buildTurnInput(prompt, imagePaths) {
|
|
241
|
+
const skillPrompt = parseSkillSlashPrompt(prompt);
|
|
242
|
+
const input = [];
|
|
243
|
+
if (skillPrompt) {
|
|
244
|
+
const skills = await this.listSkills().catch(() => []);
|
|
245
|
+
const selected = skills.find((skill) => skill.name.toLowerCase() === skillPrompt.name.toLowerCase());
|
|
246
|
+
if (selected) {
|
|
247
|
+
input.push({ type: 'skill', name: selected.name, path: selected.path });
|
|
248
|
+
if (skillPrompt.prompt.trim()) {
|
|
249
|
+
input.push({ type: 'text', text: skillPrompt.prompt, text_elements: [] });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
input.push({
|
|
254
|
+
type: 'text',
|
|
255
|
+
text: `$${skillPrompt.name}${skillPrompt.prompt.trim() ? ` ${skillPrompt.prompt.trim()}` : ''}`,
|
|
256
|
+
text_elements: [],
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
input.push({ type: 'text', text: prompt, text_elements: [] });
|
|
262
|
+
}
|
|
263
|
+
input.push(...imagePaths.map((path) => ({ type: 'localImage', path })));
|
|
264
|
+
return input;
|
|
265
|
+
}
|
|
229
266
|
async ensureStarted() {
|
|
230
267
|
if (this.child && this.initialized)
|
|
231
268
|
return;
|
|
@@ -317,6 +354,11 @@ export class CodexAppServerAdapter {
|
|
|
317
354
|
this.currentOnEvent?.({ type: 'turn.started' });
|
|
318
355
|
return;
|
|
319
356
|
}
|
|
357
|
+
if (method === 'skills/changed') {
|
|
358
|
+
this.skillsCache = null;
|
|
359
|
+
this.currentOnEvent?.({ type: 'skills.changed' });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
320
362
|
if (method === 'thread/status/changed') {
|
|
321
363
|
const status = params.status;
|
|
322
364
|
if (status?.type === 'active' && Array.isArray(status.activeFlags)) {
|
|
@@ -487,6 +529,54 @@ function readRawString(record, key) {
|
|
|
487
529
|
const value = record?.[key];
|
|
488
530
|
return typeof value === 'string' ? value : undefined;
|
|
489
531
|
}
|
|
532
|
+
function readNullableString(record, key) {
|
|
533
|
+
const value = record?.[key];
|
|
534
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
535
|
+
}
|
|
536
|
+
function parseSkillSlashPrompt(prompt) {
|
|
537
|
+
const match = prompt.trim().match(/^\/skill\s+([A-Za-z0-9:_-]+)(?:\s+([\s\S]*))?$/);
|
|
538
|
+
if (!match?.[1])
|
|
539
|
+
return null;
|
|
540
|
+
return {
|
|
541
|
+
name: match[1],
|
|
542
|
+
prompt: match[2]?.trim() ?? '',
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function parseSkillsListResponse(result) {
|
|
546
|
+
const entries = Array.isArray(result.data) ? result.data : [];
|
|
547
|
+
const skills = [];
|
|
548
|
+
const seen = new Set();
|
|
549
|
+
for (const entry of entries) {
|
|
550
|
+
if (!isRecord(entry))
|
|
551
|
+
continue;
|
|
552
|
+
const cwdSkills = Array.isArray(entry.skills) ? entry.skills : [];
|
|
553
|
+
for (const rawSkill of cwdSkills) {
|
|
554
|
+
if (!isRecord(rawSkill))
|
|
555
|
+
continue;
|
|
556
|
+
if (rawSkill.enabled === false)
|
|
557
|
+
continue;
|
|
558
|
+
const name = readString(rawSkill, 'name');
|
|
559
|
+
const path = readString(rawSkill, 'path');
|
|
560
|
+
if (!name || !path || seen.has(name.toLowerCase()))
|
|
561
|
+
continue;
|
|
562
|
+
seen.add(name.toLowerCase());
|
|
563
|
+
const interfaceMetadata = isRecord(rawSkill.interface) ? rawSkill.interface : undefined;
|
|
564
|
+
const scope = readNullableString(rawSkill, 'scope');
|
|
565
|
+
const description = readNullableString(rawSkill, 'description');
|
|
566
|
+
const shortDescription = readNullableString(rawSkill, 'shortDescription');
|
|
567
|
+
const displayName = readNullableString(interfaceMetadata, 'displayName');
|
|
568
|
+
skills.push({
|
|
569
|
+
name,
|
|
570
|
+
path,
|
|
571
|
+
...(scope ? { scope } : {}),
|
|
572
|
+
...(description ? { description } : {}),
|
|
573
|
+
...(shortDescription ? { shortDescription } : {}),
|
|
574
|
+
...(displayName ? { displayName } : {}),
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return skills;
|
|
579
|
+
}
|
|
490
580
|
function stringifyPreview(value) {
|
|
491
581
|
if (typeof value === 'string')
|
|
492
582
|
return value;
|
package/dist/host.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { type CanonRuntimeCommandDescriptor } from '@canonmsg/core';
|
|
3
|
+
import { type CodexSkillMetadata } from './app-server-adapter.js';
|
|
2
4
|
interface HostSessionState {
|
|
3
5
|
lastError?: string;
|
|
4
6
|
model?: string;
|
|
@@ -32,5 +34,6 @@ export declare const CODEX_EFFORT_OPTIONS: readonly [{
|
|
|
32
34
|
readonly value: "xhigh";
|
|
33
35
|
readonly label: "Extra high";
|
|
34
36
|
}];
|
|
37
|
+
export declare function buildCodexSkillCommand(skills: ReadonlyArray<CodexSkillMetadata>): CanonRuntimeCommandDescriptor | null;
|
|
35
38
|
export declare function main(): Promise<void>;
|
|
36
39
|
export {};
|
package/dist/host.js
CHANGED
|
@@ -85,6 +85,50 @@ export const CODEX_EFFORT_OPTIONS = [
|
|
|
85
85
|
{ value: 'xhigh', label: 'Extra high' },
|
|
86
86
|
];
|
|
87
87
|
const CODEX_EFFORT_VALUES = new Set(CODEX_EFFORT_OPTIONS.map((option) => option.value));
|
|
88
|
+
const MAX_CODEX_SKILL_COMMAND_CHOICES = 50;
|
|
89
|
+
export function buildCodexSkillCommand(skills) {
|
|
90
|
+
const choices = skills
|
|
91
|
+
.filter((skill) => /^[A-Za-z0-9:_-]+$/.test(skill.name))
|
|
92
|
+
.slice(0, MAX_CODEX_SKILL_COMMAND_CHOICES)
|
|
93
|
+
.map((skill) => {
|
|
94
|
+
const description = skill.shortDescription
|
|
95
|
+
?? skill.description
|
|
96
|
+
?? (skill.scope ? `${skill.scope} skill` : undefined);
|
|
97
|
+
return {
|
|
98
|
+
value: skill.name,
|
|
99
|
+
label: skill.displayName ?? skill.name,
|
|
100
|
+
...(description ? { description } : {}),
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
if (choices.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
return {
|
|
106
|
+
id: 'codex-skill',
|
|
107
|
+
label: 'Use skill',
|
|
108
|
+
description: 'Invoke one of the Codex skills available to this runtime.',
|
|
109
|
+
aliases: ['skill'],
|
|
110
|
+
category: 'skill',
|
|
111
|
+
placements: ['composer_slash', 'command_palette'],
|
|
112
|
+
availability: ['always'],
|
|
113
|
+
trailingTextBehavior: 'send_as_prompt',
|
|
114
|
+
args: [
|
|
115
|
+
{
|
|
116
|
+
id: 'skill',
|
|
117
|
+
label: 'Skill',
|
|
118
|
+
kind: 'enum',
|
|
119
|
+
required: true,
|
|
120
|
+
choices,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'prompt',
|
|
124
|
+
label: 'Prompt',
|
|
125
|
+
kind: 'string',
|
|
126
|
+
captureRemaining: true,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
dispatch: { kind: 'text_passthrough', template: '/skill {args.skill} {args.prompt}' },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
88
132
|
function buildCodexRuntimeDescriptor(input) {
|
|
89
133
|
const commands = [
|
|
90
134
|
{
|
|
@@ -105,6 +149,10 @@ function buildCodexRuntimeDescriptor(input) {
|
|
|
105
149
|
RUNTIME_STOP_ACTION,
|
|
106
150
|
RUNTIME_STOP_AND_DROP_ACTION,
|
|
107
151
|
];
|
|
152
|
+
const skillCommand = input.skills?.length ? buildCodexSkillCommand(input.skills) : null;
|
|
153
|
+
if (skillCommand) {
|
|
154
|
+
commands.push(skillCommand);
|
|
155
|
+
}
|
|
108
156
|
const descriptor = buildFirstPartyCodingRuntimeDescriptor({
|
|
109
157
|
clientType: 'codex',
|
|
110
158
|
models: input.models,
|
|
@@ -1437,6 +1485,10 @@ export async function main() {
|
|
|
1437
1485
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Thread ${event.threadId}`);
|
|
1438
1486
|
return;
|
|
1439
1487
|
}
|
|
1488
|
+
if (event.type === 'skills.changed') {
|
|
1489
|
+
void refreshCodexSkillInventory(true).then(() => publishRuntimeHeartbeat());
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1440
1492
|
if (event.type === 'message') {
|
|
1441
1493
|
session.turnState = 'streaming';
|
|
1442
1494
|
markTurnProgress(session);
|
|
@@ -1674,7 +1726,8 @@ export async function main() {
|
|
|
1674
1726
|
show: stringArgs(args['show-runtime-detail']),
|
|
1675
1727
|
hide: stringArgs(args['hide-runtime-detail']),
|
|
1676
1728
|
});
|
|
1677
|
-
let
|
|
1729
|
+
let codexSkills = [];
|
|
1730
|
+
const buildCurrentRuntimeDescriptor = () => ({
|
|
1678
1731
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1679
1732
|
...(typeof args.model === 'string' ? { defaultModel: args.model } : {}),
|
|
1680
1733
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
@@ -1693,8 +1746,32 @@ export async function main() {
|
|
|
1693
1746
|
presentation: runtimePresentation,
|
|
1694
1747
|
supportsPlanMode: useAppServer,
|
|
1695
1748
|
supportsRichCards: useAppServer,
|
|
1749
|
+
skills: codexSkills,
|
|
1696
1750
|
}),
|
|
1697
|
-
};
|
|
1751
|
+
});
|
|
1752
|
+
let runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1753
|
+
async function refreshCodexSkillInventory(forceReload = false) {
|
|
1754
|
+
if (!useAppServer)
|
|
1755
|
+
return;
|
|
1756
|
+
const probe = new CodexAppServerAdapter({
|
|
1757
|
+
cwd: workingDir,
|
|
1758
|
+
codexBin,
|
|
1759
|
+
model: typeof args.model === 'string' ? args.model : null,
|
|
1760
|
+
configOverrides: args.config ?? [],
|
|
1761
|
+
});
|
|
1762
|
+
try {
|
|
1763
|
+
codexSkills = await probe.listSkills({ forceReload });
|
|
1764
|
+
runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1765
|
+
}
|
|
1766
|
+
catch (error) {
|
|
1767
|
+
codexSkills = [];
|
|
1768
|
+
runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1769
|
+
console.error('[canon-codex] Failed to load Codex skills:', error instanceof Error ? error.message : error);
|
|
1770
|
+
}
|
|
1771
|
+
finally {
|
|
1772
|
+
probe.close();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1698
1775
|
function applySessionControl(conversationId, control) {
|
|
1699
1776
|
const session = sessions.get(conversationId);
|
|
1700
1777
|
if (!session || session.closed)
|
|
@@ -1950,51 +2027,7 @@ export async function main() {
|
|
|
1950
2027
|
onError: (error) => console.error(`[canon-codex] SSE error: ${error.message}`),
|
|
1951
2028
|
},
|
|
1952
2029
|
});
|
|
1953
|
-
|
|
1954
|
-
runtimeDescriptor = {
|
|
1955
|
-
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1956
|
-
...(typeof args.model === 'string' ? { defaultModel: args.model } : {}),
|
|
1957
|
-
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1958
|
-
availableExecutionModes: hostAvailableExecutionModes,
|
|
1959
|
-
availablePermissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
1960
|
-
...(codexPermissionEnvelope.defaultPermissionMode
|
|
1961
|
-
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
1962
|
-
: {}),
|
|
1963
|
-
runtimeDescriptor: buildCodexRuntimeDescriptor({
|
|
1964
|
-
models: codexModelOptions,
|
|
1965
|
-
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1966
|
-
workspaceRoots: workspaceRootMetadata,
|
|
1967
|
-
executionModes: hostAvailableExecutionModes,
|
|
1968
|
-
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
1969
|
-
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
1970
|
-
presentation: runtimePresentation,
|
|
1971
|
-
supportsPlanMode: useAppServer,
|
|
1972
|
-
supportsRichCards: useAppServer,
|
|
1973
|
-
}),
|
|
1974
|
-
};
|
|
1975
|
-
}
|
|
1976
|
-
catch {
|
|
1977
|
-
runtimeDescriptor = {
|
|
1978
|
-
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1979
|
-
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1980
|
-
availableExecutionModes: hostAvailableExecutionModes,
|
|
1981
|
-
availablePermissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
1982
|
-
...(codexPermissionEnvelope.defaultPermissionMode
|
|
1983
|
-
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
1984
|
-
: {}),
|
|
1985
|
-
runtimeDescriptor: buildCodexRuntimeDescriptor({
|
|
1986
|
-
models: codexModelOptions,
|
|
1987
|
-
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
1988
|
-
workspaceRoots: workspaceRootMetadata,
|
|
1989
|
-
executionModes: hostAvailableExecutionModes,
|
|
1990
|
-
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
1991
|
-
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
1992
|
-
presentation: runtimePresentation,
|
|
1993
|
-
supportsPlanMode: useAppServer,
|
|
1994
|
-
supportsRichCards: useAppServer,
|
|
1995
|
-
}),
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
2030
|
+
await refreshCodexSkillInventory();
|
|
1998
2031
|
try {
|
|
1999
2032
|
const conversations = await client.getConversations();
|
|
2000
2033
|
lastKnownConversationRefreshAt = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.8",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"prepack": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@canonmsg/agent-sdk": "^3.
|
|
32
|
+
"@canonmsg/agent-sdk": "^3.4.1",
|
|
33
33
|
"@canonmsg/coding-agent-host": "^0.2.2",
|
|
34
|
-
"@canonmsg/core": "^2.
|
|
34
|
+
"@canonmsg/core": "^2.9.2"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=18.0.0"
|