@canonmsg/codex-plugin 0.6.6 → 0.7.0
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/README.md +11 -1
- package/dist/host-runtime.d.ts +8 -2
- package/dist/host-runtime.js +13 -0
- package/dist/host.js +129 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ You do not need a git repo for host mode. The plugin passes `--skip-git-repo-che
|
|
|
37
37
|
|
|
38
38
|
## Current limitation
|
|
39
39
|
|
|
40
|
-
The stable `codex exec --json` surface exposes
|
|
40
|
+
The stable `codex exec --json` surface exposes thinking state, tool activity, and completed assistant-message previews, but not token-by-token text deltas. v1 therefore publishes live progress and message snapshots without claiming true token streaming.
|
|
41
41
|
|
|
42
42
|
## Working directory
|
|
43
43
|
|
|
@@ -45,12 +45,22 @@ The stable `codex exec --json` surface exposes completed assistant messages and
|
|
|
45
45
|
canon-codex --cwd /path/to/project
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
Advertise multiple project choices to the Canon app:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
canon-codex --cwd ~/dev --workspace ~/dev/canon --workspace ~/dev/yumyumv2
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`--cwd` is the default workspace. Each `--workspace` value appears as a selectable workspace during session creation. Worktree mode creates a per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-workspace mode runs directly in the selected directory.
|
|
55
|
+
|
|
48
56
|
Useful flags:
|
|
49
57
|
|
|
50
58
|
```bash
|
|
51
59
|
canon-codex --cwd /path/to/project --model gpt-5.4 --full-auto
|
|
52
60
|
```
|
|
53
61
|
|
|
62
|
+
Codex also supports `--add-dir /extra/path` for additional writable directories passed through to `codex exec`. Canon does not yet render those extra directories as workspace choices.
|
|
63
|
+
|
|
54
64
|
Recent Codex CLI releases no longer accept `--ask-for-approval` with `codex exec`. If you previously launched Canon with `--sandbox workspace-write --ask-for-approval never`, switch to `--full-auto`.
|
|
55
65
|
|
|
56
66
|
Local smoke test:
|
package/dist/host-runtime.d.ts
CHANGED
|
@@ -97,17 +97,23 @@ export declare function publishHostSessionSnapshots(input: {
|
|
|
97
97
|
liveSessionConfigByConversation?: ReadonlyMap<string, {
|
|
98
98
|
model?: string;
|
|
99
99
|
permissionMode?: string;
|
|
100
|
+
effort?: string;
|
|
101
|
+
runtimeControlValues?: Record<string, string>;
|
|
100
102
|
workspaceId?: string;
|
|
101
103
|
executionMode?: SessionWorkspaceConfig['executionMode'];
|
|
102
104
|
executionBranch?: string | null;
|
|
103
105
|
}>;
|
|
104
106
|
}): Promise<void>;
|
|
105
|
-
export declare function readHostSessionConfig<TExtra extends string = never>(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial<Record<TExtra, string>>
|
|
107
|
+
export declare function readHostSessionConfig<TExtra extends string = never>(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial<Record<TExtra, string>> & {
|
|
108
|
+
runtimeControlValues?: Record<string, string>;
|
|
109
|
+
}) | null;
|
|
106
110
|
export declare function loadHostSessionConfig<TExtra extends string = never>(input: {
|
|
107
111
|
conversationId: string;
|
|
108
112
|
agentId: string;
|
|
109
113
|
extraStringFields?: readonly TExtra[];
|
|
110
|
-
}): Promise<(SessionWorkspaceConfig & Partial<Record<TExtra, string>>
|
|
114
|
+
}): Promise<(SessionWorkspaceConfig & Partial<Record<TExtra, string>> & {
|
|
115
|
+
runtimeControlValues?: Record<string, string>;
|
|
116
|
+
}) | null>;
|
|
111
117
|
export declare function resolveHostWorkspaceCwd(input: {
|
|
112
118
|
workspaceOptions: HostWorkspaceResolverOption[];
|
|
113
119
|
config: {
|
package/dist/host-runtime.js
CHANGED
|
@@ -162,6 +162,10 @@ export async function publishHostSessionSnapshots(input) {
|
|
|
162
162
|
sessionConfig: {
|
|
163
163
|
...(mergedConfig.model ? { model: mergedConfig.model } : {}),
|
|
164
164
|
...(mergedConfig.permissionMode ? { permissionMode: mergedConfig.permissionMode } : {}),
|
|
165
|
+
...(mergedConfig.effort ? { effort: mergedConfig.effort } : {}),
|
|
166
|
+
...(mergedConfig.runtimeControlValues
|
|
167
|
+
? { runtimeControlValues: mergedConfig.runtimeControlValues }
|
|
168
|
+
: {}),
|
|
165
169
|
...(mergedConfig.workspaceId ? { workspaceId: mergedConfig.workspaceId } : {}),
|
|
166
170
|
...(mergedConfig.executionMode ? { executionMode: mergedConfig.executionMode } : {}),
|
|
167
171
|
},
|
|
@@ -183,6 +187,8 @@ export async function publishHostSessionSnapshots(input) {
|
|
|
183
187
|
hostMode: true,
|
|
184
188
|
model: snapshot.model ?? null,
|
|
185
189
|
permissionMode: snapshot.permissionMode ?? null,
|
|
190
|
+
effort: snapshot.effort ?? null,
|
|
191
|
+
runtimeControlValues: snapshot.runtimeControlValues ?? null,
|
|
186
192
|
workspaceId: snapshot.workspaceId ?? null,
|
|
187
193
|
executionMode: snapshot.executionMode ?? null,
|
|
188
194
|
executionBranch,
|
|
@@ -204,9 +210,16 @@ export function readHostSessionConfig(raw, extraStringFields = []) {
|
|
|
204
210
|
const value = normalizeOptionalString(data[field]);
|
|
205
211
|
return value ? [[field, value]] : [];
|
|
206
212
|
}));
|
|
213
|
+
const runtimeControlValues = Object.fromEntries(Object.entries(data.runtimeControlValues && typeof data.runtimeControlValues === 'object'
|
|
214
|
+
? data.runtimeControlValues
|
|
215
|
+
: {}).flatMap(([key, value]) => {
|
|
216
|
+
const normalizedValue = normalizeOptionalString(value);
|
|
217
|
+
return normalizedValue ? [[key, normalizedValue]] : [];
|
|
218
|
+
}));
|
|
207
219
|
return {
|
|
208
220
|
...(baseConfig ?? {}),
|
|
209
221
|
...extraConfig,
|
|
222
|
+
...(Object.keys(runtimeControlValues).length > 0 ? { runtimeControlValues } : {}),
|
|
210
223
|
};
|
|
211
224
|
}
|
|
212
225
|
export async function loadHostSessionConfig(input) {
|
package/dist/host.js
CHANGED
|
@@ -3,7 +3,7 @@ import { setDefaultResultOrder } from 'node:dns';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
5
|
import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
|
|
6
|
-
import { buildConfiguredWorkspaceOptions, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, clearSessionState, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfile, initRTDBAuth, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, releaseLock, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, shouldTriggerAgentTurn, writeSessionState, writeTurnState, } from '@canonmsg/core';
|
|
6
|
+
import { buildConfiguredWorkspaceOptions, buildPublicWorkspaceOptions, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, clearSessionState, clearTurnState, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfile, initRTDBAuth, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, releaseLock, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, writeRuntimeInfo, shouldTriggerAgentTurn, writeSessionState, writeTurnState, } from '@canonmsg/core';
|
|
7
7
|
import { buildCanonHostPrompt, buildHydratedInboundContext, createConversationMetadataLoader, loadHostSessionConfig, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, } from './host-runtime.js';
|
|
8
8
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
9
9
|
import { CodexConversationAdapter, } from './adapter.js';
|
|
@@ -23,6 +23,61 @@ const CODEX_RUNTIME_CAPABILITIES = {
|
|
|
23
23
|
};
|
|
24
24
|
let workingDir = process.cwd();
|
|
25
25
|
let workspaceOptions = [];
|
|
26
|
+
function buildCodexRuntimeDescriptor(input) {
|
|
27
|
+
return {
|
|
28
|
+
coreControls: [
|
|
29
|
+
{
|
|
30
|
+
id: 'model',
|
|
31
|
+
label: 'Model',
|
|
32
|
+
options: input.models,
|
|
33
|
+
defaultValue: input.models[0]?.value ?? null,
|
|
34
|
+
availability: 'setup_and_live',
|
|
35
|
+
liveBehavior: 'next_turn',
|
|
36
|
+
selectionPolicy: 'inherit',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'workspace',
|
|
40
|
+
label: 'Workspace',
|
|
41
|
+
options: input.workspaces.map((workspace) => ({
|
|
42
|
+
value: workspace.id,
|
|
43
|
+
label: workspace.label,
|
|
44
|
+
})),
|
|
45
|
+
defaultValue: input.workspaces[0]?.id ?? null,
|
|
46
|
+
availability: 'setup',
|
|
47
|
+
liveBehavior: 'none',
|
|
48
|
+
selectionPolicy: 'inherit',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'executionMode',
|
|
52
|
+
label: 'Execution mode',
|
|
53
|
+
options: input.executionModes.map((mode) => ({
|
|
54
|
+
value: mode,
|
|
55
|
+
label: mode === 'worktree' ? 'Isolated worktree' : 'Use shared workspace',
|
|
56
|
+
description: mode === 'worktree'
|
|
57
|
+
? 'Creates or reuses a per-conversation git worktree under ~/.canon/conversation-worktrees when the selected workspace is a git repo.'
|
|
58
|
+
: 'Runs directly in the selected workspace. Canon records usage, but does not create a separate checkout.',
|
|
59
|
+
})),
|
|
60
|
+
defaultValue: null,
|
|
61
|
+
availability: 'setup',
|
|
62
|
+
liveBehavior: 'none',
|
|
63
|
+
selectionPolicy: 'required_explicit',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
runtimeControls: [
|
|
67
|
+
{
|
|
68
|
+
id: 'permissionMode',
|
|
69
|
+
label: 'Execution policy',
|
|
70
|
+
options: input.permissionModes,
|
|
71
|
+
defaultValue: input.defaultPermissionMode ?? null,
|
|
72
|
+
availability: 'setup',
|
|
73
|
+
liveBehavior: 'none',
|
|
74
|
+
selectionPolicy: 'inherit',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
supportsInterrupt: true,
|
|
78
|
+
streamingTextMode: 'snapshot',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
26
81
|
function normalizeRuntimeTurnState(value) {
|
|
27
82
|
const normalizedTurn = normalizeTurnState(value);
|
|
28
83
|
if (normalizedTurn) {
|
|
@@ -62,6 +117,14 @@ function resolveWorkspaceCwd(config) {
|
|
|
62
117
|
defaultCwd: workingDir,
|
|
63
118
|
});
|
|
64
119
|
}
|
|
120
|
+
function resolveExecutionFallbackReason(environment) {
|
|
121
|
+
if (!environment?.reason || environment.mode !== 'locked') {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return environment.reason === 'Sharing the base workspace (locked mode)'
|
|
125
|
+
? null
|
|
126
|
+
: environment.reason;
|
|
127
|
+
}
|
|
65
128
|
function buildCanonPrompt(input) {
|
|
66
129
|
return buildCanonHostPrompt({
|
|
67
130
|
hostLabel: 'Codex',
|
|
@@ -194,6 +257,7 @@ export async function main() {
|
|
|
194
257
|
cwd: session.cwd,
|
|
195
258
|
executionMode: session.environment.mode,
|
|
196
259
|
...(session.environment.branch ? { executionBranch: session.environment.branch } : {}),
|
|
260
|
+
...(session.environment.worktreePath ? { worktreePath: session.environment.worktreePath } : {}),
|
|
197
261
|
hostMode: true,
|
|
198
262
|
clientType: 'codex',
|
|
199
263
|
state: session.state.state,
|
|
@@ -570,6 +634,13 @@ export async function main() {
|
|
|
570
634
|
...(codexPermissionEnvelope.defaultPermissionMode
|
|
571
635
|
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
572
636
|
: {}),
|
|
637
|
+
runtimeDescriptor: buildCodexRuntimeDescriptor({
|
|
638
|
+
models: [],
|
|
639
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
640
|
+
executionModes: hostAvailableExecutionModes,
|
|
641
|
+
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
642
|
+
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
643
|
+
}),
|
|
573
644
|
};
|
|
574
645
|
const publishRuntimeHeartbeat = async () => {
|
|
575
646
|
if (!streamConnected)
|
|
@@ -602,6 +673,49 @@ export async function main() {
|
|
|
602
673
|
}).catch((error) => {
|
|
603
674
|
console.error('[canon-codex] Failed to publish session snapshots:', error);
|
|
604
675
|
});
|
|
676
|
+
await Promise.all(Array.from(knownConversationIds).map(async (conversationId) => {
|
|
677
|
+
const session = sessions.get(conversationId);
|
|
678
|
+
const workspaceId = session
|
|
679
|
+
? resolveWorkspaceIdForBaseCwd(session.environment.baseCwd)
|
|
680
|
+
: runtimeDescriptor.defaultWorkspaceId;
|
|
681
|
+
const workspace = workspaceOptions.find((option) => option.id === workspaceId) ?? null;
|
|
682
|
+
const payload = {
|
|
683
|
+
descriptor: runtimeDescriptor.runtimeDescriptor ?? buildCodexRuntimeDescriptor({
|
|
684
|
+
models: runtimeDescriptor.availableModels ?? [],
|
|
685
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
686
|
+
executionModes: hostAvailableExecutionModes,
|
|
687
|
+
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
688
|
+
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
689
|
+
}),
|
|
690
|
+
surfaceMode: 'host',
|
|
691
|
+
statusItems: [
|
|
692
|
+
{
|
|
693
|
+
id: 'transport',
|
|
694
|
+
label: 'Transport',
|
|
695
|
+
value: 'exec --json',
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
id: 'streaming',
|
|
699
|
+
label: 'Live output',
|
|
700
|
+
value: 'Thinking, tools, and completed-message previews',
|
|
701
|
+
},
|
|
702
|
+
],
|
|
703
|
+
execution: {
|
|
704
|
+
resolvedWorkspaceLabel: workspace?.label ?? workspaceId ?? null,
|
|
705
|
+
resolvedCwd: session?.cwd ?? workspace?.cwd ?? workingDir,
|
|
706
|
+
executionMode: session?.environment.mode ?? null,
|
|
707
|
+
executionBranch: session?.environment.branch ?? null,
|
|
708
|
+
worktreePath: session?.environment.worktreePath ?? null,
|
|
709
|
+
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
710
|
+
},
|
|
711
|
+
notes: [
|
|
712
|
+
'This Codex host uses the current exec --json transport, so Canon can show thinking, tool activity, and completed assistant-message previews, but not token-by-token text deltas.',
|
|
713
|
+
],
|
|
714
|
+
};
|
|
715
|
+
await writeRuntimeInfo(conversationId, agentId, payload);
|
|
716
|
+
})).catch((error) => {
|
|
717
|
+
console.error('[canon-codex] Failed to publish runtime info:', error);
|
|
718
|
+
});
|
|
605
719
|
};
|
|
606
720
|
const stream = new CanonStream({
|
|
607
721
|
apiKey,
|
|
@@ -643,6 +757,13 @@ export async function main() {
|
|
|
643
757
|
...(codexPermissionEnvelope.defaultPermissionMode
|
|
644
758
|
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
645
759
|
: {}),
|
|
760
|
+
runtimeDescriptor: buildCodexRuntimeDescriptor({
|
|
761
|
+
models: [],
|
|
762
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
763
|
+
executionModes: hostAvailableExecutionModes,
|
|
764
|
+
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
765
|
+
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
766
|
+
}),
|
|
646
767
|
};
|
|
647
768
|
}
|
|
648
769
|
catch {
|
|
@@ -654,6 +775,13 @@ export async function main() {
|
|
|
654
775
|
...(codexPermissionEnvelope.defaultPermissionMode
|
|
655
776
|
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
656
777
|
: {}),
|
|
778
|
+
runtimeDescriptor: buildCodexRuntimeDescriptor({
|
|
779
|
+
models: [],
|
|
780
|
+
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
781
|
+
executionModes: hostAvailableExecutionModes,
|
|
782
|
+
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
783
|
+
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
784
|
+
}),
|
|
657
785
|
};
|
|
658
786
|
}
|
|
659
787
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"prepack": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@canonmsg/agent-sdk": "^0.
|
|
33
|
-
"@canonmsg/core": "^0.
|
|
32
|
+
"@canonmsg/agent-sdk": "^0.9.0",
|
|
33
|
+
"@canonmsg/core": "^0.8.0"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|