@canonmsg/codex-plugin 0.22.1 → 0.22.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.
Files changed (2) hide show
  1. package/dist/host.js +52 -7
  2. package/package.json +2 -2
package/dist/host.js CHANGED
@@ -76,6 +76,7 @@ const MAX_SESSIONS = 12;
76
76
  const IDLE_TIMEOUT_MS = 30 * 60 * 1000;
77
77
  const HEARTBEAT_MS = 30_000;
78
78
  const IDLE_CHECK_MS = 60_000;
79
+ const PLAN_REVIEW_TIMEOUT_MS = 10 * 60_000;
79
80
  const CODEX_RUNTIME_CAPABILITIES = {
80
81
  ...DEFAULT_RUNTIME_CAPABILITIES,
81
82
  supportsInterrupt: true,
@@ -1123,6 +1124,19 @@ export async function main() {
1123
1124
  writeTurn(session);
1124
1125
  void runNextTurn(session);
1125
1126
  }
1127
+ function enqueueCodexPlanReviewResult(session, result, responseUserId) {
1128
+ if (result.status === 'cancelled' || result.status === 'timeout') {
1129
+ console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Plan review ${result.status}`);
1130
+ return;
1131
+ }
1132
+ const feedback = result.feedback?.trim();
1133
+ const prompt = result.status === 'approve'
1134
+ ? 'The plan was approved. Implement the approved plan now.'
1135
+ : result.status === 'reject'
1136
+ ? `The plan was declined — keep planning and wait for guidance before implementing.${feedback ? `\n\nNotes:\n${feedback}` : ''}`
1137
+ : `Please revise the plan.${feedback ? `\n\nRevision feedback:\n${feedback}` : ''}`;
1138
+ enqueuePrompt(session, prompt, 'queue', false, result.receiptId ?? null, false, [], [], result.status !== 'approve', 'disabled', false, responseUserId);
1139
+ }
1126
1140
  function resolveArtifactRoutingMode(participantContext) {
1127
1141
  return participantContext.conversationType === 'direct' && participantContext.isOwner
1128
1142
  ? 'workspace-generated'
@@ -1331,6 +1345,14 @@ export async function main() {
1331
1345
  if (isRecord(input.message.metadata)
1332
1346
  && input.message.metadata.type === 'plan_approval_reply'
1333
1347
  && typeof input.message.metadata.decision === 'string') {
1348
+ const planId = readString(input.message.metadata, 'planId');
1349
+ if (planId && runtimeRequests.handleMessage(input.conversationId, {
1350
+ senderId: input.message.senderId,
1351
+ metadata: input.message.metadata,
1352
+ })) {
1353
+ persistInboundRecoveryCursorWhenIdle(input.conversationId, input.message.id);
1354
+ return;
1355
+ }
1334
1356
  const session = await getOrCreateSession(input.conversationId);
1335
1357
  const feedback = readString(input.message.metadata, 'feedback');
1336
1358
  const decision = input.message.metadata.decision;
@@ -1666,20 +1688,43 @@ export async function main() {
1666
1688
  requestingUserId: nextTurn.requestingUserId,
1667
1689
  ownerId,
1668
1690
  });
1669
- // Route plan-CREATE through the unified spine (`/runtime-plan/request`):
1670
- // seeds the server-owned pending node/attention/state and authors the
1671
- // `plan_approval` card. Resolution is UNCHANGED — Codex still re-queues a
1672
- // fresh turn off the server-authored `plan_approval_reply`.
1673
- await client.createRuntimePlanRequest({
1674
- conversationId: session.conversationId,
1675
- planId: session.currentTurnId ?? randomUUID(),
1691
+ const planId = session.currentTurnId ?? randomUUID();
1692
+ let planCreated = false;
1693
+ let resolvePlanCreated;
1694
+ let rejectPlanCreated;
1695
+ const planCreatedPromise = new Promise((resolve, reject) => {
1696
+ resolvePlanCreated = resolve;
1697
+ rejectPlanCreated = reject;
1698
+ });
1699
+ const planReview = runtimeRequests.request('plan', session.conversationId, {
1676
1700
  title: 'Codex Plan',
1677
1701
  body: result.finalMessage,
1678
1702
  ...(responseRouting.responseUserId
1679
1703
  ? { responseUserId: responseRouting.responseUserId }
1680
1704
  : {}),
1681
1705
  ...(session.currentTurnId ? { turnId: session.currentTurnId } : {}),
1706
+ }, {
1707
+ requestId: planId,
1708
+ expiresAt: Date.now() + PLAN_REVIEW_TIMEOUT_MS,
1709
+ responderPolicy: 'infer',
1710
+ onCreated: () => {
1711
+ planCreated = true;
1712
+ session.turnState = 'waiting_input';
1713
+ writeTurn(session);
1714
+ resolvePlanCreated();
1715
+ },
1716
+ });
1717
+ void planReview.then((planResult) => {
1718
+ resolvePlanCreated();
1719
+ enqueueCodexPlanReviewResult(session, planResult, responseRouting.responseUserId ?? null);
1720
+ }).catch((error) => {
1721
+ if (!planCreated) {
1722
+ rejectPlanCreated(error);
1723
+ return;
1724
+ }
1725
+ console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Plan review failed:`, error instanceof Error ? error.message : error);
1682
1726
  });
1727
+ await planCreatedPromise;
1683
1728
  await handoffFinalMessage(session.conversationId);
1684
1729
  console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Sent plan approval card`);
1685
1730
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "@canonmsg/agent-sdk": "^5.1.1",
33
33
  "@canonmsg/coding-agent-host": "^0.2.2",
34
- "@canonmsg/core": "^4.2.2"
34
+ "@canonmsg/core": "^4.2.3"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=18.0.0"