@canonmsg/codex-plugin 0.29.1 → 0.29.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/host.js +99 -103
- package/package.json +3 -3
package/dist/host.js
CHANGED
|
@@ -6,7 +6,7 @@ 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, 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, releaseConversationEnvironment, resolveLocalRuntimeSessionState, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState,
|
|
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, createRuntimeHeartbeat, 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, releaseConversationEnvironment, resolveLocalRuntimeSessionState, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState, publishHostSessionSnapshots, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveConfiguredWorkspaceCwd, 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';
|
|
@@ -253,9 +253,6 @@ export function buildCodexTurnResponseRouting(input) {
|
|
|
253
253
|
export function getCodexRequestingUserId(message) {
|
|
254
254
|
return message.senderType === 'human' ? message.senderId : null;
|
|
255
255
|
}
|
|
256
|
-
async function publishAgentRuntime(agentId, runtime, rtdb) {
|
|
257
|
-
await publishHostAgentRuntime(agentId, 'codex', runtime, rtdb);
|
|
258
|
-
}
|
|
259
256
|
export function resolveSessionExecutionMode(serviceAgentMode = false, defaultExecutionMode = 'worktree') {
|
|
260
257
|
if (serviceAgentMode)
|
|
261
258
|
return 'locked';
|
|
@@ -2022,7 +2019,7 @@ export async function main() {
|
|
|
2022
2019
|
return;
|
|
2023
2020
|
}
|
|
2024
2021
|
if (event.type === 'skills.changed') {
|
|
2025
|
-
void refreshCodexSkillInventory(true).then(() =>
|
|
2022
|
+
void refreshCodexSkillInventory(true).then(() => runtimeHeartbeat.refresh());
|
|
2026
2023
|
return;
|
|
2027
2024
|
}
|
|
2028
2025
|
if (event.type === 'settings.updated') {
|
|
@@ -2401,7 +2398,6 @@ export async function main() {
|
|
|
2401
2398
|
acceptedInboundMessageIds.delete(oldest);
|
|
2402
2399
|
}
|
|
2403
2400
|
}
|
|
2404
|
-
let streamConnected = false;
|
|
2405
2401
|
const hostAvailableExecutionModes = serviceAgentMode
|
|
2406
2402
|
? ['locked']
|
|
2407
2403
|
: [...EXECUTION_ENVIRONMENT_MODES];
|
|
@@ -2569,108 +2565,109 @@ export async function main() {
|
|
|
2569
2565
|
console.error(`[canon-codex] [${(error.conversationId ?? 'unknown').slice(0, 8)}] Control ${error.key ?? 'poll'} handler failed:`, error.error instanceof Error ? error.error.message : error.error);
|
|
2570
2566
|
},
|
|
2571
2567
|
});
|
|
2572
|
-
|
|
2573
|
-
const publishRuntimeHeartbeat = async () => {
|
|
2568
|
+
const publishLocalHeartbeat = () => {
|
|
2574
2569
|
heartbeatLocalRuntimeEntry(runtimeId, {
|
|
2575
2570
|
agentId,
|
|
2576
2571
|
agentName: profileAgentName,
|
|
2577
2572
|
cwd: workingDir,
|
|
2578
2573
|
baseCwd: workingDir,
|
|
2579
2574
|
});
|
|
2580
|
-
|
|
2575
|
+
};
|
|
2576
|
+
const refreshRuntimeDetails = async (signal) => {
|
|
2577
|
+
await refreshKnownConversationIds().catch((error) => {
|
|
2578
|
+
console.error('[canon-codex] Failed to refresh known conversations:', error);
|
|
2579
|
+
});
|
|
2580
|
+
if (signal.aborted)
|
|
2581
2581
|
return;
|
|
2582
|
-
await
|
|
2583
|
-
|
|
2582
|
+
await publishHostSessionSnapshots({
|
|
2583
|
+
conversationIds: Array.from(knownConversationIds),
|
|
2584
|
+
agentId,
|
|
2585
|
+
rtdb,
|
|
2586
|
+
clientType: 'codex',
|
|
2587
|
+
}).catch((error) => {
|
|
2588
|
+
console.error('[canon-codex] Failed to publish session snapshots:', error);
|
|
2584
2589
|
});
|
|
2585
|
-
if (
|
|
2590
|
+
if (signal.aborted)
|
|
2586
2591
|
return;
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
?
|
|
2604
|
-
:
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
descriptor,
|
|
2611
|
-
surfaceMode: 'host',
|
|
2612
|
-
// The exec --json transport cannot block on approvals — without a
|
|
2613
|
-
// strip-level warning a user can believe they have an approval
|
|
2614
|
-
// gate they do not have.
|
|
2615
|
-
...(useAppServer
|
|
2616
|
-
? {}
|
|
2617
|
-
: { warning: "Approvals can't block on this Codex CLI — update Codex to enable the app-server transport and approval gates." }),
|
|
2618
|
-
statusItems: [
|
|
2619
|
-
{
|
|
2620
|
-
id: 'transport',
|
|
2621
|
-
label: 'Transport',
|
|
2622
|
-
value: useAppServer ? 'app-server' : 'exec --json',
|
|
2623
|
-
},
|
|
2624
|
-
{
|
|
2625
|
-
id: 'streaming',
|
|
2626
|
-
label: 'Live output',
|
|
2627
|
-
value: useAppServer
|
|
2628
|
-
? 'Plans, questions, approvals, tools, and message deltas'
|
|
2629
|
-
: 'Thinking, tools, and completed-message previews',
|
|
2630
|
-
},
|
|
2631
|
-
{
|
|
2632
|
-
id: 'codex-cli',
|
|
2633
|
-
label: 'Codex CLI',
|
|
2634
|
-
value: codexCliStatus.version ?? (codexCliStatus.raw ?? 'Version unknown'),
|
|
2635
|
-
tone: codexCliStatus.version ? 'default' : 'warning',
|
|
2636
|
-
},
|
|
2637
|
-
{
|
|
2638
|
-
id: 'nativeActions',
|
|
2639
|
-
label: 'Native actions',
|
|
2640
|
-
value: useAppServer ? 'Enabled' : 'Limited until app-server transport',
|
|
2641
|
-
...(useAppServer ? {} : { tone: 'warning' }),
|
|
2642
|
-
},
|
|
2643
|
-
{
|
|
2644
|
-
id: 'mediaOut',
|
|
2645
|
-
label: 'Media out',
|
|
2646
|
-
value: 'Generated media artifacts',
|
|
2647
|
-
},
|
|
2648
|
-
],
|
|
2649
|
-
execution: {
|
|
2650
|
-
resolvedWorkspaceLabel: workspace?.label ?? workspaceId ?? null,
|
|
2651
|
-
resolvedCwd: session?.cwd ?? workspace?.cwd ?? workingDir,
|
|
2652
|
-
workspaceRootId: workspace?.workspaceRootId ?? null,
|
|
2653
|
-
workspaceRelativePath: workspace?.workspaceRelativePath ?? null,
|
|
2654
|
-
executionMode: session?.environment.mode ?? null,
|
|
2655
|
-
executionBranch: session?.environment.branch ?? null,
|
|
2656
|
-
worktreePath: session?.environment.worktreePath ?? null,
|
|
2657
|
-
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
2592
|
+
const results = await Promise.allSettled(Array.from(knownConversationIds).map(async (conversationId) => {
|
|
2593
|
+
const session = sessions.get(conversationId);
|
|
2594
|
+
const workspaceId = session
|
|
2595
|
+
? resolveWorkspaceIdForBaseCwd(session.environment.baseCwd)
|
|
2596
|
+
: runtimeDescriptor.defaultWorkspaceId;
|
|
2597
|
+
const workspace = workspaceOptions.find((option) => option.id === workspaceId) ?? null;
|
|
2598
|
+
const descriptor = runtimeDescriptor.runtimeDescriptor;
|
|
2599
|
+
if (!descriptor)
|
|
2600
|
+
return;
|
|
2601
|
+
const payload = {
|
|
2602
|
+
descriptor,
|
|
2603
|
+
surfaceMode: 'host',
|
|
2604
|
+
// The exec --json transport cannot block on approvals — without a
|
|
2605
|
+
// strip-level warning a user can believe they have an approval
|
|
2606
|
+
// gate they do not have.
|
|
2607
|
+
...(useAppServer
|
|
2608
|
+
? {}
|
|
2609
|
+
: { warning: "Approvals can't block on this Codex CLI — update Codex to enable the app-server transport and approval gates." }),
|
|
2610
|
+
statusItems: [
|
|
2611
|
+
{
|
|
2612
|
+
id: 'transport',
|
|
2613
|
+
label: 'Transport',
|
|
2614
|
+
value: useAppServer ? 'app-server' : 'exec --json',
|
|
2658
2615
|
},
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2616
|
+
{
|
|
2617
|
+
id: 'streaming',
|
|
2618
|
+
label: 'Live output',
|
|
2619
|
+
value: useAppServer
|
|
2620
|
+
? 'Plans, questions, approvals, tools, and message deltas'
|
|
2621
|
+
: 'Thinking, tools, and completed-message previews',
|
|
2622
|
+
},
|
|
2623
|
+
{
|
|
2624
|
+
id: 'codex-cli',
|
|
2625
|
+
label: 'Codex CLI',
|
|
2626
|
+
value: codexCliStatus.version ?? (codexCliStatus.raw ?? 'Version unknown'),
|
|
2627
|
+
tone: codexCliStatus.version ? 'default' : 'warning',
|
|
2628
|
+
},
|
|
2629
|
+
{
|
|
2630
|
+
id: 'nativeActions',
|
|
2631
|
+
label: 'Native actions',
|
|
2632
|
+
value: useAppServer ? 'Enabled' : 'Limited until app-server transport',
|
|
2633
|
+
...(useAppServer ? {} : { tone: 'warning' }),
|
|
2634
|
+
},
|
|
2635
|
+
{
|
|
2636
|
+
id: 'mediaOut',
|
|
2637
|
+
label: 'Media out',
|
|
2638
|
+
value: 'Generated media artifacts',
|
|
2639
|
+
},
|
|
2640
|
+
],
|
|
2641
|
+
execution: {
|
|
2642
|
+
resolvedWorkspaceLabel: workspace?.label ?? workspaceId ?? null,
|
|
2643
|
+
resolvedCwd: session?.cwd ?? workspace?.cwd ?? workingDir,
|
|
2644
|
+
workspaceRootId: workspace?.workspaceRootId ?? null,
|
|
2645
|
+
workspaceRelativePath: workspace?.workspaceRelativePath ?? null,
|
|
2646
|
+
executionMode: session?.environment.mode ?? null,
|
|
2647
|
+
executionBranch: session?.environment.branch ?? null,
|
|
2648
|
+
worktreePath: session?.environment.worktreePath ?? null,
|
|
2649
|
+
fallbackReason: resolveExecutionFallbackReason(session?.environment),
|
|
2650
|
+
},
|
|
2651
|
+
notes: [
|
|
2652
|
+
useAppServer
|
|
2653
|
+
? 'This Codex host uses the app-server transport, so Canon can route native plan mode, runtime questions, approvals, and live turn updates.'
|
|
2654
|
+
: 'This Codex host uses the current exec --json transport, so Canon can show thinking, tool activity, and completed assistant-message previews, but not native plan questions or structured approvals.',
|
|
2655
|
+
],
|
|
2656
|
+
};
|
|
2657
|
+
await runtimeState.writeRuntimeInfo(conversationId, payload);
|
|
2658
|
+
}));
|
|
2659
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
2660
|
+
if (failure?.status === 'rejected')
|
|
2661
|
+
throw failure.reason;
|
|
2673
2662
|
};
|
|
2663
|
+
const runtimeHeartbeat = createRuntimeHeartbeat({
|
|
2664
|
+
publisher: runtimeState,
|
|
2665
|
+
getRuntime: () => runtimeDescriptor,
|
|
2666
|
+
refreshDetails: refreshRuntimeDetails,
|
|
2667
|
+
onError: (error, operation) => {
|
|
2668
|
+
console.error(`[canon-codex] Runtime ${operation} failed:`, error);
|
|
2669
|
+
},
|
|
2670
|
+
});
|
|
2674
2671
|
let startupRecoveryComplete = false;
|
|
2675
2672
|
async function recoverInboundMessageGaps() {
|
|
2676
2673
|
// A reconnect can reveal conversations created while this host was
|
|
@@ -2796,14 +2793,13 @@ export async function main() {
|
|
|
2796
2793
|
codexDynamicTools = filterCodexCommunicationTools(baseCodexDynamicTools, outboundPolicy);
|
|
2797
2794
|
},
|
|
2798
2795
|
onConnected: () => {
|
|
2799
|
-
|
|
2800
|
-
|
|
2796
|
+
publishLocalHeartbeat();
|
|
2797
|
+
runtimeHeartbeat.connect();
|
|
2801
2798
|
observeReconnectRecovery('reconnect', reconnectRecovery.onConnected());
|
|
2802
2799
|
console.error('[canon-codex] SSE connected');
|
|
2803
2800
|
},
|
|
2804
2801
|
onDisconnected: () => {
|
|
2805
|
-
|
|
2806
|
-
runtimeState.clearAgentRuntime().catch(() => { });
|
|
2802
|
+
void runtimeHeartbeat.disconnect();
|
|
2807
2803
|
console.error('[canon-codex] SSE disconnected');
|
|
2808
2804
|
},
|
|
2809
2805
|
onReplayExpired: () => observeReconnectRecovery('replay-expired', reconnectRecovery.onReplayExpired()),
|
|
@@ -2840,7 +2836,7 @@ export async function main() {
|
|
|
2840
2836
|
writeTurn(session);
|
|
2841
2837
|
}
|
|
2842
2838
|
}
|
|
2843
|
-
|
|
2839
|
+
publishLocalHeartbeat();
|
|
2844
2840
|
}, HEARTBEAT_MS);
|
|
2845
2841
|
const idleCheck = setInterval(() => {
|
|
2846
2842
|
const now = Date.now();
|
|
@@ -2864,7 +2860,7 @@ export async function main() {
|
|
|
2864
2860
|
}
|
|
2865
2861
|
runtimeRequests.dispose();
|
|
2866
2862
|
stream.stop();
|
|
2867
|
-
await
|
|
2863
|
+
await runtimeHeartbeat.dispose();
|
|
2868
2864
|
for (const session of [...sessions.values()]) {
|
|
2869
2865
|
await session.adapter.interrupt().catch(() => { });
|
|
2870
2866
|
closeSession(session.conversationId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.2",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"prepack": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@canonmsg/agent-sdk": "^10.
|
|
32
|
+
"@canonmsg/agent-sdk": "^10.2.0",
|
|
33
33
|
"@canonmsg/agent-tools": "^0.9.0",
|
|
34
34
|
"@canonmsg/coding-agent-host": "^0.7.0",
|
|
35
|
-
"@canonmsg/core": "^12.
|
|
35
|
+
"@canonmsg/core": "^12.2.0",
|
|
36
36
|
"@canonmsg/rich-cards": "^0.10.4"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|