@canonmsg/codex-plugin 0.27.0 → 0.29.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 +0 -17
- package/dist/app-server-adapter.js +1 -7
- package/dist/codex-app-tools.d.ts +8 -26
- package/dist/codex-app-tools.js +30 -84
- package/dist/control-channel.d.ts +4 -14
- package/dist/control-channel.js +3 -10
- package/dist/host.d.ts +3 -38
- package/dist/host.js +124 -289
- package/dist/register.js +2 -1
- package/dist/session-store.d.ts +5 -1
- package/dist/session-store.js +14 -2
- package/package.json +6 -6
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
|
|
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;
|
|
@@ -22,6 +21,8 @@ export interface CodexAppToolRuntime {
|
|
|
22
21
|
currentThreadId: string | null;
|
|
23
22
|
currentCwd: string;
|
|
24
23
|
workspaces: ReadonlyArray<CodexAppToolWorkspace>;
|
|
24
|
+
/** Whether detached work may see Canon's autonomous communication surface. */
|
|
25
|
+
communicationEnabled?: boolean;
|
|
25
26
|
model?: string | null;
|
|
26
27
|
effort?: string | null;
|
|
27
28
|
}
|
|
@@ -45,11 +46,7 @@ type DynamicToolCallResponse = {
|
|
|
45
46
|
*/
|
|
46
47
|
export declare const CODEX_NO_REPLY_TOOL_NAME = "no_reply";
|
|
47
48
|
export declare const CANON_RUNTIME_CONTROL_TOOL_NAME = "canon_runtime_control";
|
|
48
|
-
export declare const
|
|
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";
|
|
49
|
+
export declare const CODEX_COMMUNICATE_TOOL_NAME = "communicate";
|
|
53
50
|
/**
|
|
54
51
|
* What the model actually sees for the tool above, for prompt text that names
|
|
55
52
|
* it (the group posture cue). Only meaningful on the app-server transport —
|
|
@@ -59,6 +56,8 @@ export declare const CODEX_NO_REPLY_MODEL_TOOL_NAME = "codex_app.no_reply";
|
|
|
59
56
|
export declare const CODEX_APP_DYNAMIC_TOOLS: ReadonlyArray<DynamicToolSpec>;
|
|
60
57
|
/** The complete model-visible surface for non-coding service agents. */
|
|
61
58
|
export declare const CODEX_SERVICE_AGENT_DYNAMIC_TOOLS: ReadonlyArray<DynamicToolSpec>;
|
|
59
|
+
/** Closed agents are not shown the optional outbound communication surface. */
|
|
60
|
+
export declare function filterCodexCommunicationTools(tools: ReadonlyArray<DynamicToolSpec>, outboundPolicy: 'open' | 'approval-required' | 'closed' | null): ReadonlyArray<DynamicToolSpec>;
|
|
62
61
|
/**
|
|
63
62
|
* Tools exposed to detached Codex threads created through the bridge. Canon
|
|
64
63
|
* conversation controls belong only to the foreground turn that received the
|
|
@@ -71,26 +70,9 @@ export declare function successfulCodexAppToolResult(payload: unknown): DynamicT
|
|
|
71
70
|
/** True when this dynamic-tool call is the deliberate-silence verb. */
|
|
72
71
|
export declare function isCodexNoReplyToolCall(params: CodexAppToolCallParams): boolean;
|
|
73
72
|
export declare function isCanonRuntimeControlToolCall(params: CodexAppToolCallParams): boolean;
|
|
74
|
-
export declare function
|
|
75
|
-
export declare function isCodexCanonVerbToolCall(params: CodexAppToolCallParams): boolean;
|
|
73
|
+
export declare function isCodexCommunicateToolCall(params: CodexAppToolCallParams): boolean;
|
|
76
74
|
export declare function isCodexServiceAgentToolCall(params: CodexAppToolCallParams): boolean;
|
|
77
|
-
export
|
|
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>;
|
|
75
|
+
export declare function handleCodexCommunicateToolCall(client: CanonClient, params: CodexAppToolCallParams): Promise<DynamicToolCallResponse>;
|
|
94
76
|
export interface CanonRuntimeControlRequest {
|
|
95
77
|
action: 'send_card' | 'request_card' | 'request_input';
|
|
96
78
|
card?: unknown;
|
package/dist/codex-app-tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NO_REPLY_ACK_NOTE, reportNoReplyOutcome, } from '@canonmsg/core';
|
|
2
|
-
import {
|
|
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
|
|
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
|
|
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,24 @@ 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
|
+
const communicationEnabled = outboundPolicy === 'open'
|
|
269
|
+
|| outboundPolicy === 'approval-required';
|
|
270
|
+
return communicationEnabled
|
|
271
|
+
? tools
|
|
272
|
+
: tools.filter((entry) => entry.name !== CODEX_COMMUNICATE_TOOL_NAME);
|
|
273
|
+
}
|
|
275
274
|
/**
|
|
276
275
|
* Tools exposed to detached Codex threads created through the bridge. Canon
|
|
277
276
|
* conversation controls belong only to the foreground turn that received the
|
|
278
277
|
* human message; a child thread has neither that turn nor its responder.
|
|
279
278
|
*/
|
|
280
279
|
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)));
|
|
280
|
+
&& entry.name !== CODEX_NO_REPLY_TOOL_NAME));
|
|
283
281
|
const CODEX_APP_TOOL_NAMES = new Set(CODEX_APP_DYNAMIC_TOOLS.map((entry) => String(entry.name)));
|
|
284
282
|
const UNSUPPORTED_TOOLS = new Map([
|
|
285
283
|
['automation_update', 'Canon does not manage Codex Desktop automations.'],
|
|
@@ -313,52 +311,21 @@ export function isCodexNoReplyToolCall(params) {
|
|
|
313
311
|
export function isCanonRuntimeControlToolCall(params) {
|
|
314
312
|
return normalizeToolName(params.tool) === CANON_RUNTIME_CONTROL_TOOL_NAME;
|
|
315
313
|
}
|
|
316
|
-
export function
|
|
317
|
-
|
|
318
|
-
return toolName ? CODEX_CANON_COMMUNICATION_BINDING.verbForToolName(toolName) : null;
|
|
319
|
-
}
|
|
320
|
-
export function isCodexCanonVerbToolCall(params) {
|
|
321
|
-
return codexCanonVerbForToolCall(params) !== null;
|
|
314
|
+
export function isCodexCommunicateToolCall(params) {
|
|
315
|
+
return normalizeToolName(params.tool) === CODEX_COMMUNICATE_TOOL_NAME;
|
|
322
316
|
}
|
|
323
317
|
export function isCodexServiceAgentToolCall(params) {
|
|
324
318
|
const toolName = normalizeToolName(params.tool);
|
|
325
|
-
return toolName === CANON_RUNTIME_CONTROL_TOOL_NAME
|
|
319
|
+
return toolName === CANON_RUNTIME_CONTROL_TOOL_NAME
|
|
320
|
+
|| toolName === CODEX_COMMUNICATE_TOOL_NAME
|
|
321
|
+
|| toolName === CODEX_NO_REPLY_TOOL_NAME;
|
|
326
322
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
}
|
|
323
|
+
export async function handleCodexCommunicateToolCall(client, params) {
|
|
324
|
+
const result = await communicationBinding.execute(client, CODEX_COMMUNICATE_TOOL_NAME, parseToolArguments(params.arguments));
|
|
325
|
+
return {
|
|
326
|
+
success: result.isError !== true,
|
|
327
|
+
contentItems: result.content.map((item) => ({ type: 'inputText', text: item.text })),
|
|
328
|
+
};
|
|
362
329
|
}
|
|
363
330
|
export function parseCanonRuntimeControlRequest(params) {
|
|
364
331
|
if (!isCanonRuntimeControlToolCall(params))
|
|
@@ -448,12 +415,6 @@ export async function handleCodexAppToolCall(runtime, params) {
|
|
|
448
415
|
error: 'canon_runtime_control is answered by the Canon host, not the app-tool bridge.',
|
|
449
416
|
});
|
|
450
417
|
}
|
|
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
418
|
const unsupportedReason = UNSUPPORTED_TOOLS.get(toolName);
|
|
458
419
|
if (unsupportedReason) {
|
|
459
420
|
return toolResult(false, { tool: toolName, error: unsupportedReason });
|
|
@@ -506,7 +467,7 @@ async function createThread(runtime, args) {
|
|
|
506
467
|
const started = await runtime.adapter.requestAppServer('thread/start', {
|
|
507
468
|
cwd,
|
|
508
469
|
...(readString(args, 'model') ?? runtime.model ? { model: readString(args, 'model') ?? runtime.model } : {}),
|
|
509
|
-
dynamicTools: CODEX_DETACHED_THREAD_DYNAMIC_TOOLS,
|
|
470
|
+
dynamicTools: filterCodexCommunicationTools(CODEX_DETACHED_THREAD_DYNAMIC_TOOLS, runtime.communicationEnabled === true ? 'open' : null),
|
|
510
471
|
experimentalRawEvents: false,
|
|
511
472
|
persistExtendedHistory: true,
|
|
512
473
|
});
|
|
@@ -702,21 +663,6 @@ function parseToolArguments(value) {
|
|
|
702
663
|
}
|
|
703
664
|
return {};
|
|
704
665
|
}
|
|
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
666
|
function normalizeToolName(value) {
|
|
721
667
|
if (typeof value !== 'string' || !value.trim())
|
|
722
668
|
return null;
|
|
@@ -5,18 +5,14 @@
|
|
|
5
5
|
* codex host characterization profile (the "codex host profile" describe
|
|
6
6
|
* block in core's control-poller.test.ts is the contract):
|
|
7
7
|
*
|
|
8
|
-
* - Keys: `
|
|
9
|
-
*
|
|
10
|
-
* at a time.
|
|
8
|
+
* - Keys: `signal`, plus `primitive` when a primitive handler is configured;
|
|
9
|
+
* read sequentially per conversation, conversations polled one at a time.
|
|
11
10
|
* - Cadence: immediate first cycle, then active/idle delays + jitter with
|
|
12
11
|
* the activity probe sampled BEFORE the cycle runs.
|
|
13
|
-
* - Session controls are always consumed once newer (default consume), even
|
|
14
|
-
* when no live session applied them; signals are NOT consumed when the
|
|
15
|
-
* handler throws (consumeOnError stays false).
|
|
16
12
|
* - Dedupe is primed eagerly via `poller.baseline([conversationId])` at
|
|
17
|
-
* session creation
|
|
13
|
+
* session creation.
|
|
18
14
|
*/
|
|
19
|
-
import { ControlChannelPoller, type ControlChannelRTDB, type ControlHandlerResult, type ControlPollerError, type ControlPrimitiveEvent, type
|
|
15
|
+
import { ControlChannelPoller, type ControlChannelRTDB, type ControlHandlerResult, type ControlPollerError, type ControlPrimitiveEvent, type ControlSignalEvent } from '@canonmsg/core';
|
|
20
16
|
export declare const CONTROL_POLL_MS = 2000;
|
|
21
17
|
export declare const IDLE_CONTROL_POLL_MS = 10000;
|
|
22
18
|
export declare const CONTROL_POLL_JITTER_MS = 1000;
|
|
@@ -28,12 +24,6 @@ export interface CodexControlChannelInput {
|
|
|
28
24
|
conversationIds: () => Iterable<string>;
|
|
29
25
|
/** Sampled before each cycle to choose the active vs idle delay. */
|
|
30
26
|
hasActiveWork: () => boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Applies a newer `/session` control node. The node is always consumed
|
|
33
|
-
* after the handler resolves; a thrown error leaves it in place (with
|
|
34
|
-
* dedupe already advanced, so it is never retried).
|
|
35
|
-
*/
|
|
36
|
-
onSessionControl: (event: ControlSessionEvent) => Promise<ControlHandlerResult> | ControlHandlerResult;
|
|
37
27
|
/**
|
|
38
28
|
* Handles a newer `/signal` node. Return `{ consume: false }` to leave the
|
|
39
29
|
* node in place; thrown errors also leave it (consumeOnError stays false).
|
package/dist/control-channel.js
CHANGED
|
@@ -5,16 +5,12 @@
|
|
|
5
5
|
* codex host characterization profile (the "codex host profile" describe
|
|
6
6
|
* block in core's control-poller.test.ts is the contract):
|
|
7
7
|
*
|
|
8
|
-
* - Keys: `
|
|
9
|
-
*
|
|
10
|
-
* at a time.
|
|
8
|
+
* - Keys: `signal`, plus `primitive` when a primitive handler is configured;
|
|
9
|
+
* read sequentially per conversation, conversations polled one at a time.
|
|
11
10
|
* - Cadence: immediate first cycle, then active/idle delays + jitter with
|
|
12
11
|
* the activity probe sampled BEFORE the cycle runs.
|
|
13
|
-
* - Session controls are always consumed once newer (default consume), even
|
|
14
|
-
* when no live session applied them; signals are NOT consumed when the
|
|
15
|
-
* handler throws (consumeOnError stays false).
|
|
16
12
|
* - Dedupe is primed eagerly via `poller.baseline([conversationId])` at
|
|
17
|
-
* session creation
|
|
13
|
+
* session creation.
|
|
18
14
|
*/
|
|
19
15
|
import { ControlChannelPoller, } from '@canonmsg/core';
|
|
20
16
|
export const CONTROL_POLL_MS = 2_000;
|
|
@@ -36,9 +32,6 @@ export function createCodexControlPoller(input) {
|
|
|
36
32
|
pollOnStart: true,
|
|
37
33
|
conversationConcurrency: 'sequential',
|
|
38
34
|
handlers: {
|
|
39
|
-
session: {
|
|
40
|
-
handle: input.onSessionControl,
|
|
41
|
-
},
|
|
42
35
|
signal: {
|
|
43
36
|
handle: input.onSignal,
|
|
44
37
|
},
|
package/dist/host.d.ts
CHANGED
|
@@ -21,38 +21,9 @@ export declare function buildCodexInitialSessionState(input: {
|
|
|
21
21
|
permissionMode?: string;
|
|
22
22
|
effort?: string | null;
|
|
23
23
|
}): HostSessionState;
|
|
24
|
-
export declare function buildCodexLiveSessionConfig(input: {
|
|
25
|
-
model?: string;
|
|
26
|
-
permissionMode?: string;
|
|
27
|
-
effort?: string;
|
|
28
|
-
workspaceId?: string | null;
|
|
29
|
-
executionMode: ExecutionEnvironmentMode;
|
|
30
|
-
executionBranch?: string | null;
|
|
31
|
-
}): {
|
|
32
|
-
executionMode: ExecutionEnvironmentMode;
|
|
33
|
-
executionBranch: string | null;
|
|
34
|
-
workspaceId?: string | undefined;
|
|
35
|
-
effort?: string | undefined;
|
|
36
|
-
permissionMode?: string | undefined;
|
|
37
|
-
model?: string | undefined;
|
|
38
|
-
};
|
|
39
|
-
export declare function buildCodexServiceSnapshotConfig(input: {
|
|
40
|
-
model?: string;
|
|
41
|
-
permissionMode?: string;
|
|
42
|
-
effort?: string;
|
|
43
|
-
workspaceId?: string;
|
|
44
|
-
}): {
|
|
45
|
-
model: string | undefined;
|
|
46
|
-
permissionMode: string | undefined;
|
|
47
|
-
effort: string | undefined;
|
|
48
|
-
workspaceId: string | undefined;
|
|
49
|
-
executionMode: "locked";
|
|
50
|
-
executionBranch: null;
|
|
51
|
-
};
|
|
52
24
|
export declare function createCodexRecoveryCheckpointTracker(persist: (messageId: string) => boolean): RecoveryCheckpointTracker;
|
|
53
25
|
/** Conservative fallback used only when native app-server discovery is unavailable. */
|
|
54
26
|
export declare const CODEX_EFFORT_OPTIONS: readonly CodexControlOption[];
|
|
55
|
-
export declare const CODEX_SESSION_CONFIG_FIELDS: readonly ["permissionMode", "effort"];
|
|
56
27
|
export declare function buildCodexSkillCommands(skills: ReadonlyArray<CodexSkillMetadata>): CanonRuntimeCommandDescriptor[];
|
|
57
28
|
export declare function buildCodexRuntimeDescriptor(input: {
|
|
58
29
|
models: CodexControlOption[];
|
|
@@ -61,6 +32,7 @@ export declare function buildCodexRuntimeDescriptor(input: {
|
|
|
61
32
|
workspaces: WorkspaceOption[];
|
|
62
33
|
workspaceRoots?: CanonWorkspaceRootMetadata[];
|
|
63
34
|
executionModes: ExecutionEnvironmentMode[];
|
|
35
|
+
defaultExecutionMode?: ExecutionEnvironmentMode;
|
|
64
36
|
permissionModes: Array<{
|
|
65
37
|
value: string;
|
|
66
38
|
label: string;
|
|
@@ -70,7 +42,6 @@ export declare function buildCodexRuntimeDescriptor(input: {
|
|
|
70
42
|
supportsPlanMode: boolean;
|
|
71
43
|
supportsCompact?: boolean;
|
|
72
44
|
supportsRichCards?: boolean;
|
|
73
|
-
supportsCanonCommunicationTools?: boolean;
|
|
74
45
|
skills?: ReadonlyArray<CodexSkillMetadata>;
|
|
75
46
|
serviceAgentMode?: boolean;
|
|
76
47
|
}): CanonRuntimeDescriptor;
|
|
@@ -86,14 +57,8 @@ export declare function getCodexRequestingUserId(message: {
|
|
|
86
57
|
senderId: string;
|
|
87
58
|
senderType?: 'human' | 'ai_agent';
|
|
88
59
|
}): string | null;
|
|
89
|
-
export declare function resolveSessionExecutionMode(
|
|
90
|
-
|
|
91
|
-
} | null | undefined, serviceAgentMode?: boolean): ExecutionEnvironmentMode;
|
|
92
|
-
export declare function resolveCodexSessionConfig<T extends Record<string, unknown>>(config: T | null | undefined, serviceAgentMode?: boolean): T | null;
|
|
93
|
-
export declare function resolveWorkspaceCwd(config: {
|
|
94
|
-
workspaceId?: string;
|
|
95
|
-
retiredWorkspaceConfig?: boolean;
|
|
96
|
-
} | null, serviceAgentMode?: boolean): string;
|
|
60
|
+
export declare function resolveSessionExecutionMode(serviceAgentMode?: boolean, defaultExecutionMode?: ExecutionEnvironmentMode): ExecutionEnvironmentMode;
|
|
61
|
+
export declare function resolveWorkspaceCwd(): string;
|
|
97
62
|
interface CodexEffectiveRuntimePolicy {
|
|
98
63
|
model?: string;
|
|
99
64
|
permissionMode?: string;
|