@canonmsg/codex-plugin 0.18.7 → 0.18.9
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 +96 -4
- package/dist/host.d.ts +3 -0
- package/dist/host.js +66 -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,56 @@ 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 trimmed = prompt.trim();
|
|
538
|
+
const match = trimmed.match(/^\$([A-Za-z0-9:_-]+)(?:\s+([\s\S]*))?$/)
|
|
539
|
+
?? trimmed.match(/^\/skill\s+([A-Za-z0-9:_-]+)(?:\s+([\s\S]*))?$/);
|
|
540
|
+
if (!match?.[1])
|
|
541
|
+
return null;
|
|
542
|
+
return {
|
|
543
|
+
name: match[1],
|
|
544
|
+
prompt: match[2]?.trim() ?? '',
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function parseSkillsListResponse(result) {
|
|
548
|
+
const entries = Array.isArray(result.data) ? result.data : [];
|
|
549
|
+
const skills = [];
|
|
550
|
+
const seen = new Set();
|
|
551
|
+
for (const entry of entries) {
|
|
552
|
+
if (!isRecord(entry))
|
|
553
|
+
continue;
|
|
554
|
+
const cwdSkills = Array.isArray(entry.skills) ? entry.skills : [];
|
|
555
|
+
for (const rawSkill of cwdSkills) {
|
|
556
|
+
if (!isRecord(rawSkill))
|
|
557
|
+
continue;
|
|
558
|
+
if (rawSkill.enabled === false)
|
|
559
|
+
continue;
|
|
560
|
+
const name = readString(rawSkill, 'name');
|
|
561
|
+
const path = readString(rawSkill, 'path');
|
|
562
|
+
if (!name || !path || seen.has(name.toLowerCase()))
|
|
563
|
+
continue;
|
|
564
|
+
seen.add(name.toLowerCase());
|
|
565
|
+
const interfaceMetadata = isRecord(rawSkill.interface) ? rawSkill.interface : undefined;
|
|
566
|
+
const scope = readNullableString(rawSkill, 'scope');
|
|
567
|
+
const description = readNullableString(rawSkill, 'description');
|
|
568
|
+
const shortDescription = readNullableString(rawSkill, 'shortDescription');
|
|
569
|
+
const displayName = readNullableString(interfaceMetadata, 'displayName');
|
|
570
|
+
skills.push({
|
|
571
|
+
name,
|
|
572
|
+
path,
|
|
573
|
+
...(scope ? { scope } : {}),
|
|
574
|
+
...(description ? { description } : {}),
|
|
575
|
+
...(shortDescription ? { shortDescription } : {}),
|
|
576
|
+
...(displayName ? { displayName } : {}),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return skills;
|
|
581
|
+
}
|
|
490
582
|
function stringifyPreview(value) {
|
|
491
583
|
if (typeof value === 'string')
|
|
492
584
|
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 buildCodexSkillCommands(skills: ReadonlyArray<CodexSkillMetadata>): CanonRuntimeCommandDescriptor[];
|
|
35
38
|
export declare function main(): Promise<void>;
|
|
36
39
|
export {};
|
package/dist/host.js
CHANGED
|
@@ -85,6 +85,36 @@ 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 buildCodexSkillCommands(skills) {
|
|
90
|
+
return 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
|
+
id: `codex-skill-${skill.name}`,
|
|
99
|
+
label: skill.displayName ?? skill.name,
|
|
100
|
+
aliases: [skill.name],
|
|
101
|
+
category: 'skill',
|
|
102
|
+
placements: ['composer_slash', 'command_palette'],
|
|
103
|
+
availability: ['always'],
|
|
104
|
+
trailingTextBehavior: 'send_as_prompt',
|
|
105
|
+
args: [
|
|
106
|
+
{
|
|
107
|
+
id: 'prompt',
|
|
108
|
+
label: 'Prompt',
|
|
109
|
+
kind: 'string',
|
|
110
|
+
captureRemaining: true,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
...(description ? { description } : {}),
|
|
114
|
+
dispatch: { kind: 'text_passthrough', template: `$${skill.name} {argument}` },
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
88
118
|
function buildCodexRuntimeDescriptor(input) {
|
|
89
119
|
const commands = [
|
|
90
120
|
{
|
|
@@ -105,6 +135,10 @@ function buildCodexRuntimeDescriptor(input) {
|
|
|
105
135
|
RUNTIME_STOP_ACTION,
|
|
106
136
|
RUNTIME_STOP_AND_DROP_ACTION,
|
|
107
137
|
];
|
|
138
|
+
const skillCommands = input.skills?.length ? buildCodexSkillCommands(input.skills) : [];
|
|
139
|
+
if (skillCommands.length) {
|
|
140
|
+
commands.push(...skillCommands);
|
|
141
|
+
}
|
|
108
142
|
const descriptor = buildFirstPartyCodingRuntimeDescriptor({
|
|
109
143
|
clientType: 'codex',
|
|
110
144
|
models: input.models,
|
|
@@ -1437,6 +1471,10 @@ export async function main() {
|
|
|
1437
1471
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Thread ${event.threadId}`);
|
|
1438
1472
|
return;
|
|
1439
1473
|
}
|
|
1474
|
+
if (event.type === 'skills.changed') {
|
|
1475
|
+
void refreshCodexSkillInventory(true).then(() => publishRuntimeHeartbeat());
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1440
1478
|
if (event.type === 'message') {
|
|
1441
1479
|
session.turnState = 'streaming';
|
|
1442
1480
|
markTurnProgress(session);
|
|
@@ -1674,7 +1712,8 @@ export async function main() {
|
|
|
1674
1712
|
show: stringArgs(args['show-runtime-detail']),
|
|
1675
1713
|
hide: stringArgs(args['hide-runtime-detail']),
|
|
1676
1714
|
});
|
|
1677
|
-
let
|
|
1715
|
+
let codexSkills = [];
|
|
1716
|
+
const buildCurrentRuntimeDescriptor = () => ({
|
|
1678
1717
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
1679
1718
|
...(typeof args.model === 'string' ? { defaultModel: args.model } : {}),
|
|
1680
1719
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
@@ -1693,8 +1732,32 @@ export async function main() {
|
|
|
1693
1732
|
presentation: runtimePresentation,
|
|
1694
1733
|
supportsPlanMode: useAppServer,
|
|
1695
1734
|
supportsRichCards: useAppServer,
|
|
1735
|
+
skills: codexSkills,
|
|
1696
1736
|
}),
|
|
1697
|
-
};
|
|
1737
|
+
});
|
|
1738
|
+
let runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1739
|
+
async function refreshCodexSkillInventory(forceReload = false) {
|
|
1740
|
+
if (!useAppServer)
|
|
1741
|
+
return;
|
|
1742
|
+
const probe = new CodexAppServerAdapter({
|
|
1743
|
+
cwd: workingDir,
|
|
1744
|
+
codexBin,
|
|
1745
|
+
model: typeof args.model === 'string' ? args.model : null,
|
|
1746
|
+
configOverrides: args.config ?? [],
|
|
1747
|
+
});
|
|
1748
|
+
try {
|
|
1749
|
+
codexSkills = await probe.listSkills({ forceReload });
|
|
1750
|
+
runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1751
|
+
}
|
|
1752
|
+
catch (error) {
|
|
1753
|
+
codexSkills = [];
|
|
1754
|
+
runtimeDescriptor = buildCurrentRuntimeDescriptor();
|
|
1755
|
+
console.error('[canon-codex] Failed to load Codex skills:', error instanceof Error ? error.message : error);
|
|
1756
|
+
}
|
|
1757
|
+
finally {
|
|
1758
|
+
probe.close();
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1698
1761
|
function applySessionControl(conversationId, control) {
|
|
1699
1762
|
const session = sessions.get(conversationId);
|
|
1700
1763
|
if (!session || session.closed)
|
|
@@ -1950,51 +2013,7 @@ export async function main() {
|
|
|
1950
2013
|
onError: (error) => console.error(`[canon-codex] SSE error: ${error.message}`),
|
|
1951
2014
|
},
|
|
1952
2015
|
});
|
|
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
|
-
}
|
|
2016
|
+
await refreshCodexSkillInventory();
|
|
1998
2017
|
try {
|
|
1999
2018
|
const conversations = await client.getConversations();
|
|
2000
2019
|
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.9",
|
|
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"
|