@canonmsg/codex-plugin 0.9.3 → 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.
Files changed (2) hide show
  1. package/dist/host.js +32 -5
  2. package/package.json +2 -2
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';
@@ -295,6 +295,13 @@ export async function main() {
295
295
  return;
296
296
  await client.updateMessageDisposition(conversationId, sourceMessageId, 'accepted_now').catch(() => { });
297
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
+ }
298
305
  function clearStreaming(conversationId) {
299
306
  runtimeState.clearStreaming(conversationId).catch(() => { });
300
307
  }
@@ -421,6 +428,7 @@ export async function main() {
421
428
  closed: false,
422
429
  };
423
430
  sessions.set(conversationId, session);
431
+ await baselineControlSignal(conversationId);
424
432
  console.error(`[canon-codex] [${conversationId.slice(0, 8)}] Environment → ${environment.mode} (${sessionCwd})`);
425
433
  writeState(session);
426
434
  writeTurn(session);
@@ -663,6 +671,8 @@ export async function main() {
663
671
  }
664
672
  }
665
673
  let controlStopped = false;
674
+ const lastSeenControl = new Map();
675
+ const lastSeenSignal = new Map();
666
676
  let streamConnected = false;
667
677
  const hostAvailableExecutionModes = [
668
678
  ...EXECUTION_ENVIRONMENT_MODES,
@@ -686,6 +696,17 @@ export async function main() {
686
696
  defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode,
687
697
  }),
688
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
+ }
689
710
  const publishRuntimeHeartbeat = async () => {
690
711
  heartbeatLocalRuntimeEntry(runtimeId, {
691
712
  agentId,
@@ -914,8 +935,6 @@ export async function main() {
914
935
  await stream.start().catch((error) => {
915
936
  console.error('[canon-codex] SSE start error:', error instanceof Error ? error.message : error);
916
937
  });
917
- const lastSeenControl = new Map();
918
- const lastSeenSignal = new Map();
919
938
  const pollControl = async () => {
920
939
  while (!controlStopped) {
921
940
  for (const conversationId of [...sessions.keys()]) {
@@ -956,15 +975,23 @@ export async function main() {
956
975
  const session = sessions.get(conversationId);
957
976
  if (!session || session.closed)
958
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
+ }
959
982
  console.error(`[canon-codex] [${conversationId.slice(0, 8)}] ${signal.type} signal`);
960
- await session.adapter.interrupt();
983
+ if (session.running) {
984
+ await session.adapter.interrupt();
985
+ }
961
986
  session.turnState = 'interrupted';
962
987
  if (signal.type === 'stop_and_drop') {
963
- session.queue.length = 0;
988
+ const droppedPrompts = session.queue.splice(0);
989
+ await markQueuedPromptsRejected(conversationId, droppedPrompts);
964
990
  }
965
991
  writeTurn(session);
966
992
  clearStreaming(conversationId);
967
993
  client.setTyping(conversationId, false).catch(() => { });
994
+ await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
968
995
  }
969
996
  catch {
970
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",
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,7 +29,7 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@canonmsg/agent-sdk": "^1.0.0",
32
+ "@canonmsg/agent-sdk": "^1.1.0",
33
33
  "@canonmsg/core": "^0.15.0"
34
34
  },
35
35
  "engines": {