march-cli 0.1.42 → 0.1.46

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/agent/code-search/cache.mjs +16 -7
  3. package/src/agent/code-search/engine.mjs +9 -2
  4. package/src/agent/code-search/retrieval/resilient-vectorizer.mjs +59 -0
  5. package/src/agent/code-search/retrieval/safetensors.mjs +16 -10
  6. package/src/agent/code-search/scanner.mjs +11 -5
  7. package/src/agent/code-search/tool.mjs +11 -5
  8. package/src/agent/model-payload-dumper.mjs +1 -1
  9. package/src/agent/runner/payload/provider-payload-transform.mjs +59 -0
  10. package/src/agent/runner/recall/mid-turn-recall-bridge.mjs +23 -0
  11. package/src/agent/runner.mjs +28 -27
  12. package/src/agent/runtime/remote-ui-client.mjs +1 -1
  13. package/src/agent/runtime/resource/context-resource-loader.mjs +17 -0
  14. package/src/agent/runtime/runtime-factory.mjs +5 -1
  15. package/src/agent/runtime/state/runner-state.mjs +10 -3
  16. package/src/agent/runtime/ui-event-bridge.mjs +2 -2
  17. package/src/agent/turn/turn-runner.mjs +35 -24
  18. package/src/cli/fallback-ui.mjs +2 -2
  19. package/src/cli/repl-loop.mjs +9 -8
  20. package/src/cli/startup/app-runtime.mjs +4 -2
  21. package/src/cli/tui/input/mouse-selection-controller.mjs +19 -0
  22. package/src/cli/tui/output/selectable-copy.mjs +3 -3
  23. package/src/cli/tui/output/timeline-block-restore.mjs +1 -1
  24. package/src/cli/tui/output-buffer.mjs +18 -0
  25. package/src/cli/tui/recall-rendering.mjs +38 -9
  26. package/src/cli/tui/selection/ansi-range.mjs +88 -0
  27. package/src/cli/tui/selection-screen.mjs +31 -99
  28. package/src/cli/turn/turn-input-preparer.mjs +15 -6
  29. package/src/cli/ui.mjs +2 -2
  30. package/src/cli/workspace/tui-timeline-projection.mjs +1 -1
  31. package/src/context/engine.mjs +15 -5
  32. package/src/context/system-core/base.md +1 -1
  33. package/src/memory/markdown/markdown-format.mjs +0 -17
  34. package/src/memory/markdown/markdown-recall.mjs +11 -19
  35. package/src/memory/markdown/semantic-preload.mjs +26 -0
  36. package/src/memory/markdown/semantic-recall.mjs +169 -0
  37. package/src/memory/markdown/sqlite-index.mjs +1 -13
  38. package/src/memory/markdown-store.mjs +34 -54
  39. package/src/web-ui/dist/assets/{index-BQtl1uQs.css → index-BG1Pxf1k.css} +1 -1
  40. package/src/web-ui/dist/assets/{index-DrlJis_D.js → index-C0xOHlDz.js} +1 -1
  41. package/src/web-ui/dist/index.html +2 -2
  42. package/src/web-ui/runtime-host.mjs +18 -3
  43. package/src/web-ui/src/components/timeline/TimelineBlocks.tsx +27 -0
  44. package/src/web-ui/src/model.ts +18 -0
  45. package/src/web-ui/src/runtime/client.ts +2 -1
  46. package/src/web-ui/src/runtime/runtimeTimeline.ts +5 -0
  47. package/src/web-ui/src/styles/shell.css +7 -0
  48. package/src/web-ui/src/timelineAdapter.ts +2 -0
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
6
  <title>March Web</title>
7
- <script type="module" crossorigin src="/assets/index-DrlJis_D.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BQtl1uQs.css">
7
+ <script type="module" crossorigin src="/assets/index-C0xOHlDz.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BG1Pxf1k.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -5,6 +5,7 @@ import { createMarchAuthStorage } from "../auth/storage.mjs";
5
5
  import { createRuntimeRunner } from "../cli/startup/create-runtime-runner.mjs";
6
6
  import { createCliShellRuntime } from "../shell/cli-runtime.mjs";
7
7
  import { MarkdownMemoryStore } from "../memory/markdown-store.mjs";
8
+ import { startSemanticMemoryRecallPreload } from "../memory/markdown/semantic-preload.mjs";
8
9
  import { resolveMemoryRoot } from "../memory/root.mjs";
9
10
  import { defaultProfilePaths, ensureProfileFiles } from "../context/profiles.mjs";
10
11
  import { loadOrCreateProjectId } from "../cli/startup/startup-session.mjs";
@@ -33,7 +34,7 @@ export async function createWebRuntimeHost({ args, config, cwd, stateRoot } = {}
33
34
  const profilePaths = defaultProfilePaths();
34
35
  ensureProfileFiles(profilePaths);
35
36
 
36
- const memoryStore = new MarkdownMemoryStore({ root: memoryRoot });
37
+ const memoryStore = new MarkdownMemoryStore({ root: memoryRoot, stateRoot });
37
38
  const remoteMemorySources = normalizeRemoteMemorySources(config);
38
39
  const shellRuntime = args.shellRuntime ? createCliShellRuntime({ cwd }) : null;
39
40
  const extensionPaths = discoverProjectExtensionPaths(cwd);
@@ -66,6 +67,7 @@ export async function createWebRuntimeHost({ args, config, cwd, stateRoot } = {}
66
67
  ui,
67
68
  shellRuntime,
68
69
  });
70
+ startSemanticMemoryRecallPreload({ memoryStore, logger, delayMs: 1000 });
69
71
  let turnRunning = false;
70
72
 
71
73
  return {
@@ -81,9 +83,13 @@ export async function createWebRuntimeHost({ args, config, cwd, stateRoot } = {}
81
83
  turnRunning = true;
82
84
  memoryStore.beginTurn();
83
85
  try {
84
- const input = prepareTurnInput({ prompt, runner, memoryStore, currentProject });
86
+ const input = await prepareTurnInput({ prompt, runner, memoryStore, currentProject });
85
87
  runner.runtimeUiEvents.emit({ type: "web_user_message", text: input.userMessage });
86
- return await runner.runTurn(input.fullPrompt, input.userMessage, input.runOptions);
88
+ runner.runtimeUiEvents.emit({ type: "recall", hints: input.userRecallHints, report: input.userRecallReport });
89
+ if (input.shouldRenderCarryoverRecall) runner.runtimeUiEvents.emit({ type: "recall", hints: input.carryoverRecallHints, report: input.carryoverRecallReport, variant: "assistant" });
90
+ const result = await runner.runTurn(input.fullPrompt, input.userMessage, input.runOptions);
91
+ emitPendingAssistantRecallPreview(runner);
92
+ return result;
87
93
  } finally {
88
94
  turnRunning = false;
89
95
  memoryStore.endTurn();
@@ -97,6 +103,15 @@ export async function createWebRuntimeHost({ args, config, cwd, stateRoot } = {}
97
103
  };
98
104
  }
99
105
 
106
+ function emitPendingAssistantRecallPreview(runner) {
107
+ if (runner.engine.hasRenderedPendingAssistantRecallHints?.()) return;
108
+ const hints = runner.engine.peekPendingAssistantRecallHints?.() ?? [];
109
+ const report = runner.engine.peekPendingAssistantRecallReport?.() ?? null;
110
+ if (hints.length === 0 && !report) return;
111
+ runner.runtimeUiEvents.emit({ type: "recall", hints, report, variant: "assistant" });
112
+ runner.engine.markPendingAssistantRecallHintsRendered?.();
113
+ }
114
+
100
115
  export function createHeadlessWebUi() {
101
116
  return {
102
117
  readline: () => Promise.resolve(null), write: () => {}, writeln: () => {},
@@ -39,6 +39,8 @@ function AuxBlock({ item }: { item: Exclude<TimelineItem, { kind: "message" }> }
39
39
  return <DiffBlock item={item} />;
40
40
  case "terminal":
41
41
  return <TerminalBlock item={item} />;
42
+ case "memoryRecall":
43
+ return <MemoryRecallBlock item={item} />;
42
44
 
43
45
  case "error":
44
46
  return <ErrorBlock item={item} />;
@@ -89,7 +91,32 @@ function TerminalBlock({ item }: { item: Extract<TimelineItem, { kind: "terminal
89
91
  );
90
92
  }
91
93
 
94
+ function MemoryRecallBlock({ item }: { item: Extract<TimelineItem, { kind: "memoryRecall" }> }) {
95
+ const isAssistant = item.variant === "assistant";
96
+ const reportCandidates = item.report?.candidates ?? [];
97
+ const candidates = (reportCandidates.length ? reportCandidates : item.hints.map((hint) => ({ ...hint, recalled: true }))).slice(0, isAssistant ? 3 : undefined);
98
+ const summary = `${item.hints.length} recalled · ${reportCandidates.length || candidates.length} ${(reportCandidates.length || candidates.length) === 1 ? "candidate" : "candidates"}`;
99
+ const threshold = typeof item.report?.threshold === "number" ? ` · threshold ${item.report.threshold.toFixed(2)}` : "";
100
+ const fallback = item.report?.vectorizerStatus === "fallback" ? " · fallback" : "";
101
+ return (
102
+ <div className={`timeline-aux memory-recall-block${isAssistant ? " compact" : ""}`}>
103
+ <div className="aux-title"><span>memory</span><strong>recall · {summary}{threshold}{fallback}</strong></div>
104
+ {item.report?.warning ? <p className="memory-recall-warning">! {item.report.warning}</p> : null}
105
+ <ul>
106
+ {candidates.length ? candidates.map((hint) => (
107
+ <li key={hint.id} className={hint.recalled === false ? "skipped" : "recalled"}>
108
+ <span>{hint.recalled === false ? "×" : "✓"}</span>
109
+ <strong>{formatRecallScore(hint.score)} {hint.name ?? hint.id}</strong>
110
+ </li>
111
+ )) : <li className="skipped"><span>·</span><strong>no candidates</strong></li>}
112
+ </ul>
113
+ </div>
114
+ );
115
+ }
92
116
 
117
+ function formatRecallScore(score?: number) {
118
+ return typeof score === "number" && Number.isFinite(score) ? score.toFixed(2) : "--";
119
+ }
93
120
 
94
121
  function ErrorBlock({ item }: { item: Extract<TimelineItem, { kind: "error" }> }) {
95
122
  return (
@@ -9,6 +9,22 @@ export type FileNode = {
9
9
  children?: FileNode[];
10
10
  };
11
11
 
12
+ export type MemoryRecallHint = {
13
+ id: string;
14
+ name?: string;
15
+ description?: string;
16
+ score?: number;
17
+ recalled?: boolean;
18
+ };
19
+
20
+ export type MemoryRecallReport = {
21
+ threshold?: number;
22
+ hints?: MemoryRecallHint[];
23
+ candidates?: MemoryRecallHint[];
24
+ vectorizerStatus?: string;
25
+ warning?: string | null;
26
+ };
27
+
12
28
  export type MarchTimelineEvent =
13
29
  | { id: string; type: "user_message"; text: string; time?: string }
14
30
  | { id: string; type: "assistant_message"; text: string; time?: string }
@@ -17,6 +33,7 @@ export type MarchTimelineEvent =
17
33
  | { id: string; type: "tool_result"; tool: string; summary: string; status: "done" | "failed" }
18
34
  | { id: string; type: "file_diff"; path: string; lines: Array<{ kind: "add" | "remove" | "keep"; text: string }> }
19
35
  | { id: string; type: "terminal_output"; command: string; output: string; status: "running" | "done" | "failed" }
36
+ | { id: string; type: "memory_recall"; hints: MemoryRecallHint[]; report?: MemoryRecallReport | null; variant?: "assistant" | "user" }
20
37
  | { id: string; type: "error"; message: string; detail?: string };
21
38
 
22
39
  export type TimelineItem =
@@ -25,6 +42,7 @@ export type TimelineItem =
25
42
  | { id: string; kind: "tool"; tool: string; target: string; status: "running" | "done" | "failed"; summary?: string }
26
43
  | { id: string; kind: "diff"; path: string; lines: Array<{ kind: "add" | "remove" | "keep"; text: string }> }
27
44
  | { id: string; kind: "terminal"; command: string; output: string; status: "running" | "done" | "failed" }
45
+ | { id: string; kind: "memoryRecall"; hints: MemoryRecallHint[]; report?: MemoryRecallReport | null; variant?: "assistant" | "user" }
28
46
  | { id: string; kind: "error"; message: string; detail?: string };
29
47
 
30
48
  export type SessionSummary = {
@@ -1,4 +1,4 @@
1
- import type { ProviderQuotaSnapshot, SessionSummary, WebUiModel } from "../model";
1
+ import type { MemoryRecallHint, MemoryRecallReport, ProviderQuotaSnapshot, SessionSummary, WebUiModel } from "../model";
2
2
 
3
3
  export type RuntimeUiEvent =
4
4
  | { type: "web_user_message"; text: string }
@@ -13,6 +13,7 @@ export type RuntimeUiEvent =
13
13
  | { type: "tool_end"; name: string; isError?: boolean; result?: unknown }
14
14
  | { type: "edit_diff"; path: string; diffLines?: Array<{ type?: string; text?: string }> }
15
15
  | { type: "provider_quota_snapshot"; snapshot: ProviderQuotaSnapshot | null }
16
+ | { type: "recall"; hints: MemoryRecallHint[]; report?: MemoryRecallReport | null; variant?: "assistant" | "user" }
16
17
  | { type: "status"; text: string }
17
18
  | { type: "retry_start"; errorMessage?: string }
18
19
  | { type: "retry_end"; success?: boolean; finalError?: string };
@@ -26,6 +26,11 @@ export function applyRuntimeEvent(events: MarchTimelineEvent[], event: RuntimeUi
26
26
  case "edit_diff":
27
27
  next.push({ id, type: "file_diff", path: event.path, lines: toDiffLines(event.diffLines) });
28
28
  return next;
29
+ case "recall":
30
+ if ((event.hints?.length ?? 0) || event.report) {
31
+ next.push({ id, type: "memory_recall", hints: event.hints ?? [], report: event.report ?? null, variant: event.variant });
32
+ }
33
+ return next;
29
34
 
30
35
  case "status":
31
36
  next.push({ id, type: "terminal_output", command: "status", output: event.text, status: "done" });
@@ -95,6 +95,13 @@
95
95
  .diff-line.remove, .error-block strong { color: var(--color-danger); }
96
96
  .diff-line.keep { color: var(--color-text-muted); }
97
97
  .terminal-block pre { margin: 7px 0 0; color: var(--color-text-muted); font-size: 12px; white-space: pre-wrap; }
98
+ .memory-recall-block.compact { opacity: 0.62; }
99
+ .memory-recall-block .memory-recall-warning { margin: 7px 0 0; color: var(--color-text-muted); }
100
+ .memory-recall-block ul { margin: 7px 0 0; padding: 0; list-style: none; display: grid; gap: 4px; }
101
+ .memory-recall-block li { display: flex; gap: 7px; align-items: center; min-width: 0; }
102
+ .memory-recall-block li.recalled span { color: var(--color-success); }
103
+ .memory-recall-block li.skipped span { color: var(--color-text-muted); }
104
+ .memory-recall-block li.skipped strong { color: var(--color-text-muted); }
98
105
 
99
106
 
100
107
  .right-body { flex: 1; min-height: 0; overflow: auto; padding: 12px; }
@@ -35,6 +35,8 @@ function toTimelineItem(event: MarchTimelineEvent): TimelineItem {
35
35
  return { id: event.id, kind: "diff", path: event.path, lines: event.lines };
36
36
  case "terminal_output":
37
37
  return { id: event.id, kind: "terminal", command: event.command, output: event.output, status: event.status };
38
+ case "memory_recall":
39
+ return { id: event.id, kind: "memoryRecall", hints: event.hints, report: event.report, variant: event.variant };
38
40
 
39
41
  case "error":
40
42
  return { id: event.id, kind: "error", message: event.message, detail: event.detail };