@canonmsg/codex-plugin 0.11.2 → 0.11.3

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 CHANGED
@@ -71,10 +71,12 @@ Advertise multiple project choices to the Canon app:
71
71
  canon-codex --cwd ~/dev --workspace-root ~/dev
72
72
  ```
73
73
 
74
- `--cwd` is the default workspace. Each `--workspace-root` value is an approved local root; the host discovers immediate child projects with common markers such as `.git`, `package.json`, `pyproject.toml`, `Cargo.toml`, or `go.mod` and publishes them as selectable projects during session creation. Use repeated `--workspace /path/to/project` entries to advertise specific projects outside those roots. Worktree mode creates a per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-project mode runs directly in the selected directory.
74
+ `--cwd` is the default workspace. Each `--workspace-root` value is an approved local root; the host discovers immediate child projects with common markers such as `.git`, `package.json`, `pyproject.toml`, `Cargo.toml`, or `go.mod` and publishes them as selectable projects during session creation. Use repeated `--workspace /path/to/project` entries to advertise specific projects outside those roots. Worktree mode creates a best-effort per-conversation git worktree under `~/.canon/conversation-worktrees`; shared-project mode runs directly in the selected directory.
75
75
 
76
76
  If worktree isolation is requested for a project that cannot support it, Canon may fall back to shared-project execution and surface the fallback reason in session details instead of failing the session outright.
77
77
 
78
+ Worktree mode is project isolation, not an operating-system sandbox. The Codex CLI sandbox and approval policy enforce actual file and command behavior.
79
+
78
80
  Useful flags:
79
81
 
80
82
  ```bash
@@ -85,6 +87,8 @@ Codex also supports `--add-dir /extra/path` for additional writable directories
85
87
 
86
88
  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`.
87
89
 
90
+ Do not start Canon with `--sandbox danger-full-access` as an unlabeled default. Use `--dangerously-bypass-approvals-and-sandbox` only when you intentionally want Canon to advertise the owner-only Bypass policy.
91
+
88
92
  Local smoke test:
89
93
 
90
94
  ```bash
package/dist/host.js CHANGED
@@ -3,7 +3,7 @@ import { setDefaultResultOrder } from 'node:dns';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { dirname } from 'node:path';
5
5
  import { parseArgs } from 'node:util';
6
- import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
6
+ import { getCodexImagePath, materializeMessageMedia, materializeReplyContextMedia, } from '@canonmsg/agent-sdk';
7
7
  import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, createConversationMetadataLoader, createRuntimeStatePublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, loadRuntimeSessionState, markLocalRuntimeStopped, normalizeTurnMetadata, normalizeTurnState, prepareConversationEnvironment, loadHostSessionConfig, releaseConversationEnvironment, resolveCanonAgent, rtdbRead, rtdbWrite, shouldTriggerAgentTurn, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
8
8
  import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
9
9
  import { CodexConversationAdapter, } from './adapter.js';
@@ -202,6 +202,21 @@ function buildCanonPrompt(input) {
202
202
  function renderInboundContent(message, materialized) {
203
203
  return renderCanonHostInboundContent(message, materialized);
204
204
  }
205
+ async function materializePromptReplyContext(input) {
206
+ if (!input.replyContext?.found || !input.replyContext.attachments?.length) {
207
+ return { replyContext: input.replyContext, materialized: [] };
208
+ }
209
+ try {
210
+ return await materializeReplyContextMedia(input.replyContext, {
211
+ agentId: input.agentId,
212
+ conversationId: input.conversationId,
213
+ });
214
+ }
215
+ catch (error) {
216
+ console.error(`${input.logPrefix} Failed to materialize replied-to media:`, error instanceof Error ? error.message : error);
217
+ return { replyContext: input.replyContext, materialized: [] };
218
+ }
219
+ }
205
220
  function summarizeCommand(command) {
206
221
  const trimmed = command.trim();
207
222
  if (!trimmed)
@@ -664,10 +679,6 @@ export async function main() {
664
679
  console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Failed to materialize media:`, error instanceof Error ? error.message : error);
665
680
  }
666
681
  }
667
- const imagePaths = materialized
668
- .map((attachment) => getCodexImagePath(attachment))
669
- .filter((path) => path !== null);
670
- const mediaAddDirs = uniqueStrings(materialized.map((attachment) => dirname(attachment.path)));
671
682
  const content = renderInboundContent(input.message, materialized);
672
683
  const hydrated = await loadHydratedInboundContext({
673
684
  conversationId: input.conversationId,
@@ -681,6 +692,18 @@ export async function main() {
681
692
  const behavior = input.behavior ?? hydrated.behavior;
682
693
  const activeSelfContextId = hydrated.activeSelfContextId;
683
694
  const selfContexts = hydrated.selfContexts;
695
+ const replyMedia = await materializePromptReplyContext({
696
+ replyContext: hydrated.replyContext,
697
+ agentId,
698
+ conversationId: input.conversationId,
699
+ logPrefix: `[canon-codex] [${input.conversationId.slice(0, 8)}]`,
700
+ });
701
+ const replyContext = replyMedia.replyContext;
702
+ const promptMaterialized = [...replyMedia.materialized, ...materialized];
703
+ const imagePaths = promptMaterialized
704
+ .map((attachment) => getCodexImagePath(attachment))
705
+ .filter((path) => path !== null);
706
+ const mediaAddDirs = uniqueStrings(promptMaterialized.map((attachment) => dirname(attachment.path)));
684
707
  const participantContext = hydrated.participantContext;
685
708
  const autoReply = decideAutoReply(participantContext, behavior);
686
709
  if (!autoReply.allow) {
@@ -717,6 +740,7 @@ export async function main() {
717
740
  participantContext,
718
741
  behavior,
719
742
  selfContexts,
743
+ replyContext,
720
744
  });
721
745
  if (session.running && deliveryIntent === 'interrupt') {
722
746
  enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs);
@@ -2,15 +2,19 @@ import type { CodexSandboxMode } from './adapter.js';
2
2
  export declare const CODEX_PERMISSION_OPTIONS: readonly [{
3
3
  readonly value: "readonly";
4
4
  readonly label: "Read-only";
5
+ readonly description: "Inspect files and project state without making changes.";
5
6
  }, {
6
7
  readonly value: "workspace";
7
8
  readonly label: "Workspace-write";
9
+ readonly description: "Edit inside the selected workspace using Codex CLI sandboxing.";
8
10
  }, {
9
11
  readonly value: "full-auto";
10
12
  readonly label: "Full auto";
13
+ readonly description: "Automatically run workspace-write actions with lower-friction approvals.";
11
14
  }, {
12
15
  readonly value: "bypass";
13
16
  readonly label: "Bypass (dangerous)";
17
+ readonly description: "Skip Codex approval and sandbox protection. Use only in an externally sandboxed environment.";
14
18
  }];
15
19
  export type CodexPermissionMode = (typeof CODEX_PERMISSION_OPTIONS)[number]['value'];
16
20
  export type CodexApprovalShape = {
@@ -1,8 +1,8 @@
1
1
  export const CODEX_PERMISSION_OPTIONS = [
2
- { value: 'readonly', label: 'Read-only' },
3
- { value: 'workspace', label: 'Workspace-write' },
4
- { value: 'full-auto', label: 'Full auto' },
5
- { value: 'bypass', label: 'Bypass (dangerous)' },
2
+ { value: 'readonly', label: 'Read-only', description: 'Inspect files and project state without making changes.' },
3
+ { value: 'workspace', label: 'Workspace-write', description: 'Edit inside the selected workspace using Codex CLI sandboxing.' },
4
+ { value: 'full-auto', label: 'Full auto', description: 'Automatically run workspace-write actions with lower-friction approvals.' },
5
+ { value: 'bypass', label: 'Bypass (dangerous)', description: 'Skip Codex approval and sandbox protection. Use only in an externally sandboxed environment.' },
6
6
  ];
7
7
  export function mapCanonPermissionToCodex(mode) {
8
8
  switch (mode) {
@@ -29,12 +29,8 @@ export function deriveCodexPermissionEnvelope(args) {
29
29
  availablePermissionModes: codexOptionsThrough('readonly'),
30
30
  };
31
31
  }
32
- if (args.sandbox === 'danger-full-access') {
33
- return {
34
- availablePermissionModes: args['dangerously-bypass-approvals-and-sandbox']
35
- ? [...CODEX_PERMISSION_OPTIONS]
36
- : codexOptionsThrough('full-auto'),
37
- };
32
+ if (args.sandbox === 'danger-full-access' && !args['dangerously-bypass-approvals-and-sandbox']) {
33
+ throw new Error('Codex sandbox danger-full-access cannot be used as an unlabeled Canon default. Use --dangerously-bypass-approvals-and-sandbox if you intentionally want the Bypass policy.');
38
34
  }
39
35
  if (args['dangerously-bypass-approvals-and-sandbox']) {
40
36
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
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": "^1.4.0",
33
- "@canonmsg/core": "^0.18.0"
32
+ "@canonmsg/agent-sdk": "^1.4.1",
33
+ "@canonmsg/core": "^0.18.1"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.0.0"