@canonmsg/codex-plugin 0.9.8 → 0.10.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/dist/host.js CHANGED
@@ -4,7 +4,7 @@ import { randomUUID } from 'node:crypto';
4
4
  import { dirname } from 'node:path';
5
5
  import { parseArgs } from 'node:util';
6
6
  import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
7
- 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
+ import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, 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';
8
8
  import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
9
9
  import { CodexConversationAdapter, } from './adapter.js';
10
10
  import { clearStoredThreadId, buildCodexThreadPolicyFingerprint, loadStoredThreadId, saveStoredThreadId, } from './session-store.js';
@@ -65,26 +65,9 @@ function buildCodexRuntimeDescriptor(input) {
65
65
  defaultPermissionMode: input.defaultPermissionMode,
66
66
  streamingTextMode: 'snapshot',
67
67
  actions: [
68
- {
69
- id: 'stop',
70
- label: 'Stop',
71
- description: 'Interrupt the current Codex exec turn.',
72
- aliases: ['stop'],
73
- category: 'turn',
74
- placements: ['composer_slash', 'command_palette'],
75
- availability: ['busy'],
76
- dispatch: { kind: 'signal', signal: 'interrupt' },
77
- },
78
- {
79
- id: 'stop-and-clear-queue',
80
- label: 'Stop & clear queue',
81
- description: 'Interrupt the current Codex exec turn and drop queued Canon messages.',
82
- aliases: ['stop-clear', 'clear-queue'],
83
- category: 'turn',
84
- placements: ['composer_slash', 'command_palette', 'session_strip'],
85
- availability: ['busy_with_queue'],
86
- dispatch: { kind: 'signal', signal: 'stop_and_drop' },
87
- },
68
+ RUNTIME_STOP_ACTION,
69
+ RUNTIME_STOP_AND_DROP_ACTION,
70
+ RUNTIME_NEW_SESSION_ACTION,
88
71
  ],
89
72
  });
90
73
  if (input.models.length > 0) {
@@ -452,6 +435,32 @@ export async function main() {
452
435
  client.setTyping(conversationId, false).catch(() => { });
453
436
  sessions.delete(conversationId);
454
437
  }
438
+ async function resetRuntimeSession(session) {
439
+ const conversationId = session.conversationId;
440
+ session.resetRequested = true;
441
+ const droppedPrompts = session.queue.splice(0);
442
+ await markQueuedPromptsRejected(conversationId, droppedPrompts);
443
+ clearStoredThreadId(runtimeId, agentId, conversationId, session.environment.baseCwd, session.environment.mode);
444
+ session.adapter.clearThreadId();
445
+ session.activeSelfContextId = null;
446
+ session.state.lastError = undefined;
447
+ if (session.running) {
448
+ await session.adapter.interrupt();
449
+ session.turnState = 'interrupted';
450
+ }
451
+ else {
452
+ session.turnState = 'idle';
453
+ session.currentTurnId = null;
454
+ session.currentTurnOpenedAt = null;
455
+ session.lastAcceptedIntent = null;
456
+ session.resetRequested = false;
457
+ }
458
+ stopVisibleWorkSignal(session);
459
+ clearStreaming(conversationId);
460
+ client.setTyping(conversationId, false).catch(() => { });
461
+ writeState(session);
462
+ writeTurn(session);
463
+ }
455
464
  function evictOldestIdle() {
456
465
  let oldest = null;
457
466
  for (const session of sessions.values()) {
@@ -531,6 +540,7 @@ export async function main() {
531
540
  currentTurnOpenedAt: null,
532
541
  activeSelfContextId: null,
533
542
  lastAcceptedIntent: null,
543
+ resetRequested: false,
534
544
  lastActivity: Date.now(),
535
545
  typingKeepaliveTimer: null,
536
546
  closed: false,
@@ -680,6 +690,9 @@ export async function main() {
680
690
  const handleCodexEvent = (event) => {
681
691
  session.lastActivity = Date.now();
682
692
  if (event.type === 'thread.started') {
693
+ if (session.resetRequested) {
694
+ return;
695
+ }
683
696
  saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, event.threadId, session.environment.mode, session.policyFingerprint);
684
697
  console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Thread ${event.threadId}`);
685
698
  return;
@@ -727,7 +740,7 @@ export async function main() {
727
740
  clearStoredThread();
728
741
  result = await runTurnOnce();
729
742
  }
730
- if (result.threadId) {
743
+ if (result.threadId && !session.resetRequested) {
731
744
  saveStoredThreadId(runtimeId, agentId, session.conversationId, session.environment.baseCwd, result.threadId, session.environment.mode, session.policyFingerprint);
732
745
  }
733
746
  if (!result.interrupted && result.finalMessage) {
@@ -811,6 +824,7 @@ export async function main() {
811
824
  session.currentTurnId = null;
812
825
  session.currentTurnOpenedAt = null;
813
826
  session.lastAcceptedIntent = null;
827
+ session.resetRequested = false;
814
828
  session.lastActivity = Date.now();
815
829
  writeState(session);
816
830
  writeTurn(session);
@@ -1153,7 +1167,7 @@ export async function main() {
1153
1167
  continue;
1154
1168
  const signal = raw;
1155
1169
  const timestamp = signal.updatedAt ?? 0;
1156
- if ((signal.type !== 'interrupt' && signal.type !== 'stop_and_drop')
1170
+ if ((signal.type !== 'interrupt' && signal.type !== 'stop_and_drop' && signal.type !== 'new_session')
1157
1171
  || timestamp <= (lastSeenSignal.get(conversationId) ?? 0)) {
1158
1172
  continue;
1159
1173
  }
@@ -1161,6 +1175,12 @@ export async function main() {
1161
1175
  const session = sessions.get(conversationId);
1162
1176
  if (!session || session.closed)
1163
1177
  continue;
1178
+ if (signal.type === 'new_session') {
1179
+ console.error(`[canon-codex] [${conversationId.slice(0, 8)}] new_session signal`);
1180
+ await resetRuntimeSession(session);
1181
+ await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
1182
+ continue;
1183
+ }
1164
1184
  if (!session.running && (signal.type !== 'stop_and_drop' || session.queue.length === 0)) {
1165
1185
  await rtdbWrite(`/control/${conversationId}/${agentId}/signal`, null).catch(() => { });
1166
1186
  continue;
@@ -84,11 +84,26 @@ export function saveStoredThreadId(runtimeId, agentId, conversationId, baseCwd,
84
84
  }
85
85
  export function clearStoredThreadId(runtimeId, agentId, conversationId, baseCwd, executionMode) {
86
86
  if (runtimeId) {
87
+ const existing = baseCwd
88
+ ? loadRuntimeSessionState(runtimeId, {
89
+ conversationId,
90
+ baseCwd,
91
+ executionMode,
92
+ })
93
+ : null;
87
94
  clearRuntimeSessionState(runtimeId, {
88
95
  conversationId,
89
96
  baseCwd,
90
97
  executionMode,
91
98
  });
99
+ if (existing?.lastInboundMessageId && baseCwd) {
100
+ saveRuntimeSessionState(runtimeId, {
101
+ conversationId,
102
+ baseCwd,
103
+ ...(executionMode ? { executionMode } : {}),
104
+ lastInboundMessageId: existing.lastInboundMessageId,
105
+ });
106
+ }
92
107
  return;
93
108
  }
94
109
  const store = loadStore();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.9.8",
3
+ "version": "0.10.0",
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": "^1.1.4",
33
- "@canonmsg/core": "^0.15.4"
32
+ "@canonmsg/agent-sdk": "^1.2.0",
33
+ "@canonmsg/core": "^0.16.0"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.0.0"