@canonmsg/codex-plugin 0.27.0 → 0.28.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 CHANGED
@@ -109,23 +109,6 @@ discovery it also offers no model picker and a fixed effort list. It registers
109
109
  no dynamic tools either, so `no_reply` is unavailable there, the same way rich
110
110
  cards are.
111
111
 
112
- On the app-server transport, an owner-started foreground turn also receives a
113
- small Canon communication surface: `codex_app.canon_send_to`, contact/request
114
- and conversation listing, and `codex_app.canon_cancel_contact_request` for the
115
- agent's own pending DM requests. `canon_send_to` accepts one stable contact
116
- target and visible text; the host supplies trusted source-turn routing. These
117
- tools are withheld from non-owner turns, service-agent mode, detached threads,
118
- and the exec fallback. Contact cards only claim reach-out support when this
119
- exact surface is mounted.
120
-
121
- When a reach-out needs target-owner setup or approval, Canon parks the visible
122
- opener. Terminal request outcomes enter a deduplicated local lifecycle inbox and
123
- are supplied as trusted context on the next natural owner turn; they never start
124
- a synthetic model turn. A connected outcome means Canon already delivered the
125
- opener. After an SSE reconnect or expired replay window, the host refreshes
126
- memberships, reconciles outbound request phases, and recovers unseen messages
127
- from its persisted cursors before resuming live delivery.
128
-
129
112
  Set `CANON_CODEX_TRANSPORT=exec` or `CANON_CODEX_TRANSPORT=app-server` to skip
130
113
  the probe when it misfires.
131
114
 
@@ -638,13 +638,7 @@ function isForegroundCanonToolCall(params) {
638
638
  const tool = rawTool.startsWith('codex_app.')
639
639
  ? rawTool.slice('codex_app.'.length)
640
640
  : rawTool;
641
- return tool === 'canon_runtime_control'
642
- || tool === 'canon_send_to'
643
- || tool === 'canon_list_contacts'
644
- || tool === 'canon_list_contact_requests'
645
- || tool === 'canon_list_conversations'
646
- || tool === 'canon_cancel_contact_request'
647
- || tool === 'no_reply';
641
+ return tool === 'canon_runtime_control' || tool === 'no_reply';
648
642
  }
649
643
  function parseJson(line) {
650
644
  try {
@@ -1,5 +1,4 @@
1
- import { type CanonClient, type CanonVerbName, type NoReplyReportClient } from '@canonmsg/core';
2
- import { type OwnerBoundCanonContactTarget } from '@canonmsg/agent-tools';
1
+ import { type CanonClient, type NoReplyReportClient } from '@canonmsg/core';
3
2
  type JsonRecord = Record<string, unknown>;
4
3
  interface DynamicToolSpec {
5
4
  [key: string]: unknown;
@@ -45,11 +44,7 @@ type DynamicToolCallResponse = {
45
44
  */
46
45
  export declare const CODEX_NO_REPLY_TOOL_NAME = "no_reply";
47
46
  export declare const CANON_RUNTIME_CONTROL_TOOL_NAME = "canon_runtime_control";
48
- export declare const CODEX_CANON_SEND_TO_TOOL_NAME = "canon_send_to";
49
- export declare const CODEX_CANON_LIST_CONTACTS_TOOL_NAME = "canon_list_contacts";
50
- export declare const CODEX_CANON_LIST_CONTACT_REQUESTS_TOOL_NAME = "canon_list_contact_requests";
51
- export declare const CODEX_CANON_LIST_CONVERSATIONS_TOOL_NAME = "canon_list_conversations";
52
- export declare const CODEX_CANON_CANCEL_CONTACT_REQUEST_TOOL_NAME = "canon_cancel_contact_request";
47
+ export declare const CODEX_COMMUNICATE_TOOL_NAME = "communicate";
53
48
  /**
54
49
  * What the model actually sees for the tool above, for prompt text that names
55
50
  * it (the group posture cue). Only meaningful on the app-server transport —
@@ -59,6 +54,8 @@ export declare const CODEX_NO_REPLY_MODEL_TOOL_NAME = "codex_app.no_reply";
59
54
  export declare const CODEX_APP_DYNAMIC_TOOLS: ReadonlyArray<DynamicToolSpec>;
60
55
  /** The complete model-visible surface for non-coding service agents. */
61
56
  export declare const CODEX_SERVICE_AGENT_DYNAMIC_TOOLS: ReadonlyArray<DynamicToolSpec>;
57
+ /** Closed agents are not shown the optional outbound communication surface. */
58
+ export declare function filterCodexCommunicationTools(tools: ReadonlyArray<DynamicToolSpec>, outboundPolicy: 'open' | 'approval-required' | 'closed' | null): ReadonlyArray<DynamicToolSpec>;
62
59
  /**
63
60
  * Tools exposed to detached Codex threads created through the bridge. Canon
64
61
  * conversation controls belong only to the foreground turn that received the
@@ -71,26 +68,9 @@ export declare function successfulCodexAppToolResult(payload: unknown): DynamicT
71
68
  /** True when this dynamic-tool call is the deliberate-silence verb. */
72
69
  export declare function isCodexNoReplyToolCall(params: CodexAppToolCallParams): boolean;
73
70
  export declare function isCanonRuntimeControlToolCall(params: CodexAppToolCallParams): boolean;
74
- export declare function codexCanonVerbForToolCall(params: CodexAppToolCallParams): CanonVerbName | null;
75
- export declare function isCodexCanonVerbToolCall(params: CodexAppToolCallParams): boolean;
71
+ export declare function isCodexCommunicateToolCall(params: CodexAppToolCallParams): boolean;
76
72
  export declare function isCodexServiceAgentToolCall(params: CodexAppToolCallParams): boolean;
77
- export interface CodexCanonVerbTrustedContext {
78
- conversationId: string;
79
- sourceMessageId: string | null;
80
- turnId: string | null;
81
- replyContactTarget?: OwnerBoundCanonContactTarget;
82
- }
83
- /**
84
- * Execute the deliberately filtered canonical-verb projection. The model can
85
- * author only the destination and visible copy. Provenance and turn metadata
86
- * come exclusively from the active host turn, so a prompt cannot forge its
87
- * source or make an outbound message look like an unrelated event.
88
- */
89
- export declare function answerCodexCanonVerb(input: {
90
- client: CanonClient;
91
- params: CodexAppToolCallParams;
92
- context: CodexCanonVerbTrustedContext;
93
- }): Promise<DynamicToolCallResponse>;
73
+ export declare function handleCodexCommunicateToolCall(client: CanonClient, params: CodexAppToolCallParams): Promise<DynamicToolCallResponse>;
94
74
  export interface CanonRuntimeControlRequest {
95
75
  action: 'send_card' | 'request_card' | 'request_input';
96
76
  card?: unknown;
@@ -1,5 +1,5 @@
1
1
  import { NO_REPLY_ACK_NOTE, reportNoReplyOutcome, } from '@canonmsg/core';
2
- import { createOwnerBoundCanonCommunicationBinding, } from '@canonmsg/agent-tools';
2
+ import { canonCommunicateToolDefinition, createCanonCommunicationBinding, } from '@canonmsg/agent-tools';
3
3
  const emptyObjectSchema = {
4
4
  type: 'object',
5
5
  properties: {},
@@ -91,21 +91,7 @@ function tool(name, description, inputSchema, deferLoading = true) {
91
91
  */
92
92
  export const CODEX_NO_REPLY_TOOL_NAME = 'no_reply';
93
93
  export const CANON_RUNTIME_CONTROL_TOOL_NAME = 'canon_runtime_control';
94
- export const CODEX_CANON_SEND_TO_TOOL_NAME = 'canon_send_to';
95
- export const CODEX_CANON_LIST_CONTACTS_TOOL_NAME = 'canon_list_contacts';
96
- export const CODEX_CANON_LIST_CONTACT_REQUESTS_TOOL_NAME = 'canon_list_contact_requests';
97
- export const CODEX_CANON_LIST_CONVERSATIONS_TOOL_NAME = 'canon_list_conversations';
98
- export const CODEX_CANON_CANCEL_CONTACT_REQUEST_TOOL_NAME = 'canon_cancel_contact_request';
99
- const CODEX_CANON_COMMUNICATION_BINDING = createOwnerBoundCanonCommunicationBinding({
100
- toolNames: {
101
- send_to: CODEX_CANON_SEND_TO_TOOL_NAME,
102
- list_contacts: CODEX_CANON_LIST_CONTACTS_TOOL_NAME,
103
- list_contact_requests: CODEX_CANON_LIST_CONTACT_REQUESTS_TOOL_NAME,
104
- list_conversations: CODEX_CANON_LIST_CONVERSATIONS_TOOL_NAME,
105
- cancel_contact_request: CODEX_CANON_CANCEL_CONTACT_REQUEST_TOOL_NAME,
106
- },
107
- });
108
- const CODEX_CANON_FOREGROUND_TOOL_NAMES = new Set(CODEX_CANON_COMMUNICATION_BINDING.tools.map((definition) => definition.name));
94
+ export const CODEX_COMMUNICATE_TOOL_NAME = 'communicate';
109
95
  /**
110
96
  * What the model actually sees for the tool above, for prompt text that names
111
97
  * it (the group posture cue). Only meaningful on the app-server transport —
@@ -137,7 +123,11 @@ const CANON_RUNTIME_CONTROL_TOOL = tool(CANON_RUNTIME_CONTROL_TOOL_NAME, 'Intera
137
123
  },
138
124
  required: ['action'],
139
125
  }, false);
140
- const CODEX_CANON_COMMUNICATION_TOOLS = CODEX_CANON_COMMUNICATION_BINDING.tools.map((definition) => tool(definition.name, definition.description, definition.inputSchema, false));
126
+ const communicationBinding = createCanonCommunicationBinding({
127
+ toolName: CODEX_COMMUNICATE_TOOL_NAME,
128
+ });
129
+ const communicateDefinition = canonCommunicateToolDefinition(CODEX_COMMUNICATE_TOOL_NAME);
130
+ const CODEX_COMMUNICATE_TOOL = tool(communicateDefinition.name, communicateDefinition.description, communicateDefinition.inputSchema, false);
141
131
  const CODEX_NO_REPLY_TOOL = tool(CODEX_NO_REPLY_TOOL_NAME, 'End your turn without posting anything to the conversation. Use it in group '
142
132
  + 'chats when you have nothing to add — no message is created, so no other '
143
133
  + 'member or agent is triggered. Optional private reason (logged, never '
@@ -261,8 +251,8 @@ export const CODEX_APP_DYNAMIC_TOOLS = [
261
251
  },
262
252
  required: ['threadId', 'title'],
263
253
  }),
264
- ...CODEX_CANON_COMMUNICATION_TOOLS,
265
254
  CANON_RUNTIME_CONTROL_TOOL,
255
+ CODEX_COMMUNICATE_TOOL,
266
256
  // Canon's `no_reply` verb, projected into the only model-visible tool surface
267
257
  // the Codex transport gives us. It is answered by the Canon host itself.
268
258
  CODEX_NO_REPLY_TOOL,
@@ -270,16 +260,22 @@ export const CODEX_APP_DYNAMIC_TOOLS = [
270
260
  /** The complete model-visible surface for non-coding service agents. */
271
261
  export const CODEX_SERVICE_AGENT_DYNAMIC_TOOLS = [
272
262
  CANON_RUNTIME_CONTROL_TOOL,
263
+ CODEX_COMMUNICATE_TOOL,
273
264
  CODEX_NO_REPLY_TOOL,
274
265
  ];
266
+ /** Closed agents are not shown the optional outbound communication surface. */
267
+ export function filterCodexCommunicationTools(tools, outboundPolicy) {
268
+ return outboundPolicy === 'closed'
269
+ ? tools.filter((entry) => entry.name !== CODEX_COMMUNICATE_TOOL_NAME)
270
+ : tools;
271
+ }
275
272
  /**
276
273
  * Tools exposed to detached Codex threads created through the bridge. Canon
277
274
  * conversation controls belong only to the foreground turn that received the
278
275
  * human message; a child thread has neither that turn nor its responder.
279
276
  */
280
277
  export const CODEX_DETACHED_THREAD_DYNAMIC_TOOLS = CODEX_APP_DYNAMIC_TOOLS.filter((entry) => (entry.name !== CANON_RUNTIME_CONTROL_TOOL_NAME
281
- && entry.name !== CODEX_NO_REPLY_TOOL_NAME
282
- && !CODEX_CANON_FOREGROUND_TOOL_NAMES.has(entry.name)));
278
+ && entry.name !== CODEX_NO_REPLY_TOOL_NAME));
283
279
  const CODEX_APP_TOOL_NAMES = new Set(CODEX_APP_DYNAMIC_TOOLS.map((entry) => String(entry.name)));
284
280
  const UNSUPPORTED_TOOLS = new Map([
285
281
  ['automation_update', 'Canon does not manage Codex Desktop automations.'],
@@ -313,52 +309,21 @@ export function isCodexNoReplyToolCall(params) {
313
309
  export function isCanonRuntimeControlToolCall(params) {
314
310
  return normalizeToolName(params.tool) === CANON_RUNTIME_CONTROL_TOOL_NAME;
315
311
  }
316
- export function codexCanonVerbForToolCall(params) {
317
- const toolName = normalizeToolName(params.tool);
318
- return toolName ? CODEX_CANON_COMMUNICATION_BINDING.verbForToolName(toolName) : null;
319
- }
320
- export function isCodexCanonVerbToolCall(params) {
321
- return codexCanonVerbForToolCall(params) !== null;
312
+ export function isCodexCommunicateToolCall(params) {
313
+ return normalizeToolName(params.tool) === CODEX_COMMUNICATE_TOOL_NAME;
322
314
  }
323
315
  export function isCodexServiceAgentToolCall(params) {
324
316
  const toolName = normalizeToolName(params.tool);
325
- return toolName === CANON_RUNTIME_CONTROL_TOOL_NAME || toolName === CODEX_NO_REPLY_TOOL_NAME;
317
+ return toolName === CANON_RUNTIME_CONTROL_TOOL_NAME
318
+ || toolName === CODEX_COMMUNICATE_TOOL_NAME
319
+ || toolName === CODEX_NO_REPLY_TOOL_NAME;
326
320
  }
327
- /**
328
- * Execute the deliberately filtered canonical-verb projection. The model can
329
- * author only the destination and visible copy. Provenance and turn metadata
330
- * come exclusively from the active host turn, so a prompt cannot forge its
331
- * source or make an outbound message look like an unrelated event.
332
- */
333
- export async function answerCodexCanonVerb(input) {
334
- const verb = codexCanonVerbForToolCall(input.params);
335
- if (!verb)
336
- return toolResult(false, { error: 'Unsupported Canon verb tool.' });
337
- try {
338
- const result = await CODEX_CANON_COMMUNICATION_BINDING.execute({
339
- client: input.client,
340
- toolName: normalizeToolName(input.params.tool) ?? '',
341
- arguments: input.params.arguments,
342
- context: input.context.sourceMessageId ? {
343
- isOwnerTurn: true,
344
- conversationId: input.context.conversationId,
345
- sourceMessageId: input.context.sourceMessageId,
346
- ...(input.context.turnId ? { turnId: input.context.turnId } : {}),
347
- ...(input.context.replyContactTarget
348
- ? { replyContactTarget: input.context.replyContactTarget }
349
- : {}),
350
- } : null,
351
- });
352
- return {
353
- success: result.isError !== true,
354
- contentItems: result.content.map((item) => ({ type: 'inputText', text: item.text })),
355
- };
356
- }
357
- catch (error) {
358
- return toolResult(false, {
359
- error: error instanceof Error ? error.message : String(error),
360
- });
361
- }
321
+ export async function handleCodexCommunicateToolCall(client, params) {
322
+ const result = await communicationBinding.execute(client, CODEX_COMMUNICATE_TOOL_NAME, parseToolArguments(params.arguments));
323
+ return {
324
+ success: result.isError !== true,
325
+ contentItems: result.content.map((item) => ({ type: 'inputText', text: item.text })),
326
+ };
362
327
  }
363
328
  export function parseCanonRuntimeControlRequest(params) {
364
329
  if (!isCanonRuntimeControlToolCall(params))
@@ -448,12 +413,6 @@ export async function handleCodexAppToolCall(runtime, params) {
448
413
  error: 'canon_runtime_control is answered by the Canon host, not the app-tool bridge.',
449
414
  });
450
415
  }
451
- if (CODEX_CANON_FOREGROUND_TOOL_NAMES.has(toolName)) {
452
- return toolResult(false, {
453
- tool: toolName,
454
- error: `${toolName} is answered by the Canon host, not the app-tool bridge.`,
455
- });
456
- }
457
416
  const unsupportedReason = UNSUPPORTED_TOOLS.get(toolName);
458
417
  if (unsupportedReason) {
459
418
  return toolResult(false, { tool: toolName, error: unsupportedReason });
@@ -702,21 +661,6 @@ function parseToolArguments(value) {
702
661
  }
703
662
  return {};
704
663
  }
705
- function parseToolArgumentsStrict(value) {
706
- if (value === undefined || value === null || value === '')
707
- return {};
708
- if (isRecord(value))
709
- return value;
710
- if (typeof value !== 'string' || !value.trim())
711
- return null;
712
- try {
713
- const parsed = JSON.parse(value);
714
- return isRecord(parsed) ? parsed : null;
715
- }
716
- catch {
717
- return null;
718
- }
719
- }
720
664
  function normalizeToolName(value) {
721
665
  if (typeof value !== 'string' || !value.trim())
722
666
  return null;
package/dist/host.d.ts CHANGED
@@ -61,6 +61,7 @@ export declare function buildCodexRuntimeDescriptor(input: {
61
61
  workspaces: WorkspaceOption[];
62
62
  workspaceRoots?: CanonWorkspaceRootMetadata[];
63
63
  executionModes: ExecutionEnvironmentMode[];
64
+ defaultExecutionMode?: ExecutionEnvironmentMode;
64
65
  permissionModes: Array<{
65
66
  value: string;
66
67
  label: string;
@@ -70,7 +71,6 @@ export declare function buildCodexRuntimeDescriptor(input: {
70
71
  supportsPlanMode: boolean;
71
72
  supportsCompact?: boolean;
72
73
  supportsRichCards?: boolean;
73
- supportsCanonCommunicationTools?: boolean;
74
74
  skills?: ReadonlyArray<CodexSkillMetadata>;
75
75
  serviceAgentMode?: boolean;
76
76
  }): CanonRuntimeDescriptor;
@@ -88,7 +88,7 @@ export declare function getCodexRequestingUserId(message: {
88
88
  }): string | null;
89
89
  export declare function resolveSessionExecutionMode(config: {
90
90
  executionMode?: ExecutionEnvironmentMode;
91
- } | null | undefined, serviceAgentMode?: boolean): ExecutionEnvironmentMode;
91
+ } | null | undefined, serviceAgentMode?: boolean, defaultExecutionMode?: ExecutionEnvironmentMode): ExecutionEnvironmentMode;
92
92
  export declare function resolveCodexSessionConfig<T extends Record<string, unknown>>(config: T | null | undefined, serviceAgentMode?: boolean): T | null;
93
93
  export declare function resolveWorkspaceCwd(config: {
94
94
  workspaceId?: string;
package/dist/host.js CHANGED
@@ -6,11 +6,11 @@ import { dirname } from 'node:path';
6
6
  import { parseArgs } from 'node:util';
7
7
  import { getCodexImagePath, materializeMessageMedia, materializeReplyContextMedia, sendMediaFileMessage, } from '@canonmsg/agent-sdk';
8
8
  import { buildTrailBlockId, buildUndeliverableFinalNotice, captureTurnArtifactSnapshot, createTurnArtifactRouter, IDLE_TIMEOUT_MS, PLAN_BLOCK_TITLE, resolveTurnArtifactRouting, collectMissedInboundMessages, createRecoveryCheckpointTracker, createReconnectRecoveryCoordinator, STARTUP_RECOVERY_PAGE_SIZE, } from '@canonmsg/coding-agent-host';
9
- import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, resolveQuestionAllowOther, buildCanonTurnContextV2, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, buildCanonInboundFrameV1, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, HOST_ADMISSION_ACTIONS_DISABLED, createConversationMetadataLoader, createRuntimeStatePublisher, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, decideAutoReply, initRTDBAuth, buildLocalRuntimeId, formatPendingContactLifecycleContext, heartbeatLocalRuntimeEntry, markLocalRuntimeStopped, normalizeTurnMetadata, parseTurnVerbosityConfig, RuntimeRequestManager, prepareConversationEnvironment, loadHostSessionConfig, releaseConversationEnvironment, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, readLocalRuntimeEntry, reconcileContactLifecycleEvents, recordLocalRuntimeContactLifecycleEvent, saveLocalRuntimeContactLifecycleCursor, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveHostWorkspaceCwd, isSilentTurnSuppressed, resolveSilentTurnDelivery, resolveTurnVerbosity, shouldTriggerAgentTurn, takeLocalRuntimeContactLifecycleEvents, upsertLocalRuntimeEntry, } from '@canonmsg/core';
9
+ import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, resolveQuestionAllowOther, buildCanonTurnContextV2, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, buildCanonInboundFrameV1, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createRuntimeStatePublisher, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, decideAutoReply, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, markLocalRuntimeStopped, normalizeTurnMetadata, parseTurnVerbosityConfig, RuntimeRequestManager, prepareConversationEnvironment, loadHostSessionConfig, releaseConversationEnvironment, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveHostWorkspaceCwd, isSilentTurnSuppressed, resolveSilentTurnDelivery, resolveTurnVerbosity, shouldTriggerAgentTurn, upsertLocalRuntimeEntry, } from '@canonmsg/core';
10
10
  import { validateCard } from '@canonmsg/rich-cards';
11
11
  import { CodexConversationAdapter, } from './adapter.js';
12
12
  import { CodexAppServerAdapter, } from './app-server-adapter.js';
13
- import { CODEX_APP_DYNAMIC_TOOLS, CODEX_NO_REPLY_MODEL_TOOL_NAME, CODEX_SERVICE_AGENT_DYNAMIC_TOOLS, answerCodexCanonVerb, answerCodexNoReply, classifyCodexAppToolRequest, deniedCodexAppToolResult, handleCodexAppToolCall, isCanonRuntimeControlToolCall, isCodexAppToolCall, isCodexCanonVerbToolCall, isCodexServiceAgentToolCall, parseCanonRuntimeControlRequest, readCodexNoReplyReason, successfulCodexAppToolResult, } from './codex-app-tools.js';
13
+ import { CODEX_APP_DYNAMIC_TOOLS, CODEX_NO_REPLY_MODEL_TOOL_NAME, CODEX_SERVICE_AGENT_DYNAMIC_TOOLS, answerCodexNoReply, classifyCodexAppToolRequest, deniedCodexAppToolResult, filterCodexCommunicationTools, handleCodexAppToolCall, handleCodexCommunicateToolCall, isCanonRuntimeControlToolCall, isCodexAppToolCall, isCodexCommunicateToolCall, isCodexServiceAgentToolCall, parseCanonRuntimeControlRequest, readCodexNoReplyReason, successfulCodexAppToolResult, } from './codex-app-tools.js';
14
14
  import { mapCanonApprovalResultToCodexDecision, mapCodexAppServerApprovalRequest, } from './app-server-approval.js';
15
15
  import { clearStoredThreadId, buildCodexThreadPolicyFingerprint, loadStoredThreadId, saveStoredThreadId, } from './session-store.js';
16
16
  import { deriveCodexPermissionEnvelope, mapCanonPermissionToCodex, } from './permission-mode.js';
@@ -30,6 +30,7 @@ COMMON FLAGS
30
30
  --cwd <path> Project directory to run Codex from
31
31
  --workspace <path> Additional project to expose in Canon
32
32
  --workspace-root <path> Discover projects under an approved root
33
+ --default-execution-mode <mode> New-conversation default: worktree or locked
33
34
  --model <model> Default Codex model for new turns
34
35
  --sandbox <mode> Codex sandbox mode
35
36
  --full-auto Allow non-interactive write access
@@ -199,6 +200,7 @@ export function buildCodexRuntimeDescriptor(input) {
199
200
  workspaces: serviceAgentMode ? [] : input.workspaces,
200
201
  workspaceRoots: serviceAgentMode ? undefined : input.workspaceRoots,
201
202
  executionModes: serviceAgentMode ? [] : input.executionModes,
203
+ defaultExecutionMode: serviceAgentMode ? 'locked' : input.defaultExecutionMode,
202
204
  permissionModes: serviceAgentMode ? [] : input.permissionModes,
203
205
  defaultPermissionMode: serviceAgentMode ? undefined : input.defaultPermissionMode,
204
206
  permissionModeLabel: 'Execution policy',
@@ -210,9 +212,6 @@ export function buildCodexRuntimeDescriptor(input) {
210
212
  effortLiveBehavior: 'next_turn',
211
213
  presentation: input.presentation,
212
214
  streamingTextMode: 'snapshot',
213
- admissionActions: input.supportsCanonCommunicationTools && !serviceAgentMode
214
- ? { ...HOST_ADMISSION_ACTIONS_DISABLED, requestContact: true, reachOut: true }
215
- : HOST_ADMISSION_ACTIONS_DISABLED,
216
215
  ...(input.supportsPlanMode && !serviceAgentMode
217
216
  ? {
218
217
  turnModes: [
@@ -284,21 +283,25 @@ export function getCodexRequestingUserId(message) {
284
283
  async function publishAgentRuntime(agentId, runtime, rtdb) {
285
284
  await publishHostAgentRuntime(agentId, 'codex', runtime, rtdb);
286
285
  }
287
- async function loadSessionConfig(conversationId, agentId, rtdb, retryMissing = true) {
286
+ async function loadSessionConfig(conversationId, agentId, rtdb) {
288
287
  return loadHostSessionConfig({
289
288
  conversationId,
290
289
  agentId,
291
290
  rtdb,
292
291
  extraStringFields: CODEX_SESSION_CONFIG_FIELDS,
293
- retryMissingMs: retryMissing ? 3_000 : 0,
294
292
  });
295
293
  }
296
- export function resolveSessionExecutionMode(config, serviceAgentMode = false) {
294
+ export function resolveSessionExecutionMode(config, serviceAgentMode = false, defaultExecutionMode = 'worktree') {
297
295
  if (serviceAgentMode)
298
296
  return 'locked';
299
- if (config?.executionMode)
300
- return config.executionMode;
301
- throw new ExecutionEnvironmentError('Session config is missing an execution mode.', 'Choose Isolated worktree or Use shared project before starting this coding session.');
297
+ return config?.executionMode ?? defaultExecutionMode;
298
+ }
299
+ function resolveConfiguredDefaultExecutionMode(value) {
300
+ if (value == null || value === '')
301
+ return 'worktree';
302
+ if (value === 'worktree' || value === 'locked')
303
+ return value;
304
+ throw new Error('--default-execution-mode must be worktree or locked');
302
305
  }
303
306
  export function resolveCodexSessionConfig(config, serviceAgentMode = false) {
304
307
  return serviceAgentMode ? null : config ?? null;
@@ -377,8 +380,8 @@ function buildCanonPrompt(input) {
377
380
  message: input.message,
378
381
  })), input.noReplyToolName ? { noReplyToolName: input.noReplyToolName } : {});
379
382
  }
380
- function renderInboundContent(message, materialized, options) {
381
- return renderCanonHostInboundContent(message, materialized, options);
383
+ function renderInboundContent(message, materialized) {
384
+ return renderCanonHostInboundContent(message, materialized);
382
385
  }
383
386
  async function materializePromptReplyContext(input) {
384
387
  if (!input.replyContext?.found || !input.replyContext.attachments?.length) {
@@ -395,16 +398,6 @@ async function materializePromptReplyContext(input) {
395
398
  return { replyContext: input.replyContext, materialized: [] };
396
399
  }
397
400
  }
398
- function ownerBoundReplyContactTarget(replyContext) {
399
- const card = replyContext?.found ? replyContext.contactCard : undefined;
400
- if (!card?.userId)
401
- return undefined;
402
- return {
403
- targetUserId: card.userId,
404
- ...(card.canonContactId ? { canonContactId: card.canonContactId } : {}),
405
- sourceCardMessageId: replyContext.messageId,
406
- };
407
- }
408
401
  function summarizeCommand(command) {
409
402
  const trimmed = command.trim();
410
403
  if (!trimmed)
@@ -650,6 +643,7 @@ export async function main() {
650
643
  'add-dir': { type: 'string', multiple: true },
651
644
  workspace: { type: 'string', multiple: true },
652
645
  'workspace-root': { type: 'string', multiple: true },
646
+ 'default-execution-mode': { type: 'string' },
653
647
  config: { type: 'string', multiple: true },
654
648
  'codex-bin': { type: 'string' },
655
649
  'runtime-visibility': { type: 'string' },
@@ -674,8 +668,11 @@ export async function main() {
674
668
  onWarning: (message) => console.error(`[canon-codex] ${message}`),
675
669
  });
676
670
  const serviceAgentMode = args['service-agent'] === true;
671
+ const defaultExecutionMode = serviceAgentMode
672
+ ? 'locked'
673
+ : resolveConfiguredDefaultExecutionMode(args['default-execution-mode']);
677
674
  const nativeVisionEnabled = args['no-native-vision'] !== true;
678
- const codexDynamicTools = serviceAgentMode
675
+ const baseCodexDynamicTools = serviceAgentMode
679
676
  ? CODEX_SERVICE_AGENT_DYNAMIC_TOOLS
680
677
  : CODEX_APP_DYNAMIC_TOOLS;
681
678
  workingDir = (typeof args.cwd === 'string' ? args.cwd : null) || process.cwd();
@@ -720,11 +717,13 @@ export async function main() {
720
717
  let agentId;
721
718
  let ownerId = null;
722
719
  let ownerName = null;
720
+ let outboundPolicy = null;
723
721
  try {
724
722
  const ctx = await client.getAgentMe();
725
723
  agentId = ctx.agentId;
726
724
  ownerId = ctx.ownerId;
727
725
  ownerName = ctx.ownerName;
726
+ outboundPolicy = ctx.outboundPolicy;
728
727
  console.error(`[canon-codex] Connected as ${ctx.displayName || agentId}`);
729
728
  }
730
729
  catch {
@@ -737,6 +736,7 @@ export async function main() {
737
736
  }
738
737
  console.error(`[canon-codex] Authenticated as ${agentId}`);
739
738
  }
739
+ const codexDynamicTools = filterCodexCommunicationTools(baseCodexDynamicTools, outboundPolicy);
740
740
  // Shared poll/timeout engine. Built-in `input`/`card` descriptors own
741
741
  // create+poll (codex passes native/responder policy via payload/options).
742
742
  // Approval keeps codex's own resolution shape (no owner-authored outcome
@@ -927,7 +927,6 @@ export async function main() {
927
927
  ownerName,
928
928
  membershipChange: pendingMembershipChanges.get(input.conversationId) ?? null,
929
929
  groupContextMode: getGroupContextMode(input.conversationId, conversation),
930
- renderOptions: input.renderOptions,
931
930
  });
932
931
  }
933
932
  function writeState(session) {
@@ -1225,7 +1224,6 @@ export async function main() {
1225
1224
  session.currentTurnOpenedAt = null;
1226
1225
  session.currentTurnUpdatedAt = null;
1227
1226
  session.currentTurnCanUseCodexAppTools = false;
1228
- session.currentTurnCanUseCodexCanonTools = false;
1229
1227
  session.currentTurnSilenced = false;
1230
1228
  session.lastAcceptedIntent = null;
1231
1229
  session.resetRequested = false;
@@ -1263,8 +1261,8 @@ export async function main() {
1263
1261
  evictOldestIdle();
1264
1262
  }
1265
1263
  const creation = (async () => {
1266
- const config = resolveCodexSessionConfig(await loadSessionConfig(conversationId, agentId, rtdb, !serviceAgentMode), serviceAgentMode);
1267
- const sessionExecutionMode = resolveSessionExecutionMode(config, serviceAgentMode);
1264
+ const config = resolveCodexSessionConfig(await loadSessionConfig(conversationId, agentId, rtdb), serviceAgentMode);
1265
+ const sessionExecutionMode = resolveSessionExecutionMode(config, serviceAgentMode, defaultExecutionMode);
1268
1266
  const workspaceCwd = resolveWorkspaceCwd(config, serviceAgentMode);
1269
1267
  const environment = prepareConversationEnvironment({
1270
1268
  agentId,
@@ -1338,8 +1336,6 @@ export async function main() {
1338
1336
  currentTurnOpenedAt: null,
1339
1337
  currentTurnUpdatedAt: null,
1340
1338
  currentTurnCanUseCodexAppTools: false,
1341
- currentTurnCanUseCodexCanonTools: false,
1342
- currentTurnReplyContactTarget: null,
1343
1339
  currentTurnAbortController: null,
1344
1340
  // Corrected by the first turn that runs; a session with no turn
1345
1341
  // publishes nothing anyway, and quiet is never an accident.
@@ -1386,10 +1382,8 @@ export async function main() {
1386
1382
  planMode,
1387
1383
  artifactRoutingMode: turn.artifactRoutingMode ?? 'disabled',
1388
1384
  canUseCodexAppTools: turn.canUseCodexAppTools ?? false,
1389
- canUseCodexCanonTools: turn.canUseCodexCanonTools ?? false,
1390
1385
  ...(turn.turnVerbosity ? { turnVerbosity: turn.turnVerbosity } : {}),
1391
1386
  requestingUserId: turn.requestingUserId ?? null,
1392
- ...(turn.replyContactTarget ? { replyContactTarget: turn.replyContactTarget } : {}),
1393
1387
  };
1394
1388
  if (toFront) {
1395
1389
  session.queue.unshift(nextPrompt);
@@ -1420,38 +1414,6 @@ export async function main() {
1420
1414
  requestingUserId: responseUserId,
1421
1415
  });
1422
1416
  }
1423
- function recordContactLifecycleEvent(request) {
1424
- if (request.requesterId !== agentId || !request.sourceConversationId)
1425
- return false;
1426
- return recordLocalRuntimeContactLifecycleEvent(runtimeId, request);
1427
- }
1428
- async function reconcileContactLifecycleInbox() {
1429
- let cursor = readLocalRuntimeEntry(runtimeId)?.contactLifecycleCursor ?? null;
1430
- let recorded = 0;
1431
- let duplicate = 0;
1432
- let ignored = 0;
1433
- for (let pageNumber = 0; pageNumber < 10; pageNumber += 1) {
1434
- const page = await client.listContactRequestLifecyclePage({
1435
- cursor,
1436
- limit: 100,
1437
- });
1438
- const result = await reconcileContactLifecycleEvents({
1439
- requests: page.requests,
1440
- requesterId: agentId,
1441
- record: (request) => recordLocalRuntimeContactLifecycleEvent(runtimeId, request),
1442
- });
1443
- recorded += result.recorded;
1444
- duplicate += result.duplicate;
1445
- ignored += result.ignored;
1446
- cursor = page.nextCursor;
1447
- saveLocalRuntimeContactLifecycleCursor(runtimeId, cursor);
1448
- if (!page.hasMore)
1449
- break;
1450
- }
1451
- if (recorded > 0) {
1452
- console.error(`[canon-codex] Contact lifecycle recovery: recorded=${recorded} duplicate=${duplicate} ignored=${ignored}`);
1453
- }
1454
- }
1455
1417
  function resolveArtifactRoutingMode(participantContext) {
1456
1418
  return participantContext.conversationType === 'direct' && participantContext.isOwner
1457
1419
  ? 'workspace-generated'
@@ -1463,19 +1425,15 @@ export async function main() {
1463
1425
  * so a prompt that waits behind another still runs under the answer it
1464
1426
  * arrived with.
1465
1427
  */
1466
- function resolveCodexTurnModes(participantContext, message, replyContext) {
1428
+ function resolveCodexTurnModes(participantContext, message) {
1467
1429
  return {
1468
1430
  artifactRoutingMode: resolveArtifactRoutingMode(participantContext),
1469
1431
  canUseCodexAppTools: participantContext.isOwner || serviceAgentMode,
1470
- canUseCodexCanonTools: participantContext.isOwner && !serviceAgentMode,
1471
1432
  turnVerbosity: resolveTurnVerbosity({
1472
1433
  configured: configuredTurnVerbosity,
1473
1434
  conversationType: participantContext.conversationType,
1474
1435
  }),
1475
1436
  requestingUserId: getCodexRequestingUserId(message),
1476
- ...(participantContext.isOwner && replyContext
1477
- ? { replyContactTarget: ownerBoundReplyContactTarget(replyContext) }
1478
- : {}),
1479
1437
  };
1480
1438
  }
1481
1439
  function runtimeCardRequestPayload(method, params) {
@@ -1492,6 +1450,12 @@ export async function main() {
1492
1450
  const params = request.params;
1493
1451
  const expiresAt = Date.now() + 30 * 60_000;
1494
1452
  if (request.method === 'item/tool/call' && isCodexAppToolCall(params)) {
1453
+ if (isCodexCommunicateToolCall(params)) {
1454
+ if (!(session.adapter instanceof CodexAppServerAdapter)) {
1455
+ return deniedCodexAppToolResult('This Codex transport does not support dynamic tools.');
1456
+ }
1457
+ return handleCodexCommunicateToolCall(client, params);
1458
+ }
1495
1459
  if (serviceAgentMode && !isCodexServiceAgentToolCall(params)) {
1496
1460
  return deniedCodexAppToolResult('This service agent exposes only Canon conversation tools.');
1497
1461
  }
@@ -1517,23 +1481,6 @@ export async function main() {
1517
1481
  if (disposition === 'denied-non-owner') {
1518
1482
  return deniedCodexAppToolResult('Only the Canon owner can use codex_app tools.');
1519
1483
  }
1520
- if (isCodexCanonVerbToolCall(params)) {
1521
- if (!session.currentTurnCanUseCodexCanonTools) {
1522
- return deniedCodexAppToolResult('Canon contact and conversation tools require a real owner-authored inbound turn.');
1523
- }
1524
- return await answerCodexCanonVerb({
1525
- client,
1526
- params,
1527
- context: {
1528
- conversationId: session.conversationId,
1529
- sourceMessageId,
1530
- turnId: session.currentTurnId,
1531
- ...(session.currentTurnReplyContactTarget
1532
- ? { replyContactTarget: session.currentTurnReplyContactTarget }
1533
- : {}),
1534
- },
1535
- });
1536
- }
1537
1484
  if (isCanonRuntimeControlToolCall(params)) {
1538
1485
  const command = parseCanonRuntimeControlRequest(params);
1539
1486
  if (!command) {
@@ -1873,8 +1820,7 @@ export async function main() {
1873
1820
  console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Failed to materialize media:`, error instanceof Error ? error.message : error);
1874
1821
  }
1875
1822
  }
1876
- const renderOptions = resolveInboundContentRenderOptions(input.isOwner);
1877
- const renderedContent = renderInboundContent(input.message, materialized, renderOptions);
1823
+ const renderedContent = renderInboundContent(input.message, materialized);
1878
1824
  const turnMetadata = normalizeTurnMetadata(input.message.metadata);
1879
1825
  const requestedPlanMode = turnMetadata?.requestedTurnMode === 'plan';
1880
1826
  const planCommand = resolveCodexPlanCommand({
@@ -1893,7 +1839,6 @@ export async function main() {
1893
1839
  selfContexts: input.selfContexts,
1894
1840
  provenance: input.provenance,
1895
1841
  hydratedPage: input.hydratedPage,
1896
- renderOptions,
1897
1842
  });
1898
1843
  const behavior = input.behavior ?? hydrated.behavior;
1899
1844
  const activeSelfContextId = hydrated.activeSelfContextId;
@@ -1948,7 +1893,7 @@ export async function main() {
1948
1893
  return;
1949
1894
  }
1950
1895
  session.activeSelfContextId = activeSelfContextId;
1951
- const basePrompt = buildCanonPrompt({
1896
+ const prompt = buildCanonPrompt({
1952
1897
  content,
1953
1898
  conversationId: input.conversationId,
1954
1899
  participantContext,
@@ -1960,12 +1905,8 @@ export async function main() {
1960
1905
  message: input.message,
1961
1906
  ...(useAppServer ? { noReplyToolName: CODEX_NO_REPLY_MODEL_TOOL_NAME } : {}),
1962
1907
  });
1963
- const lifecycleContext = input.isOwner
1964
- ? formatPendingContactLifecycleContext(takeLocalRuntimeContactLifecycleEvents(runtimeId, input.conversationId))
1965
- : null;
1966
- const prompt = lifecycleContext ? `${basePrompt}\n\n${lifecycleContext}` : basePrompt;
1967
1908
  if (session.running && deliveryIntent === 'interrupt') {
1968
- enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, resolveCodexTurnModes(participantContext, input.message, replyContext));
1909
+ enqueuePrompt(session, prompt, deliveryIntent, true, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, resolveCodexTurnModes(participantContext, input.message));
1969
1910
  console.error(`[canon-codex] [${input.conversationId.slice(0, 8)}] Interrupting current turn for explicit human send-now`);
1970
1911
  session.currentTurnAbortController?.abort(new Error('Codex turn interrupted by a newer message'));
1971
1912
  await session.adapter.interrupt().catch(() => { });
@@ -1973,7 +1914,7 @@ export async function main() {
1973
1914
  typingSignals.clear(input.conversationId).catch(() => { });
1974
1915
  return;
1975
1916
  }
1976
- enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, resolveCodexTurnModes(participantContext, input.message, replyContext));
1917
+ enqueuePrompt(session, prompt, deliveryIntent, false, input.message.id, shouldMarkAccepted, imagePaths, mediaAddDirs, planCommand.planMode, resolveCodexTurnModes(participantContext, input.message));
1977
1918
  }
1978
1919
  function sendTurnArtifactFile(session, file) {
1979
1920
  return sendMediaFileMessage(client, session.conversationId, file.path, '', {
@@ -2004,8 +1945,6 @@ export async function main() {
2004
1945
  session.currentTurnOpenedAt = Date.now();
2005
1946
  session.currentTurnUpdatedAt = session.currentTurnOpenedAt;
2006
1947
  session.currentTurnCanUseCodexAppTools = nextTurn.canUseCodexAppTools === true;
2007
- session.currentTurnCanUseCodexCanonTools = nextTurn.canUseCodexCanonTools === true;
2008
- session.currentTurnReplyContactTarget = nextTurn.replyContactTarget ?? null;
2009
1948
  session.currentTurnAbortController = new AbortController();
2010
1949
  // A continuation prompt (a plan-review result) carries none, and keeps the
2011
1950
  // conversation's last answer rather than silently reverting to verbose.
@@ -2419,8 +2358,6 @@ export async function main() {
2419
2358
  session.currentTurnOpenedAt = null;
2420
2359
  session.currentTurnUpdatedAt = null;
2421
2360
  session.currentTurnCanUseCodexAppTools = false;
2422
- session.currentTurnCanUseCodexCanonTools = false;
2423
- session.currentTurnReplyContactTarget = null;
2424
2361
  session.currentTurnSilenced = false;
2425
2362
  session.lastAcceptedIntent = null;
2426
2363
  session.resetRequested = false;
@@ -2479,6 +2416,7 @@ export async function main() {
2479
2416
  });
2480
2417
  let codexSkills = [];
2481
2418
  const buildCurrentRuntimeDescriptor = () => ({
2419
+ defaultExecutionMode,
2482
2420
  ...(serviceAgentMode
2483
2421
  ? {}
2484
2422
  : {
@@ -2498,31 +2436,18 @@ export async function main() {
2498
2436
  workspaces: buildPublicWorkspaceOptions(workspaceOptions),
2499
2437
  workspaceRoots: workspaceRootMetadata,
2500
2438
  executionModes: hostAvailableExecutionModes,
2439
+ defaultExecutionMode,
2501
2440
  permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
2502
2441
  defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
2503
2442
  presentation: runtimePresentation,
2504
2443
  supportsPlanMode: useAppServer,
2505
2444
  supportsCompact: useAppServer,
2506
2445
  supportsRichCards: useAppServer,
2507
- supportsCanonCommunicationTools: useAppServer,
2508
2446
  skills: codexSkills,
2509
2447
  serviceAgentMode,
2510
2448
  }),
2511
2449
  });
2512
2450
  let runtimeDescriptor = buildCurrentRuntimeDescriptor();
2513
- const resolveInboundContentRenderOptions = (isOwnerTurn) => {
2514
- const descriptorActions = runtimeDescriptor.runtimeDescriptor?.admissionActions
2515
- ?? HOST_ADMISSION_ACTIONS_DISABLED;
2516
- const admissionActions = isOwnerTurn && useAppServer && !serviceAgentMode
2517
- ? descriptorActions
2518
- : HOST_ADMISSION_ACTIONS_DISABLED;
2519
- return {
2520
- admissionActions,
2521
- ...(admissionActions.reachOut
2522
- ? { reachOutToolName: 'codex_app.canon_send_to' }
2523
- : {}),
2524
- };
2525
- };
2526
2451
  async function refreshCodexSkillInventory(forceReload = false) {
2527
2452
  if (!useAppServer)
2528
2453
  return;
@@ -2844,12 +2769,6 @@ export async function main() {
2844
2769
  const conversationsDiscoveredWhileOffline = startupRecoveryComplete
2845
2770
  ? new Set([...knownConversationIds].filter((id) => !knownBeforeRefresh.has(id)))
2846
2771
  : new Set();
2847
- try {
2848
- await reconcileContactLifecycleInbox();
2849
- }
2850
- catch (error) {
2851
- console.error('[canon-codex] Contact lifecycle recovery failed:', error instanceof Error ? error.message : error);
2852
- }
2853
2772
  for (const conversationId of knownConversationIds) {
2854
2773
  const recoveryCheckpoints = recoveryCheckpointsFor(conversationId);
2855
2774
  const recoveryBatch = recoveryCheckpoints.reserveBatch();
@@ -2957,9 +2876,6 @@ export async function main() {
2957
2876
  onConversationUpdated: (payload) => {
2958
2877
  handleConversationUpdated(payload);
2959
2878
  },
2960
- onContactRequestUpdated: (payload) => {
2961
- recordContactLifecycleEvent(payload);
2962
- },
2963
2879
  onConnected: () => {
2964
2880
  streamConnected = true;
2965
2881
  void publishRuntimeHeartbeat();
package/dist/register.js CHANGED
@@ -28,7 +28,6 @@ After approval, start it with CANON_AGENT=<profile> canon-codex --cwd /path/to/p
28
28
  const OPTIONS = {
29
29
  moduleUrl: import.meta.url,
30
30
  clientType: 'codex',
31
- sessionSetupPolicy: 'runtime_descriptor_required',
32
31
  cliName: 'canon-codex-register',
33
32
  hostBinName: 'canon-codex',
34
33
  developerInfo: 'Codex host plugin',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,11 +29,11 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@canonmsg/agent-sdk": "^8.9.0",
33
- "@canonmsg/agent-tools": "^0.6.0",
34
- "@canonmsg/coding-agent-host": "^0.6.0",
35
- "@canonmsg/core": "^10.7.0",
36
- "@canonmsg/rich-cards": "^0.10.0"
32
+ "@canonmsg/agent-sdk": "^9.0.0",
33
+ "@canonmsg/agent-tools": "^0.7.0",
34
+ "@canonmsg/coding-agent-host": "^0.7.0",
35
+ "@canonmsg/core": "^11.0.0",
36
+ "@canonmsg/rich-cards": "^0.10.3"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=18.0.0"