march-cli 0.1.17 → 0.1.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "march-cli",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "March CLI — terminal-native coding agent with context reconstruction",
5
5
  "type": "module",
6
6
  "main": "./src/main.mjs",
@@ -0,0 +1,65 @@
1
+ import { getOpenAICodexWebSocketDebugStats } from "@earendil-works/pi-ai/openai-codex-responses";
2
+
3
+ export function getCodexTransportDebugSnapshot(session) {
4
+ if (!isCodexTransportDebugEnabled()) return null;
5
+ const sessionId = session?.sessionId;
6
+ return sessionId ? (getOpenAICodexWebSocketDebugStats(sessionId) ?? null) : null;
7
+ }
8
+
9
+ export function dumpCodexTransportDebug({ before, session, ui, logger }) {
10
+ if (!isCodexTransportDebugEnabled()) return;
11
+ const sessionId = session?.sessionId;
12
+ const after = sessionId ? (getOpenAICodexWebSocketDebugStats(sessionId) ?? null) : null;
13
+ const fields = formatCodexTransportDebugFields(sessionId, before, after);
14
+ logger?.event("codex.transport", fields);
15
+ writeCodexTransportDebug(ui, formatCodexTransportDebugLines(fields));
16
+ }
17
+
18
+ function isCodexTransportDebugEnabled() {
19
+ const value = process.env.MARCH_CODEX_TRANSPORT_DEBUG;
20
+ return value === "1" || value === "true" || value === "yes";
21
+ }
22
+
23
+ function formatCodexTransportDebugFields(sessionId, before, after) {
24
+ const delta = (key) => (after?.[key] ?? 0) - (before?.[key] ?? 0);
25
+ return {
26
+ sessionId: sessionId ?? "unknown",
27
+ requests: delta("requests"),
28
+ totalRequests: after?.requests ?? 0,
29
+ connectionsCreated: delta("connectionsCreated"),
30
+ connectionsReused: delta("connectionsReused"),
31
+ cachedContextRequests: delta("cachedContextRequests"),
32
+ storeTrueRequests: delta("storeTrueRequests"),
33
+ fullContextRequests: delta("fullContextRequests"),
34
+ deltaRequests: delta("deltaRequests"),
35
+ websocketFailures: delta("websocketFailures"),
36
+ sseFallbacks: delta("sseFallbacks"),
37
+ websocketFallbackActive: Boolean(after?.websocketFallbackActive),
38
+ lastInputItems: after?.lastInputItems ?? 0,
39
+ lastDeltaInputItems: after?.lastDeltaInputItems ?? 0,
40
+ lastWebSocketError: after?.lastWebSocketError ?? null,
41
+ hasStats: Boolean(after),
42
+ };
43
+ }
44
+
45
+ function formatCodexTransportDebugLines(fields) {
46
+ if (!fields.hasStats) return [`[codex-transport] sessionId=${fields.sessionId} no Codex WebSocket stats`];
47
+ return [
48
+ `[codex-transport] sessionId=${fields.sessionId}`,
49
+ ` requests=${fields.requests} totalRequests=${fields.totalRequests}`,
50
+ ` wsConnections created=${fields.connectionsCreated} reused=${fields.connectionsReused}`,
51
+ ` modes full=${fields.fullContextRequests} delta=${fields.deltaRequests} cached=${fields.cachedContextRequests} storeTrue=${fields.storeTrueRequests}`,
52
+ ` fallback websocketFailures=${fields.websocketFailures} sseFallbacks=${fields.sseFallbacks} active=${fields.websocketFallbackActive}`,
53
+ ` lastInputItems=${fields.lastInputItems} lastDeltaInputItems=${fields.lastDeltaInputItems}`,
54
+ ...(fields.lastWebSocketError ? [` lastWebSocketError=${fields.lastWebSocketError}`] : []),
55
+ ];
56
+ }
57
+
58
+ function writeCodexTransportDebug(ui, lines) {
59
+ if (ui?.debugLines) {
60
+ ui.debugLines(lines);
61
+ return;
62
+ }
63
+ if (!ui?.writeln) return;
64
+ for (const line of lines) ui.writeln(line);
65
+ }
@@ -12,6 +12,7 @@ import { createRunnerRuntimeHost } from "./runtime/runner-runtime-host.mjs";
12
12
  import { createRuntimeUiBridge } from "./runtime/ui-event-bridge.mjs";
13
13
  import { getRunnerSessionStats, syncEngineSessionState } from "./runner/runner-session-state.mjs";
14
14
  import { notifyTurnEndBestEffort, notifyTurnEndDetached, providerContextToPayload } from "./runner/runner-utils.mjs";
15
+ import { dumpCodexTransportDebug, getCodexTransportDebugSnapshot } from "./runner/codex-transport-debug.mjs";
15
16
  import { resolveRunnerSessionOptions } from "./session/session-options.mjs";
16
17
  import { createSessionBinding } from "./session/session-binding.mjs";
17
18
  import { maybeAutoNameSession } from "./session/session-auto-name.mjs";
@@ -110,6 +111,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
110
111
  currentTurnContextMode = contextMode;
111
112
  nextTurnContextMode = "rebuild";
112
113
  const turnStartedAt = Date.now();
114
+ const codexTransportStatsBefore = getCodexTransportDebugSnapshot(sessionBinding.get());
113
115
  const turnLog = beginLoggedTurn({ logger, engine, modelId, provider, contextMode, userMessage, userRecallHints, startedAt: turnStartedAt }); currentTurnId = turnLog.turnId;
114
116
  try {
115
117
  const result = await runRunnerTurn({
@@ -140,6 +142,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
140
142
  turnLog.endError(err);
141
143
  throw err;
142
144
  } finally {
145
+ dumpCodexTransportDebug({ before: codexTransportStatsBefore, session: sessionBinding.get(), ui: runtimeUi, logger });
143
146
  currentTurnId = null;
144
147
  currentTurnContextMode = "rebuild";
145
148
  }
@@ -12,6 +12,7 @@ export function createRemoteRuntimeUiClient(peer) {
12
12
  retryStart: (event) => peer.notify("uiEvent", { type: "retry_start", ...event }),
13
13
  retryEnd: (event) => peer.notify("uiEvent", { type: "retry_end", ...event }),
14
14
  status: (text) => peer.notify("uiEvent", { type: "status", text }),
15
+ debugLines: (lines) => peer.notify("uiEvent", { type: "debug_lines", lines }),
15
16
  memoryHint: ({ source, hints }) => peer.notify("uiEvent", { type: "memory_hint", source, hints }),
16
17
  editDiff: (path, diffLines) => peer.notify("uiEvent", { type: "edit_diff", path, diffLines }),
17
18
  requestPermission: (request) => peer.call("uiRequest", { type: "permission_request", ...request }),
@@ -49,6 +49,7 @@ export function createRuntimeUiClient(eventBus) {
49
49
  retryStart: (event) => eventBus.emit({ type: "retry_start", ...event }),
50
50
  retryEnd: (event) => eventBus.emit({ type: "retry_end", ...event }),
51
51
  status: (text) => eventBus.emit({ type: "status", text }),
52
+ debugLines: (lines) => eventBus.emit({ type: "debug_lines", lines }),
52
53
  memoryHint: ({ source, hints }) => eventBus.emit({ type: "memory_hint", source, hints }),
53
54
  editDiff: (path, diffLines) => eventBus.emit({ type: "edit_diff", path, diffLines }),
54
55
  requestPermission: (request) => eventBus.request({ type: "permission_request", ...request }),
@@ -69,6 +70,7 @@ export function dispatchRuntimeUiEvent(ui, event) {
69
70
  case "retry_start": return ui.retryStart?.(pickRetryStart(event));
70
71
  case "retry_end": return ui.retryEnd?.(pickRetryEnd(event));
71
72
  case "status": return ui.status?.(event.text);
73
+ case "debug_lines": return writeDebugLines(ui, event.lines);
72
74
  case "memory_hint": return ui.memoryHint?.({ source: event.source, hints: event.hints });
73
75
  case "edit_diff": return ui.editDiff?.(event.path, event.diffLines);
74
76
  case "permission_request": return ui.requestPermission?.({ toolName: event.toolName, params: event.params, category: event.category });
@@ -76,6 +78,14 @@ export function dispatchRuntimeUiEvent(ui, event) {
76
78
  }
77
79
  }
78
80
 
81
+ function writeDebugLines(ui, lines) {
82
+ if (!Array.isArray(lines)) return undefined;
83
+ if (ui?.debugLines) return ui.debugLines(lines);
84
+ if (!ui?.writeln) return undefined;
85
+ for (const line of lines) ui.writeln(line);
86
+ return undefined;
87
+ }
88
+
79
89
  function pickRetryStart({ attempt, maxAttempts, delayMs, errorMessage }) {
80
90
  return { attempt, maxAttempts, delayMs, errorMessage };
81
91
  }