@canonmsg/codex-plugin 0.19.1 → 0.19.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/adapter.d.ts CHANGED
@@ -8,6 +8,7 @@ export type CodexEvent = {
8
8
  } | {
9
9
  type: 'message';
10
10
  text: string;
11
+ delta?: string;
11
12
  itemId?: string;
12
13
  } | {
13
14
  type: 'plan.updated';
@@ -39,6 +39,7 @@ export declare class CodexAppServerAdapter {
39
39
  private skillsCache;
40
40
  private messageTextByItem;
41
41
  private planText;
42
+ private traceTurnEpochMs;
42
43
  constructor(opts: {
43
44
  cwd: string;
44
45
  threadId?: string | null;
@@ -96,4 +97,6 @@ export declare class CodexAppServerAdapter {
96
97
  private clearActiveTurn;
97
98
  private sendRequest;
98
99
  private write;
100
+ private trace;
101
+ private traceLine;
99
102
  }
@@ -31,6 +31,7 @@ export class CodexAppServerAdapter {
31
31
  skillsCache = null;
32
32
  messageTextByItem = new Map();
33
33
  planText = '';
34
+ traceTurnEpochMs = null;
34
35
  constructor(opts) {
35
36
  this.cwd = opts.cwd;
36
37
  this.threadId = opts.threadId ?? null;
@@ -117,6 +118,8 @@ export class CodexAppServerAdapter {
117
118
  this.interrupted = false;
118
119
  this.messageTextByItem.clear();
119
120
  this.planText = '';
121
+ this.traceTurnEpochMs = Date.now();
122
+ this.trace('turn/run begin');
120
123
  try {
121
124
  if (this.threadId && this.loadedThreadId !== this.threadId) {
122
125
  const resumed = await this.sendRequest('thread/resume', {
@@ -158,9 +161,11 @@ export class CodexAppServerAdapter {
158
161
  this.currentTurnReject = reject;
159
162
  });
160
163
  turnPromise.catch(() => { });
164
+ const turnInput = await this.buildTurnInput(prompt, imagePaths);
165
+ this.trace('turn/start sent');
161
166
  const turnStarted = await this.sendRequest('turn/start', {
162
167
  threadId: this.threadId,
163
- input: await this.buildTurnInput(prompt, imagePaths),
168
+ input: turnInput,
164
169
  ...(this.model ? { model: this.model } : {}),
165
170
  ...this.sandboxPolicyPayload(_extraAddDirs),
166
171
  collaborationMode: {
@@ -172,6 +177,7 @@ export class CodexAppServerAdapter {
172
177
  },
173
178
  },
174
179
  });
180
+ this.trace('turn/start ack');
175
181
  const turn = turnStarted.turn;
176
182
  if (this.currentTurnResolve) {
177
183
  this.currentTurnId = readString(turn, 'id') ?? null;
@@ -322,6 +328,7 @@ export class CodexAppServerAdapter {
322
328
  const message = parseJson(line);
323
329
  if (!message)
324
330
  return;
331
+ this.traceLine(line, message);
325
332
  if ('id' in message && ('result' in message || 'error' in message) && !('method' in message)) {
326
333
  const id = Number(message.id);
327
334
  const pending = this.pending.get(id);
@@ -395,7 +402,7 @@ export class CodexAppServerAdapter {
395
402
  this.messageTextByItem.set(itemId, next);
396
403
  this.currentFinalMessage = this.joinedAgentMessageText() ?? this.currentFinalMessage;
397
404
  if (next.trim())
398
- this.currentOnEvent?.({ type: 'message', text: next, itemId });
405
+ this.currentOnEvent?.({ type: 'message', text: next, delta, itemId });
399
406
  return;
400
407
  }
401
408
  if (method === 'turn/plan/updated') {
@@ -467,6 +474,7 @@ export class CodexAppServerAdapter {
467
474
  return;
468
475
  }
469
476
  if (method === 'turn/completed') {
477
+ this.trace('turn/completed notification');
470
478
  const turn = params.turn;
471
479
  const status = turn?.status;
472
480
  if (isRecord(status) && status.type === 'failed') {
@@ -530,6 +538,7 @@ export class CodexAppServerAdapter {
530
538
  this.currentErrorText = null;
531
539
  this.messageTextByItem.clear();
532
540
  this.planText = '';
541
+ this.traceTurnEpochMs = null;
533
542
  }
534
543
  sendRequest(method, params) {
535
544
  const id = this.requestSeq++;
@@ -549,6 +558,33 @@ export class CodexAppServerAdapter {
549
558
  throw new Error('Codex app-server is not running');
550
559
  this.child.stdin.write(`${JSON.stringify(message)}\n`);
551
560
  }
561
+ trace(message) {
562
+ if (!isCodexTraceEnabled())
563
+ return;
564
+ const elapsedMs = this.traceTurnEpochMs === null ? 0 : Date.now() - this.traceTurnEpochMs;
565
+ console.error(`[canon-codex-trace] +${elapsedMs}ms ${message}`);
566
+ }
567
+ traceLine(line, message) {
568
+ if (!isCodexTraceEnabled())
569
+ return;
570
+ const method = typeof message.method === 'string' ? message.method : null;
571
+ if (!method) {
572
+ const id = 'id' in message ? ` id=${String(message.id)}` : '';
573
+ this.trace(`line bytes=${line.length} response${id}`);
574
+ return;
575
+ }
576
+ if (method === 'item/agentMessage/delta') {
577
+ const params = isRecord(message.params) ? message.params : {};
578
+ const itemId = readString(params, 'itemId') ?? 'agent-message';
579
+ const delta = readRawString(params, 'delta') ?? '';
580
+ this.trace(`line bytes=${line.length} method=${method} itemId=${itemId} deltaLen=${delta.length}`);
581
+ return;
582
+ }
583
+ this.trace(`line bytes=${line.length} method=${method}`);
584
+ }
585
+ }
586
+ function isCodexTraceEnabled() {
587
+ return process.env.CANON_CODEX_TRACE_EVENTS === '1';
552
588
  }
553
589
  function parseJson(line) {
554
590
  try {
package/dist/host.js CHANGED
@@ -26,7 +26,7 @@ import { detectCodexCliVersion } from './codex-cli-version.js';
26
26
  import { buildCodexModelGuardMessage, formatCodexTurnFailure, isRecoverableCodexThreadError, } from './error-format.js';
27
27
  import { attachCodexControlNotifications } from './control-channel.js';
28
28
  import { runCli } from './cli-entry.js';
29
- import { beginCommandBlock, claimCommandBlock, createCommandBlockTracker, hasSpeechSegmentText, textSegmentBlockId, } from './turn-activity.js';
29
+ import { beginCommandBlock, applyCodexMessageToStreamingOutput, claimCommandBlock, createCommandBlockTracker, hasSpeechSegmentText, } from './turn-activity.js';
30
30
  const HELP = `canon-codex — run a local Codex agent host for Canon
31
31
 
32
32
  USAGE
@@ -108,6 +108,35 @@ const CODEX_RUNTIME_CAPABILITIES = {
108
108
  supportsQueue: true,
109
109
  supportsNonFinalPermanentMessages: false,
110
110
  };
111
+ function isCodexTraceEnabled() {
112
+ return process.env.CANON_CODEX_TRACE_EVENTS === '1';
113
+ }
114
+ function createCodexTracePort(port) {
115
+ if (!isCodexTraceEnabled())
116
+ return port;
117
+ return new Proxy(port, {
118
+ get(target, property, receiver) {
119
+ if (property !== 'publishStreaming') {
120
+ return Reflect.get(target, property, receiver);
121
+ }
122
+ return async (input) => {
123
+ const startedAt = Date.now();
124
+ const textLength = typeof input.text === 'string' ? input.text.length : 0;
125
+ console.error(`[canon-codex-trace] publishStreaming sent conversation=${input.conversationId} turn=${input.turnId ?? input.messageId ?? 'unknown'} textLen=${textLength}`);
126
+ try {
127
+ const result = await target.publishStreaming(input);
128
+ console.error(`[canon-codex-trace] publishStreaming ack conversation=${input.conversationId} ackMs=${Date.now() - startedAt}`);
129
+ return result;
130
+ }
131
+ catch (error) {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ console.error(`[canon-codex-trace] publishStreaming error conversation=${input.conversationId} ackMs=${Date.now() - startedAt} error=${message}`);
134
+ throw error;
135
+ }
136
+ };
137
+ },
138
+ });
139
+ }
111
140
  // This host process resolves and locks exactly one agent profile. The lock
112
141
  // handle returned by resolveCanonAgent is held here so the top-level runCli
113
142
  // error handler (outside main's scope) can release it on a failed start —
@@ -554,7 +583,7 @@ export async function main() {
554
583
  });
555
584
  // The BridgeClient's zod-derived result shapes mirror the core wire types
556
585
  // structurally; the port pins the strong types the host machinery uses.
557
- const port = bridge.client;
586
+ const port = createCodexTracePort(bridge.client);
558
587
  bridge.client.onProtocolError((error) => {
559
588
  console.error(`[canon-codex] Bridge protocol error: ${error.message}`);
560
589
  });
@@ -976,7 +1005,12 @@ export async function main() {
976
1005
  session.turnState = 'streaming';
977
1006
  writers.writeTurn();
978
1007
  writers.stopVisibleWorkSignal();
979
- writers.streamingOutput.replaceTextSegmentSnapshot(textSegmentBlockId(session.currentTurnId, event.itemId), event.text);
1008
+ applyCodexMessageToStreamingOutput(writers.streamingOutput, {
1009
+ turnId: session.currentTurnId,
1010
+ itemId: event.itemId,
1011
+ text: event.text,
1012
+ delta: event.delta,
1013
+ });
980
1014
  return;
981
1015
  }
982
1016
  if (event.type === 'plan.updated') {
@@ -24,6 +24,16 @@ export declare function createCommandBlockTracker(): CommandBlockTracker;
24
24
  * only owns the codex id scheme.
25
25
  */
26
26
  export declare function textSegmentBlockId(turnId: string | null | undefined, itemId?: string): string;
27
+ export interface CodexTextSegmentOutput {
28
+ appendTextSegmentDelta(id: string, delta: string): void;
29
+ replaceTextSegmentSnapshot(id: string, text: string): void;
30
+ }
31
+ export declare function applyCodexMessageToStreamingOutput(output: CodexTextSegmentOutput, input: {
32
+ turnId: string | null | undefined;
33
+ itemId?: string;
34
+ text: string;
35
+ delta?: string;
36
+ }): void;
27
37
  export declare function beginCommandBlock(tracker: CommandBlockTracker, input: {
28
38
  turnId: string | null | undefined;
29
39
  command: string;
@@ -27,6 +27,14 @@ function normalizeOptionalString(value) {
27
27
  export function textSegmentBlockId(turnId, itemId) {
28
28
  return `message:${turnId ?? 'turn'}:${normalizeOptionalString(itemId) ?? 'latest'}`;
29
29
  }
30
+ export function applyCodexMessageToStreamingOutput(output, input) {
31
+ const segmentId = textSegmentBlockId(input.turnId, input.itemId);
32
+ if (input.delta !== undefined) {
33
+ output.appendTextSegmentDelta(segmentId, input.delta);
34
+ return;
35
+ }
36
+ output.replaceTextSegmentSnapshot(segmentId, input.text);
37
+ }
30
38
  function nextCommandBlockId(tracker, turnId, itemId) {
31
39
  const stableTurnId = turnId ?? 'turn';
32
40
  if (itemId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.19.1",
3
+ "version": "0.19.2",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,11 +29,11 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@canonmsg/agent-host": "^0.3.0",
33
- "@canonmsg/agent-sdk": "^3.4.3",
32
+ "@canonmsg/agent-host": "^0.4.0",
33
+ "@canonmsg/agent-sdk": "^3.4.4",
34
34
  "@canonmsg/backend-contracts": "^1.8.1",
35
35
  "@canonmsg/bridge": "^0.2.1",
36
- "@canonmsg/core": "^3.1.1",
36
+ "@canonmsg/core": "^3.2.0",
37
37
  "@canonmsg/framework": "^0.2.1"
38
38
  },
39
39
  "engines": {