@canonmsg/codex-plugin 0.9.2 → 0.9.4
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 +37 -28
- package/package.json +3 -3
package/dist/host.js
CHANGED
|
@@ -3,7 +3,7 @@ import { setDefaultResultOrder } from 'node:dns';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
5
|
import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
|
|
6
|
-
import { buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, 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, shouldTriggerAgentTurn, saveRuntimeSessionState, publishHostAgentRuntime, publishHostSessionSnapshots, renderCanonHostInboundContent, resolveHostWorkspaceCwd, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
6
|
+
import { buildCanonHostPrompt, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, 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';
|
|
7
7
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
8
8
|
import { CodexConversationAdapter, } from './adapter.js';
|
|
9
9
|
import { clearStoredThreadId, loadStoredThreadId, saveStoredThreadId, } from './session-store.js';
|
|
@@ -63,15 +63,6 @@ function normalizeRuntimeTurnState(value) {
|
|
|
63
63
|
if (normalizedTurn) {
|
|
64
64
|
return { state: normalizedTurn.state };
|
|
65
65
|
}
|
|
66
|
-
if (!value || typeof value !== 'object')
|
|
67
|
-
return null;
|
|
68
|
-
const state = value.state;
|
|
69
|
-
if (state === 'running') {
|
|
70
|
-
return { state: 'streaming' };
|
|
71
|
-
}
|
|
72
|
-
if (state === 'requires_action') {
|
|
73
|
-
return { state: 'waiting_input' };
|
|
74
|
-
}
|
|
75
66
|
return null;
|
|
76
67
|
}
|
|
77
68
|
async function publishAgentRuntime(agentId, runtime) {
|
|
@@ -247,11 +238,7 @@ export async function main() {
|
|
|
247
238
|
}
|
|
248
239
|
async function loadSenderRuntimeState(conversationId, senderId) {
|
|
249
240
|
try {
|
|
250
|
-
|
|
251
|
-
rtdbRead(`/turn-state/${conversationId}/${senderId}`),
|
|
252
|
-
rtdbRead(`/session-state/${conversationId}/${senderId}`),
|
|
253
|
-
]);
|
|
254
|
-
return normalizeRuntimeTurnState(turnState) ?? normalizeRuntimeTurnState(sessionState);
|
|
241
|
+
return normalizeRuntimeTurnState(await rtdbRead(`/turn-state/${conversationId}/${senderId}`));
|
|
255
242
|
}
|
|
256
243
|
catch {
|
|
257
244
|
return null;
|
|
@@ -286,7 +273,6 @@ export async function main() {
|
|
|
286
273
|
: {}),
|
|
287
274
|
hostMode: true,
|
|
288
275
|
clientType: 'codex',
|
|
289
|
-
state: session.state.state,
|
|
290
276
|
isActive: true,
|
|
291
277
|
}).catch(() => { });
|
|
292
278
|
}
|
|
@@ -309,6 +295,13 @@ export async function main() {
|
|
|
309
295
|
return;
|
|
310
296
|
await client.updateMessageDisposition(conversationId, sourceMessageId, 'accepted_now').catch(() => { });
|
|
311
297
|
}
|
|
298
|
+
async function markQueuedPromptsRejected(conversationId, prompts) {
|
|
299
|
+
await Promise.all(prompts.map((prompt) => {
|
|
300
|
+
if (!prompt.markAccepted || !prompt.sourceMessageId)
|
|
301
|
+
return Promise.resolve();
|
|
302
|
+
return client.updateMessageDisposition(conversationId, prompt.sourceMessageId, 'rejected').catch(() => { });
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
312
305
|
function clearStreaming(conversationId) {
|
|
313
306
|
runtimeState.clearStreaming(conversationId).catch(() => { });
|
|
314
307
|
}
|
|
@@ -435,6 +428,7 @@ export async function main() {
|
|
|
435
428
|
closed: false,
|
|
436
429
|
};
|
|
437
430
|
sessions.set(conversationId, session);
|
|
431
|
+
await baselineControlSignal(conversationId);
|
|
438
432
|
console.error(`[canon-codex] [${conversationId.slice(0, 8)}] Environment → ${environment.mode} (${sessionCwd})`);
|
|
439
433
|
writeState(session);
|
|
440
434
|
writeTurn(session);
|
|
@@ -677,6 +671,8 @@ export async function main() {
|
|
|
677
671
|
}
|
|
678
672
|
}
|
|
679
673
|
let controlStopped = false;
|
|
674
|
+
const lastSeenControl = new Map();
|
|
675
|
+
const lastSeenSignal = new Map();
|
|
680
676
|
let streamConnected = false;
|
|
681
677
|
const hostAvailableExecutionModes = [
|
|
682
678
|
...EXECUTION_ENVIRONMENT_MODES,
|
|
@@ -700,6 +696,17 @@ export async function main() {
|
|
|
700
696
|
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
701
697
|
}),
|
|
702
698
|
};
|
|
699
|
+
async function baselineControlSignal(conversationId) {
|
|
700
|
+
if (lastSeenSignal.has(conversationId))
|
|
701
|
+
return;
|
|
702
|
+
const raw = await rtdbRead(`/control/${conversationId}/${agentId}/signal`).catch(() => null);
|
|
703
|
+
if (!raw || typeof raw !== 'object')
|
|
704
|
+
return;
|
|
705
|
+
const timestamp = Number(raw.updatedAt ?? 0);
|
|
706
|
+
if (timestamp > 0) {
|
|
707
|
+
lastSeenSignal.set(conversationId, timestamp);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
703
710
|
const publishRuntimeHeartbeat = async () => {
|
|
704
711
|
heartbeatLocalRuntimeEntry(runtimeId, {
|
|
705
712
|
agentId,
|
|
@@ -744,15 +751,11 @@ export async function main() {
|
|
|
744
751
|
? resolveWorkspaceIdForBaseCwd(session.environment.baseCwd)
|
|
745
752
|
: runtimeDescriptor.defaultWorkspaceId;
|
|
746
753
|
const workspace = workspaceOptions.find((option) => option.id === workspaceId) ?? null;
|
|
754
|
+
const descriptor = runtimeDescriptor.runtimeDescriptor;
|
|
755
|
+
if (!descriptor)
|
|
756
|
+
return;
|
|
747
757
|
const payload = {
|
|
748
|
-
descriptor
|
|
749
|
-
models: runtimeDescriptor.availableModels ?? [],
|
|
750
|
-
workspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
751
|
-
workspaceRoots: workspaceRootMetadata,
|
|
752
|
-
executionModes: hostAvailableExecutionModes,
|
|
753
|
-
permissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
754
|
-
defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
|
|
755
|
-
}),
|
|
758
|
+
descriptor,
|
|
756
759
|
surfaceMode: 'host',
|
|
757
760
|
statusItems: [
|
|
758
761
|
{
|
|
@@ -932,8 +935,6 @@ export async function main() {
|
|
|
932
935
|
await stream.start().catch((error) => {
|
|
933
936
|
console.error('[canon-codex] SSE start error:', error instanceof Error ? error.message : error);
|
|
934
937
|
});
|
|
935
|
-
const lastSeenControl = new Map();
|
|
936
|
-
const lastSeenSignal = new Map();
|
|
937
938
|
const pollControl = async () => {
|
|
938
939
|
while (!controlStopped) {
|
|
939
940
|
for (const conversationId of [...sessions.keys()]) {
|
|
@@ -974,15 +975,23 @@ export async function main() {
|
|
|
974
975
|
const session = sessions.get(conversationId);
|
|
975
976
|
if (!session || session.closed)
|
|
976
977
|
continue;
|
|
978
|
+
if (!session.running && (signal.type !== 'stop_and_drop' || session.queue.length === 0)) {
|
|
979
|
+
await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
977
982
|
console.error(`[canon-codex] [${conversationId.slice(0, 8)}] ${signal.type} signal`);
|
|
978
|
-
|
|
983
|
+
if (session.running) {
|
|
984
|
+
await session.adapter.interrupt();
|
|
985
|
+
}
|
|
979
986
|
session.turnState = 'interrupted';
|
|
980
987
|
if (signal.type === 'stop_and_drop') {
|
|
981
|
-
session.queue.
|
|
988
|
+
const droppedPrompts = session.queue.splice(0);
|
|
989
|
+
await markQueuedPromptsRejected(conversationId, droppedPrompts);
|
|
982
990
|
}
|
|
983
991
|
writeTurn(session);
|
|
984
992
|
clearStreaming(conversationId);
|
|
985
993
|
client.setTyping(conversationId, false).catch(() => { });
|
|
994
|
+
await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
|
|
986
995
|
}
|
|
987
996
|
catch {
|
|
988
997
|
// Ignore transient RTDB failures.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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": "^
|
|
33
|
-
"@canonmsg/core": "^0.
|
|
32
|
+
"@canonmsg/agent-sdk": "^1.1.0",
|
|
33
|
+
"@canonmsg/core": "^0.15.0"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|