march-cli 0.1.15 → 0.1.17

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.15",
3
+ "version": "0.1.17",
4
4
  "description": "March CLI — terminal-native coding agent with context reconstruction",
5
5
  "type": "module",
6
6
  "main": "./src/main.mjs",
@@ -5,8 +5,7 @@ import { createMarchLifecycleAdapter } from "../extensions/lifecycle-adapter.mjs
5
5
  import { syncPiSessionSidecar } from "../session/sidecar-sync.mjs";
6
6
  import { LspService } from "../lsp/service.mjs";
7
7
  import { formatLspServiceEvent } from "../lsp/status-message.mjs";
8
- import { formatRecallHints } from "../memory/markdown-store.mjs";
9
- import { appendProviderUserMessage, estimateProviderPayloadTokens, installModelPayloadDumper, replaceProviderContextMessages } from "./model-payload-dumper.mjs";
8
+ import { estimateProviderPayloadTokens, installModelPayloadDumper, replaceProviderContextMessages } from "./model-payload-dumper.mjs";
10
9
  import { resolveInitialModel, resolveRunnerSessionManager } from "./runner/runner-init.mjs";
11
10
  import { runRunnerCleanup } from "./runner/runner-cleanup.mjs";
12
11
  import { createRunnerRuntimeHost } from "./runtime/runner-runtime-host.mjs";
@@ -55,7 +54,6 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
55
54
  let currentModelCallKind = "model", currentTurnId = null, currentPromptForContext = "";
56
55
  let currentTurnContextMode = "rebuild";
57
56
  let nextTurnContextMode = "rebuild";
58
- let pendingMidTurnRecallHints = [];
59
57
  let lastNotificationResult = null, runtimeHost = null, lifecycleAdapter = null;
60
58
  let _currentFastEntry = null;
61
59
  if (useRuntimeHost) {
@@ -111,7 +109,6 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
111
109
  const contextMode = nextTurnContextMode;
112
110
  currentTurnContextMode = contextMode;
113
111
  nextTurnContextMode = "rebuild";
114
- pendingMidTurnRecallHints = [];
115
112
  const turnStartedAt = Date.now();
116
113
  const turnLog = beginLoggedTurn({ logger, engine, modelId, provider, contextMode, userMessage, userRecallHints, startedAt: turnStartedAt }); currentTurnId = turnLog.turnId;
117
114
  try {
@@ -121,7 +118,6 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
121
118
  setModelCallKind: (kind) => { currentModelCallKind = kind; },
122
119
  logger: turnLog.logger,
123
120
  setPhase: turnLog.setPhase,
124
- onMidTurnRecallHints: (hints) => { pendingMidTurnRecallHints.push(...hints); },
125
121
  syncCurrentPiSidecar,
126
122
  autoNameSession,
127
123
  contextMode,
@@ -289,10 +285,6 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
289
285
  : replaceProviderContextMessages(payload, engine.buildProviderContext(currentPromptForContext));
290
286
  nextPayload = injectHostedTools(nextPayload, model, hostedTools);
291
287
  if (_currentFastEntry) nextPayload = { ...nextPayload, service_tier: "priority" };
292
- if (pendingMidTurnRecallHints.length > 0) {
293
- nextPayload = appendProviderUserMessage(nextPayload, formatRecallHints("assistant", pendingMidTurnRecallHints));
294
- pendingMidTurnRecallHints = [];
295
- }
296
288
  return nextPayload;
297
289
  }
298
290
  }
@@ -1,93 +1,11 @@
1
- import { join } from "node:path";
2
- import { createRunner } from "../runner.mjs";
3
1
  import { createProcessRuntimeIpcPeer } from "./ipc/process-ipc-transport.mjs";
4
- import { createRemoteRuntimeUiClient } from "./remote-ui-client.mjs";
5
2
  import { createRunnerIpcTarget } from "./runner-ipc-target.mjs";
6
- import { createMarchAuthStorage } from "../../auth/storage.mjs";
7
- import { createCliShellRuntime } from "../../shell/cli-runtime.mjs";
8
- import { MarkdownMemoryStore } from "../../memory/markdown-store.mjs";
9
- import { createMarkdownMemoryTools } from "../../memory/markdown-tools.mjs";
10
- import { initializeMcp } from "../../mcp/index.mjs";
11
- import { createWebToolsFromConfig } from "../../web/tools.mjs";
12
- import { createPermissionController } from "../../cli/permissions.mjs";
13
- import { resolvePiSessionManager } from "../../session/pi-manager.mjs";
14
- import { createModelContextDumper } from "../../debug/model-context-dumper.mjs";
15
- import { createLogger, installProcessLogHandlers } from "../../debug/logger.mjs";
16
- import { createDesktopTurnNotifier } from "../../notification/desktop-notifier.mjs";
3
+ import { createIsolatedRunner } from "./runner-process-factory.mjs";
17
4
 
18
5
  const peer = createProcessRuntimeIpcPeer({
19
- target: createRunnerIpcTarget({ createRunnerImpl: createIsolatedRunner }),
6
+ target: createRunnerIpcTarget({
7
+ createRunnerImpl: (options) => createIsolatedRunner(options, { peer }),
8
+ }),
20
9
  });
21
10
 
22
11
  process.once("disconnect", () => peer.dispose());
23
-
24
- async function createIsolatedRunner(options = {}) {
25
- const ui = createRemoteRuntimeUiClient(peer);
26
- const memoryStore = new MarkdownMemoryStore({ root: options.memoryRoot });
27
- const memoryTools = createMarkdownMemoryTools(memoryStore);
28
- const shellRuntime = options.shellRuntime ? createCliShellRuntime({ cwd: options.cwd }) : null;
29
- const mcpInit = await initializeMcp({ projectDir: options.cwd });
30
- const logger = createLogger({ logDir: options.logDir ?? (options.stateRoot ? join(options.stateRoot, "logs") : undefined) });
31
- installProcessLogHandlers(logger);
32
-
33
- const runner = await createRunner({
34
- cwd: options.cwd,
35
- modelId: options.modelId,
36
- provider: options.provider,
37
- serviceTier: options.serviceTier,
38
- providers: options.providers,
39
- stateRoot: options.stateRoot,
40
- ui,
41
- memoryRoot: options.memoryRoot,
42
- profilePaths: options.profilePaths,
43
- memoryStore,
44
- memoryTools,
45
- shellRuntime,
46
- mcpTools: mcpInit.mcpTools,
47
- mcpInjections: mcpInit.mcpInjections,
48
- mcpClientManager: mcpInit.clientManager,
49
- webTools: createWebToolsFromConfig(options.config ?? {}),
50
- namespace: options.namespace,
51
- projectMarchDir: options.projectMarchDir,
52
- extensionPaths: options.extensionPaths ?? [],
53
- sessionManager: resolvePiSessionManager({
54
- cwd: options.cwd,
55
- projectMarchDir: options.projectMarchDir,
56
- enabled: true,
57
- }),
58
- useRuntimeHost: true,
59
- syncPiSidecar: true,
60
- lifecycleHooks: options.lifecycleHooks ?? [],
61
- lifecycleDiagnostics: options.lifecycleDiagnostics ?? [],
62
- authStorage: createMarchAuthStorage({
63
- provider: options.provider ?? "deepseek",
64
- providers: options.providers,
65
- cwd: options.cwd,
66
- }).authStorage,
67
- maxTurns: options.config?.maxTurns ?? undefined,
68
- trimBatch: options.config?.trimBatch ?? undefined,
69
- hostedTools: options.config?.hostedTools,
70
- permissionController: createPermissionController({ mode: options.permissionMode }),
71
- modelContextDumper: createModelContextDumper(options.modelContextDumper ?? { enabled: false }),
72
- turnNotifier: createDesktopTurnNotifier({
73
- enabled: Boolean(options.config?.notifications?.turnEnd),
74
- config: options.config?.notifications,
75
- }),
76
- logger,
77
- onModelPayload: (event) => peer.notify("modelPayload", pickModelPayloadEvent(event)),
78
- });
79
-
80
- const originalDispose = runner.dispose;
81
- runner.dispose = async () => {
82
- try {
83
- await originalDispose.call(runner);
84
- } finally {
85
- memoryStore.close?.();
86
- }
87
- };
88
- return runner;
89
- }
90
-
91
- function pickModelPayloadEvent({ estimatedTokens, provider, model, kind, turnId } = {}) {
92
- return { estimatedTokens, provider, model, kind, turnId };
93
- }
@@ -0,0 +1,108 @@
1
+ import { join } from "node:path";
2
+ import { createRunner } from "../runner.mjs";
3
+ import { createRemoteRuntimeUiClient } from "./remote-ui-client.mjs";
4
+ import { createMarchAuthStorage } from "../../auth/storage.mjs";
5
+ import { createCliShellRuntime } from "../../shell/cli-runtime.mjs";
6
+ import { MarkdownMemoryStore } from "../../memory/markdown-store.mjs";
7
+ import { createMarkdownMemoryTools } from "../../memory/markdown-tools.mjs";
8
+ import { initializeMcp } from "../../mcp/index.mjs";
9
+ import { createWebToolsFromConfig } from "../../web/tools.mjs";
10
+ import { createPermissionController } from "../../cli/permissions.mjs";
11
+ import { resolvePiSessionManager } from "../../session/pi-manager.mjs";
12
+ import { createModelContextDumper } from "../../debug/model-context-dumper.mjs";
13
+ import { createLogger, installProcessLogHandlers } from "../../debug/logger.mjs";
14
+ import { createDesktopTurnNotifier } from "../../notification/desktop-notifier.mjs";
15
+ import { installNetworkEnvironment } from "../../network/environment.mjs";
16
+
17
+ const DEFAULT_DEPS = {
18
+ createRunner,
19
+ createRemoteRuntimeUiClient,
20
+ createCliShellRuntime,
21
+ MarkdownMemoryStore,
22
+ createMarkdownMemoryTools,
23
+ initializeMcp,
24
+ createWebToolsFromConfig,
25
+ createPermissionController,
26
+ resolvePiSessionManager,
27
+ createModelContextDumper,
28
+ createMarchAuthStorage,
29
+ createLogger,
30
+ installProcessLogHandlers,
31
+ createDesktopTurnNotifier,
32
+ installNetworkEnvironment,
33
+ };
34
+
35
+ export async function createIsolatedRunner(options = {}, deps = {}) {
36
+ const d = { ...DEFAULT_DEPS, ...deps };
37
+ d.installNetworkEnvironment(options.config?.network);
38
+
39
+ const ui = d.createRemoteRuntimeUiClient(d.peer);
40
+ const memoryStore = new d.MarkdownMemoryStore({ root: options.memoryRoot });
41
+ const memoryTools = d.createMarkdownMemoryTools(memoryStore);
42
+ const shellRuntime = options.shellRuntime ? d.createCliShellRuntime({ cwd: options.cwd }) : null;
43
+ const mcpInit = await d.initializeMcp({ projectDir: options.cwd });
44
+ const logDir = options.logDir ?? (options.stateRoot ? join(options.stateRoot, "logs") : undefined);
45
+ const logger = d.createLogger({ logDir });
46
+ d.installProcessLogHandlers(logger);
47
+
48
+ const runner = await d.createRunner({
49
+ cwd: options.cwd,
50
+ modelId: options.modelId,
51
+ provider: options.provider,
52
+ serviceTier: options.serviceTier,
53
+ providers: options.providers,
54
+ stateRoot: options.stateRoot,
55
+ ui,
56
+ memoryRoot: options.memoryRoot,
57
+ profilePaths: options.profilePaths,
58
+ memoryStore,
59
+ memoryTools,
60
+ shellRuntime,
61
+ mcpTools: mcpInit.mcpTools,
62
+ mcpInjections: mcpInit.mcpInjections,
63
+ mcpClientManager: mcpInit.clientManager,
64
+ webTools: d.createWebToolsFromConfig(options.config ?? {}),
65
+ namespace: options.namespace,
66
+ projectMarchDir: options.projectMarchDir,
67
+ extensionPaths: options.extensionPaths ?? [],
68
+ sessionManager: d.resolvePiSessionManager({
69
+ cwd: options.cwd,
70
+ projectMarchDir: options.projectMarchDir,
71
+ enabled: true,
72
+ }),
73
+ useRuntimeHost: true,
74
+ syncPiSidecar: true,
75
+ lifecycleHooks: options.lifecycleHooks ?? [],
76
+ lifecycleDiagnostics: options.lifecycleDiagnostics ?? [],
77
+ authStorage: d.createMarchAuthStorage({
78
+ provider: options.provider ?? "deepseek",
79
+ providers: options.providers,
80
+ cwd: options.cwd,
81
+ }).authStorage,
82
+ maxTurns: options.config?.maxTurns ?? undefined,
83
+ trimBatch: options.config?.trimBatch ?? undefined,
84
+ hostedTools: options.config?.hostedTools,
85
+ permissionController: d.createPermissionController({ mode: options.permissionMode }),
86
+ modelContextDumper: d.createModelContextDumper(options.modelContextDumper ?? { enabled: false }),
87
+ turnNotifier: d.createDesktopTurnNotifier({
88
+ enabled: Boolean(options.config?.notifications?.turnEnd),
89
+ config: options.config?.notifications,
90
+ }),
91
+ logger,
92
+ onModelPayload: (event) => d.peer.notify("modelPayload", pickModelPayloadEvent(event)),
93
+ });
94
+
95
+ const originalDispose = runner.dispose;
96
+ runner.dispose = async () => {
97
+ try {
98
+ await originalDispose.call(runner);
99
+ } finally {
100
+ memoryStore.close?.();
101
+ }
102
+ };
103
+ return runner;
104
+ }
105
+
106
+ function pickModelPayloadEvent({ estimatedTokens, provider, model, kind, turnId } = {}) {
107
+ return { estimatedTokens, provider, model, kind, turnId };
108
+ }
@@ -1,3 +1,4 @@
1
+ import { formatRecallHints } from "../../memory/markdown-store.mjs";
1
2
  import { resolveImageAttachmentReferences } from "../../session/attachment-references.mjs";
2
3
  import { closeAssistantReply, compactAssistantContext, createTurnEventState, handleRunnerSessionEvent } from "./turn-events.mjs";
3
4
 
@@ -13,7 +14,6 @@ export async function runRunnerTurn({
13
14
  setModelCallKind,
14
15
  logger = null,
15
16
  setPhase = null,
16
- onMidTurnRecallHints,
17
17
  syncCurrentPiSidecar,
18
18
  autoNameSession,
19
19
  contextMode = "rebuild",
@@ -41,7 +41,7 @@ export async function runRunnerTurn({
41
41
  const hints = flushAssistantRecall({ memoryStore, engine, turnState, currentProject });
42
42
  if (hints.length > 0) {
43
43
  midTurnRecallHints.push(...hints);
44
- onMidTurnRecallHints?.(hints);
44
+ queueMidTurnRecallHints(activeSession, hints, logger);
45
45
  ui.memoryHint?.({ source: "assistant", hints });
46
46
  }
47
47
  }
@@ -89,6 +89,20 @@ export async function runRunnerTurn({
89
89
  }
90
90
  }
91
91
 
92
+ function queueMidTurnRecallHints(session, hints, logger) {
93
+ const content = formatRecallHints("assistant", hints);
94
+ if (!content) return;
95
+ const injected = session.sendCustomMessage?.({
96
+ customType: "march.memory_hint",
97
+ content,
98
+ display: false,
99
+ details: { source: "assistant" },
100
+ }, { deliverAs: "steer" });
101
+ void injected?.catch?.((err) => {
102
+ logger?.debug("memory.mid_turn_recall.inject_failed", { errorMessage: err?.message ?? String(err) });
103
+ });
104
+ }
105
+
92
106
  function logSessionEvent(logger, event) {
93
107
  if (!logger) return;
94
108
  if (event.type === "message_update") {