march-cli 0.1.24 → 0.1.26
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/bin/march.mjs +13 -13
- package/package.json +49 -49
- package/src/agent/command-exec-tool.mjs +172 -172
- package/src/agent/context-stats-tool.mjs +57 -57
- package/src/agent/editing/diff-apply.mjs +28 -28
- package/src/agent/editing/diff-format.mjs +57 -57
- package/src/agent/editing/lsp-report.mjs +69 -69
- package/src/agent/file-edit-tool.mjs +250 -250
- package/src/agent/file-tools/read-file-tool.mjs +112 -112
- package/src/agent/file-tools/read-image-tool.mjs +76 -76
- package/src/agent/model-payload-dumper.mjs +208 -208
- package/src/agent/pi-session/pi-session-sidecar-failure.mjs +10 -10
- package/src/agent/provider/payload-messages.mjs +138 -138
- package/src/agent/runner/codex-large-context-guard.mjs +87 -87
- package/src/agent/runner/codex-transport-compression.mjs +180 -180
- package/src/agent/runner/codex-transport-debug.mjs +113 -113
- package/src/agent/runner/codex-websocket-event-debug.mjs +130 -130
- package/src/agent/runner/fast-model.mjs +36 -36
- package/src/agent/runner/runner-cleanup.mjs +12 -12
- package/src/agent/runner/runner-init.mjs +15 -15
- package/src/agent/runner/runner-session-state.mjs +40 -40
- package/src/agent/runner/runner-utils.mjs +24 -24
- package/src/agent/runner.mjs +299 -299
- package/src/agent/runtime/ipc/ipc-peer.mjs +99 -99
- package/src/agent/runtime/ipc/process-ipc-transport.mjs +16 -16
- package/src/agent/runtime/remote-runner-client.mjs +73 -73
- package/src/agent/runtime/remote-ui-client.mjs +20 -20
- package/src/agent/runtime/runner-ipc-target.mjs +125 -125
- package/src/agent/runtime/runner-process-client.mjs +47 -47
- package/src/agent/runtime/runner-process-entry.mjs +11 -11
- package/src/agent/runtime/runner-process-factory.mjs +111 -108
- package/src/agent/runtime/runner-runtime-host.mjs +79 -79
- package/src/agent/runtime/runtime-factory.mjs +42 -42
- package/src/agent/runtime/runtime-host.mjs +34 -34
- package/src/agent/runtime/ui-event-bridge.mjs +95 -95
- package/src/agent/screen-tools/list-windows-tool.mjs +39 -39
- package/src/agent/screen-tools/screen-tool.mjs +49 -49
- package/src/agent/screen-tools/windows-screen.mjs +133 -133
- package/src/agent/session/session-auto-name.mjs +41 -41
- package/src/agent/session/session-binding.mjs +12 -12
- package/src/agent/session/session-options.mjs +47 -47
- package/src/agent/tool-names.mjs +1 -1
- package/src/agent/tool-result.mjs +3 -3
- package/src/agent/tool-summary.mjs +112 -112
- package/src/agent/tools.mjs +58 -58
- package/src/agent/turn/turn-events.mjs +111 -111
- package/src/agent/turn/turn-logging.mjs +30 -30
- package/src/agent/turn/turn-runner.mjs +196 -196
- package/src/agent/vision-capability.mjs +14 -14
- package/src/auth/login-command.mjs +90 -90
- package/src/auth/storage.mjs +34 -34
- package/src/cli/args.mjs +96 -79
- package/src/cli/commands/copy-command.mjs +87 -87
- package/src/cli/commands/export-command.mjs +206 -206
- package/src/cli/commands/extensions-command.mjs +53 -53
- package/src/cli/commands/help-command.mjs +7 -7
- package/src/cli/commands/model-command.mjs +141 -141
- package/src/cli/commands/paste-image-command.mjs +43 -43
- package/src/cli/commands/provider-command.mjs +59 -59
- package/src/cli/commands/status-command.mjs +196 -194
- package/src/cli/commands/thinking-command.mjs +87 -87
- package/src/cli/fallback-ui.mjs +156 -156
- package/src/cli/input/attachment-tokens.mjs +20 -20
- package/src/cli/input/autocomplete.mjs +74 -74
- package/src/cli/input/external-editor.mjs +39 -39
- package/src/cli/input/file-search/index.mjs +160 -160
- package/src/cli/input/history-store.mjs +35 -35
- package/src/cli/input/image-clipboard.mjs +55 -55
- package/src/cli/input/keybinding-dispatch.mjs +76 -76
- package/src/cli/input/keybindings.mjs +96 -96
- package/src/cli/input/mode-state.mjs +43 -43
- package/src/cli/input/prompt-templates.mjs +84 -84
- package/src/cli/input/select-with-keyboard.mjs +86 -86
- package/src/cli/permissions.mjs +103 -103
- package/src/cli/repl-commands.mjs +86 -86
- package/src/cli/repl-loop.mjs +183 -183
- package/src/cli/selector-list.mjs +21 -21
- package/src/cli/session/pi-session-switch-command.mjs +41 -41
- package/src/cli/session/session-command.mjs +23 -23
- package/src/cli/session/session-list-command.mjs +68 -68
- package/src/cli/session/session-name-command.mjs +26 -26
- package/src/cli/session/session-source-command.mjs +89 -89
- package/src/cli/session/session-switch-command.mjs +1 -1
- package/src/cli/shell/shell-command.mjs +55 -55
- package/src/cli/shell/shell-drawer-controls.mjs +33 -33
- package/src/cli/shell/shell-drawer.mjs +192 -192
- package/src/cli/shell/shell-split-layout.mjs +70 -70
- package/src/cli/slash-commands.mjs +192 -192
- package/src/cli/startup/create-runtime-runner.mjs +61 -61
- package/src/cli/startup/runtime-close.mjs +23 -23
- package/src/cli/startup/startup-banner.mjs +71 -71
- package/src/cli/startup/startup-session.mjs +51 -51
- package/src/cli/status-line-updater.mjs +75 -75
- package/src/cli/tool-output.mjs +9 -9
- package/src/cli/tui/editor/external-editor-runner.mjs +24 -24
- package/src/cli/tui/input/mouse-selection-controller.mjs +91 -91
- package/src/cli/tui/input/mouse-tracking.mjs +20 -20
- package/src/cli/tui/layout/main-pane-layout.mjs +47 -47
- package/src/cli/tui/layout/safe-render-boundary.mjs +46 -46
- package/src/cli/tui/markdown-renderer.mjs +285 -285
- package/src/cli/tui/output/scroll-state.mjs +79 -79
- package/src/cli/tui/output/text-line-renderer.mjs +50 -50
- package/src/cli/tui/output/tool-card-renderer.mjs +59 -59
- package/src/cli/tui/output/visible-lines.mjs +8 -8
- package/src/cli/tui/output-buffer.mjs +293 -293
- package/src/cli/tui/permission-request-ui.mjs +18 -18
- package/src/cli/tui/recall-rendering.mjs +28 -25
- package/src/cli/tui/render/render-scheduler.mjs +26 -26
- package/src/cli/tui/render/stream-delta-buffer.mjs +46 -46
- package/src/cli/tui/select/editor-select-list.mjs +111 -111
- package/src/cli/tui/selection-screen.mjs +269 -269
- package/src/cli/tui/status/retry-status.mjs +72 -72
- package/src/cli/tui/status/spinner-status.mjs +42 -42
- package/src/cli/tui/status/status-bar.mjs +225 -225
- package/src/cli/tui/syntax/highlighting.mjs +260 -260
- package/src/cli/tui/syntax/languages.mjs +91 -91
- package/src/cli/tui/syntax/tree-sitter/bash.highlights.scm +261 -261
- package/src/cli/tui/syntax/tree-sitter/c.highlights.scm +341 -341
- package/src/cli/tui/syntax/tree-sitter/cpp.highlights.scm +268 -268
- package/src/cli/tui/syntax/tree-sitter/csharp.highlights.scm +577 -577
- package/src/cli/tui/syntax/tree-sitter/css.highlights.scm +109 -109
- package/src/cli/tui/syntax/tree-sitter/diff.highlights.scm +49 -49
- package/src/cli/tui/syntax/tree-sitter/go.highlights.scm +254 -254
- package/src/cli/tui/syntax/tree-sitter/html.highlights.scm +13 -13
- package/src/cli/tui/syntax/tree-sitter/java.highlights.scm +330 -330
- package/src/cli/tui/syntax/tree-sitter/json.highlights.scm +38 -38
- package/src/cli/tui/syntax/tree-sitter/php.highlights.scm +203 -203
- package/src/cli/tui/syntax/tree-sitter/python.highlights.scm +137 -137
- package/src/cli/tui/syntax/tree-sitter/ruby.highlights.scm +309 -309
- package/src/cli/tui/syntax/tree-sitter/rust.highlights.scm +531 -531
- package/src/cli/tui/syntax/tree-sitter/toml.highlights.scm +39 -39
- package/src/cli/tui/syntax/tree-sitter/tsx.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/typescript.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/yaml.highlights.scm +99 -99
- package/src/cli/tui/tool-rendering.mjs +87 -87
- package/src/cli/tui/tui-diff-rendering.mjs +157 -157
- package/src/cli/tui/tui-handlers.mjs +111 -111
- package/src/cli/tui/tui-input-controller.mjs +61 -61
- package/src/cli/tui/ui-theme.mjs +157 -157
- package/src/cli/ui.mjs +297 -297
- package/src/config/config-json.mjs +108 -84
- package/src/config/dotenv.mjs +20 -20
- package/src/config/features.mjs +75 -75
- package/src/config/loader.mjs +156 -143
- package/src/config/settings-command.mjs +97 -97
- package/src/context/engine.mjs +199 -198
- package/src/context/injections.mjs +26 -26
- package/src/context/profiles.mjs +39 -39
- package/src/context/project-context.mjs +20 -20
- package/src/context/session-status.mjs +25 -17
- package/src/context/shell-layers.mjs +23 -23
- package/src/context/system-core/base.md +51 -50
- package/src/context/system-core/prompts/deepseek-v4-pro.md +3 -3
- package/src/context/system-core/prompts/default.md +3 -3
- package/src/context/system-core.mjs +35 -35
- package/src/debug/logger.mjs +141 -141
- package/src/debug/model-context-dumper.mjs +52 -52
- package/src/extensions/discovery.mjs +40 -40
- package/src/extensions/lifecycle-adapter.mjs +210 -210
- package/src/extensions/lifecycle-manifest.mjs +69 -69
- package/src/image-gen/index.mjs +7 -7
- package/src/image-gen/provider.mjs +231 -231
- package/src/image-gen/tool.mjs +84 -84
- package/src/lsp/client.mjs +257 -257
- package/src/lsp/diagnostic-store.mjs +42 -42
- package/src/lsp/diagnostics-format.mjs +72 -72
- package/src/lsp/managed-node-server.mjs +99 -99
- package/src/lsp/path-match.mjs +10 -10
- package/src/lsp/server-definitions.mjs +188 -188
- package/src/lsp/servers.mjs +165 -165
- package/src/lsp/service.mjs +110 -110
- package/src/lsp/status-message.mjs +9 -9
- package/src/lsp/typescript-project-resolver.mjs +186 -186
- package/src/main.mjs +294 -299
- package/src/mcp/client.mjs +195 -195
- package/src/mcp/config.mjs +130 -130
- package/src/mcp/index.mjs +48 -48
- package/src/mcp/tools.mjs +98 -98
- package/src/memory/command.mjs +120 -0
- package/src/memory/markdown/markdown-delete.mjs +23 -23
- package/src/memory/markdown/markdown-format.mjs +128 -128
- package/src/memory/markdown/markdown-recall.mjs +28 -28
- package/src/memory/markdown/ripgrep.mjs +16 -16
- package/src/memory/markdown/sqlite-index.mjs +87 -87
- package/src/memory/markdown-store.mjs +272 -286
- package/src/memory/markdown-tools.mjs +174 -103
- package/src/memory/remote/client.mjs +68 -0
- package/src/memory/remote/config.mjs +52 -0
- package/src/memory/remote/server.mjs +99 -0
- package/src/memory/search.mjs +183 -0
- package/src/network/environment.mjs +131 -131
- package/src/notification/desktop-notifier.mjs +262 -262
- package/src/platform/open-file.mjs +28 -28
- package/src/platform/spawn-command.mjs +27 -27
- package/src/provider/accept-command.mjs +89 -89
- package/src/provider/command.mjs +21 -21
- package/src/provider/config-command.mjs +129 -129
- package/src/provider/custom-provider.mjs +113 -113
- package/src/provider/hosted-tools.mjs +111 -111
- package/src/provider/presets.mjs +72 -72
- package/src/provider/share-command.mjs +79 -79
- package/src/provider/share-payload.mjs +52 -52
- package/src/session/attachment-display.mjs +16 -16
- package/src/session/attachment-references.mjs +65 -65
- package/src/session/attachments.mjs +140 -140
- package/src/session/persist.mjs +1 -1
- package/src/session/pi-manager.mjs +34 -34
- package/src/session/session-utils.mjs +16 -16
- package/src/session/sidecar-sync.mjs +19 -19
- package/src/session/sidecar.mjs +69 -69
- package/src/session/transcript.mjs +83 -83
- package/src/session/tree.mjs +42 -42
- package/src/shell/cli-runtime.mjs +11 -11
- package/src/shell/hints.mjs +12 -12
- package/src/shell/node-pty-adapter.mjs +81 -81
- package/src/shell/runtime-state.mjs +126 -126
- package/src/shell/runtime.mjs +252 -252
- package/src/shell/screen-buffer.mjs +136 -136
- package/src/shell/tool-read.mjs +74 -74
- package/src/shell/tools.mjs +299 -299
- package/src/supergrok/actions/image-generate.mjs +60 -60
- package/src/supergrok/actions/search.mjs +78 -78
- package/src/supergrok/auth.mjs +36 -36
- package/src/supergrok/constants.mjs +18 -18
- package/src/supergrok/oauth-provider.mjs +278 -278
- package/src/supergrok/provider.mjs +35 -35
- package/src/supergrok/response.mjs +76 -76
- package/src/supergrok/tool.mjs +61 -61
- package/src/text/ansi.mjs +3 -3
- package/src/web/config-command.mjs +43 -43
- package/src/web/fetch.mjs +78 -78
- package/src/web/presets.mjs +16 -16
- package/src/web/search.mjs +83 -83
- package/src/web/tools.mjs +107 -107
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
import { formatRecallHints } from "../../memory/markdown-store.mjs";
|
|
2
|
-
import { resolveImageAttachmentReferences } from "../../session/attachment-references.mjs";
|
|
3
|
-
import { closeAssistantReply, compactAssistantContext, createTurnEventState, handleRunnerSessionEvent } from "./turn-events.mjs";
|
|
4
|
-
|
|
5
|
-
export async function runRunnerTurn({
|
|
6
|
-
prompt,
|
|
7
|
-
userMessage,
|
|
8
|
-
options = {},
|
|
9
|
-
sessionBinding,
|
|
10
|
-
engine,
|
|
11
|
-
ui,
|
|
12
|
-
projectMarchDir,
|
|
13
|
-
memoryStore,
|
|
14
|
-
setModelCallKind,
|
|
15
|
-
logger = null,
|
|
16
|
-
setPhase = null,
|
|
17
|
-
syncCurrentPiSidecar,
|
|
18
|
-
autoNameSession,
|
|
19
|
-
contextMode = "rebuild",
|
|
20
|
-
}) {
|
|
21
|
-
const {
|
|
22
|
-
userRecallHints = [],
|
|
23
|
-
currentProject = "",
|
|
24
|
-
} = options;
|
|
25
|
-
const activeSession = sessionBinding.get();
|
|
26
|
-
const turnState = createTurnEventState();
|
|
27
|
-
const midTurnRecallHints = [];
|
|
28
|
-
ui.turnStart();
|
|
29
|
-
setPhase?.("subscribed");
|
|
30
|
-
logger?.event("turn.ui.start");
|
|
31
|
-
|
|
32
|
-
const unsubscribe = activeSession.subscribe((event) => {
|
|
33
|
-
logSessionEvent(logger, event);
|
|
34
|
-
if (event.type === "tool_execution_start") setPhase?.(`tool_running:${event.toolName ?? "unknown"}`);
|
|
35
|
-
if (event.type === "tool_execution_end") setPhase?.("model_streaming");
|
|
36
|
-
if (event.type === "auto_retry_start") setPhase?.("retry_wait");
|
|
37
|
-
if (event.type === "auto_retry_end") setPhase?.("model_streaming");
|
|
38
|
-
if (event.type === "message_update") setPhase?.("model_streaming");
|
|
39
|
-
handleRunnerSessionEvent(event, { ui, engine, state: turnState });
|
|
40
|
-
if (event.type === "tool_execution_start") {
|
|
41
|
-
const hints = flushAssistantRecall({ memoryStore, engine, turnState, currentProject });
|
|
42
|
-
if (hints.length > 0) {
|
|
43
|
-
midTurnRecallHints.push(...hints);
|
|
44
|
-
queueMidTurnRecallHints(activeSession, hints, logger);
|
|
45
|
-
ui.memoryHint?.({ source: "assistant", hints });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const attachmentReferences = resolveImageAttachmentReferences({
|
|
52
|
-
text: userMessage ?? prompt,
|
|
53
|
-
projectMarchDir,
|
|
54
|
-
});
|
|
55
|
-
logger?.event("turn.attachments.resolved", { imageCount: attachmentReferences.images.length });
|
|
56
|
-
setModelCallKind("user");
|
|
57
|
-
setPhase?.("model_request");
|
|
58
|
-
logger?.event("model.prompt.start", { contextMode });
|
|
59
|
-
try {
|
|
60
|
-
if (contextMode === "rebuild") resetPiMessageHistory(activeSession);
|
|
61
|
-
await activeSession.prompt(
|
|
62
|
-
contextMode === "continueExistingPiTranscript" ? (userMessage ?? prompt) : prompt,
|
|
63
|
-
attachmentReferences.images.length > 0 ? { images: attachmentReferences.images } : undefined,
|
|
64
|
-
);
|
|
65
|
-
} finally {
|
|
66
|
-
setModelCallKind("model");
|
|
67
|
-
logger?.event("model.prompt.end");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
setPhase?.("finalizing");
|
|
71
|
-
finalizeTurn({
|
|
72
|
-
prompt,
|
|
73
|
-
userMessage,
|
|
74
|
-
userRecallHints,
|
|
75
|
-
currentProject,
|
|
76
|
-
memoryStore,
|
|
77
|
-
engine,
|
|
78
|
-
ui,
|
|
79
|
-
turnState,
|
|
80
|
-
midTurnRecallHints,
|
|
81
|
-
syncCurrentPiSidecar,
|
|
82
|
-
autoNameSession,
|
|
83
|
-
});
|
|
84
|
-
return { draft: turnState.draft };
|
|
85
|
-
} finally {
|
|
86
|
-
logger?.event("turn.ui.end");
|
|
87
|
-
ui.turnEnd();
|
|
88
|
-
unsubscribe();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
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
|
-
|
|
106
|
-
function logSessionEvent(logger, event) {
|
|
107
|
-
if (!logger) return;
|
|
108
|
-
if (event.type === "message_update") {
|
|
109
|
-
const messageEvent = event.assistantMessageEvent;
|
|
110
|
-
logger.debug("session.event", {
|
|
111
|
-
type: event.type,
|
|
112
|
-
assistantMessageType: messageEvent?.type,
|
|
113
|
-
deltaLength: messageEvent?.delta?.length,
|
|
114
|
-
});
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
logger.event("session.event", {
|
|
118
|
-
type: event.type,
|
|
119
|
-
toolName: event.toolName,
|
|
120
|
-
isError: event.isError,
|
|
121
|
-
attempt: event.attempt,
|
|
122
|
-
maxAttempts: event.maxAttempts,
|
|
123
|
-
delayMs: event.delayMs,
|
|
124
|
-
success: event.success,
|
|
125
|
-
errorMessage: event.errorMessage,
|
|
126
|
-
finalError: event.finalError,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function finalizeTurn({ prompt, userMessage, userRecallHints, currentProject, memoryStore, engine, ui, turnState, midTurnRecallHints, syncCurrentPiSidecar, autoNameSession }) {
|
|
131
|
-
closeAssistantReply({ ui, state: turnState });
|
|
132
|
-
const assistantRecallHints = flushAssistantRecall({ memoryStore, engine, turnState, currentProject });
|
|
133
|
-
engine.setPendingAssistantRecallHints?.(assistantRecallHints);
|
|
134
|
-
const recordedAssistantRecallHints = uniqueHints([...midTurnRecallHints, ...assistantRecallHints]);
|
|
135
|
-
|
|
136
|
-
engine.recordTurn({
|
|
137
|
-
userMessage: userMessage ?? prompt.slice(0, 300),
|
|
138
|
-
assistantMessage: turnState.draft,
|
|
139
|
-
assistantContext: compactAssistantContext(turnState),
|
|
140
|
-
userRecallHints,
|
|
141
|
-
assistantRecallHints: recordedAssistantRecallHints,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
autoNameSession?.();
|
|
145
|
-
syncCurrentPiSidecar();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function flushAssistantRecall({ memoryStore, engine, turnState, currentProject }) {
|
|
149
|
-
if (!memoryStore) return [];
|
|
150
|
-
const text = assistantRecallDeltaText(turnState);
|
|
151
|
-
advanceAssistantRecallCursor(turnState);
|
|
152
|
-
if (!text.trim()) return [];
|
|
153
|
-
return memoryStore.recallForAssistant(text, {
|
|
154
|
-
currentProject,
|
|
155
|
-
excludedIds: engine.getRecentRecallMemoryIds?.() ?? [],
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function assistantRecallDeltaText(turnState) {
|
|
160
|
-
const cursor = turnState.recallCursor ?? { draftLength: 0, thinkingLength: 0 };
|
|
161
|
-
const thinking = assistantThinkingText(turnState);
|
|
162
|
-
return [
|
|
163
|
-
turnState.draft.slice(cursor.draftLength),
|
|
164
|
-
thinking.slice(cursor.thinkingLength),
|
|
165
|
-
]
|
|
166
|
-
.filter(Boolean)
|
|
167
|
-
.join("\n");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function advanceAssistantRecallCursor(turnState) {
|
|
171
|
-
turnState.recallCursor = {
|
|
172
|
-
draftLength: turnState.draft.length,
|
|
173
|
-
thinkingLength: assistantThinkingText(turnState).length,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function assistantThinkingText(turnState) {
|
|
178
|
-
return `${turnState.thinkingAccumulator}${turnState.thinkingText}`;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function uniqueHints(hints) {
|
|
182
|
-
const seen = new Set();
|
|
183
|
-
const unique = [];
|
|
184
|
-
for (const hint of hints) {
|
|
185
|
-
if (!hint?.id || seen.has(hint.id)) continue;
|
|
186
|
-
seen.add(hint.id);
|
|
187
|
-
unique.push(hint);
|
|
188
|
-
}
|
|
189
|
-
return unique;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function resetPiMessageHistory(session) {
|
|
193
|
-
if (Array.isArray(session?.agent?.state?.messages)) {
|
|
194
|
-
session.agent.state.messages = [];
|
|
195
|
-
}
|
|
196
|
-
}
|
|
1
|
+
import { formatRecallHints } from "../../memory/markdown-store.mjs";
|
|
2
|
+
import { resolveImageAttachmentReferences } from "../../session/attachment-references.mjs";
|
|
3
|
+
import { closeAssistantReply, compactAssistantContext, createTurnEventState, handleRunnerSessionEvent } from "./turn-events.mjs";
|
|
4
|
+
|
|
5
|
+
export async function runRunnerTurn({
|
|
6
|
+
prompt,
|
|
7
|
+
userMessage,
|
|
8
|
+
options = {},
|
|
9
|
+
sessionBinding,
|
|
10
|
+
engine,
|
|
11
|
+
ui,
|
|
12
|
+
projectMarchDir,
|
|
13
|
+
memoryStore,
|
|
14
|
+
setModelCallKind,
|
|
15
|
+
logger = null,
|
|
16
|
+
setPhase = null,
|
|
17
|
+
syncCurrentPiSidecar,
|
|
18
|
+
autoNameSession,
|
|
19
|
+
contextMode = "rebuild",
|
|
20
|
+
}) {
|
|
21
|
+
const {
|
|
22
|
+
userRecallHints = [],
|
|
23
|
+
currentProject = "",
|
|
24
|
+
} = options;
|
|
25
|
+
const activeSession = sessionBinding.get();
|
|
26
|
+
const turnState = createTurnEventState();
|
|
27
|
+
const midTurnRecallHints = [];
|
|
28
|
+
ui.turnStart();
|
|
29
|
+
setPhase?.("subscribed");
|
|
30
|
+
logger?.event("turn.ui.start");
|
|
31
|
+
|
|
32
|
+
const unsubscribe = activeSession.subscribe((event) => {
|
|
33
|
+
logSessionEvent(logger, event);
|
|
34
|
+
if (event.type === "tool_execution_start") setPhase?.(`tool_running:${event.toolName ?? "unknown"}`);
|
|
35
|
+
if (event.type === "tool_execution_end") setPhase?.("model_streaming");
|
|
36
|
+
if (event.type === "auto_retry_start") setPhase?.("retry_wait");
|
|
37
|
+
if (event.type === "auto_retry_end") setPhase?.("model_streaming");
|
|
38
|
+
if (event.type === "message_update") setPhase?.("model_streaming");
|
|
39
|
+
handleRunnerSessionEvent(event, { ui, engine, state: turnState });
|
|
40
|
+
if (event.type === "tool_execution_start") {
|
|
41
|
+
const hints = flushAssistantRecall({ memoryStore, engine, turnState, currentProject });
|
|
42
|
+
if (hints.length > 0) {
|
|
43
|
+
midTurnRecallHints.push(...hints);
|
|
44
|
+
queueMidTurnRecallHints(activeSession, hints, logger);
|
|
45
|
+
ui.memoryHint?.({ source: "assistant", hints });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const attachmentReferences = resolveImageAttachmentReferences({
|
|
52
|
+
text: userMessage ?? prompt,
|
|
53
|
+
projectMarchDir,
|
|
54
|
+
});
|
|
55
|
+
logger?.event("turn.attachments.resolved", { imageCount: attachmentReferences.images.length });
|
|
56
|
+
setModelCallKind("user");
|
|
57
|
+
setPhase?.("model_request");
|
|
58
|
+
logger?.event("model.prompt.start", { contextMode });
|
|
59
|
+
try {
|
|
60
|
+
if (contextMode === "rebuild") resetPiMessageHistory(activeSession);
|
|
61
|
+
await activeSession.prompt(
|
|
62
|
+
contextMode === "continueExistingPiTranscript" ? (userMessage ?? prompt) : prompt,
|
|
63
|
+
attachmentReferences.images.length > 0 ? { images: attachmentReferences.images } : undefined,
|
|
64
|
+
);
|
|
65
|
+
} finally {
|
|
66
|
+
setModelCallKind("model");
|
|
67
|
+
logger?.event("model.prompt.end");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setPhase?.("finalizing");
|
|
71
|
+
finalizeTurn({
|
|
72
|
+
prompt,
|
|
73
|
+
userMessage,
|
|
74
|
+
userRecallHints,
|
|
75
|
+
currentProject,
|
|
76
|
+
memoryStore,
|
|
77
|
+
engine,
|
|
78
|
+
ui,
|
|
79
|
+
turnState,
|
|
80
|
+
midTurnRecallHints,
|
|
81
|
+
syncCurrentPiSidecar,
|
|
82
|
+
autoNameSession,
|
|
83
|
+
});
|
|
84
|
+
return { draft: turnState.draft };
|
|
85
|
+
} finally {
|
|
86
|
+
logger?.event("turn.ui.end");
|
|
87
|
+
ui.turnEnd();
|
|
88
|
+
unsubscribe();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
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
|
+
|
|
106
|
+
function logSessionEvent(logger, event) {
|
|
107
|
+
if (!logger) return;
|
|
108
|
+
if (event.type === "message_update") {
|
|
109
|
+
const messageEvent = event.assistantMessageEvent;
|
|
110
|
+
logger.debug("session.event", {
|
|
111
|
+
type: event.type,
|
|
112
|
+
assistantMessageType: messageEvent?.type,
|
|
113
|
+
deltaLength: messageEvent?.delta?.length,
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
logger.event("session.event", {
|
|
118
|
+
type: event.type,
|
|
119
|
+
toolName: event.toolName,
|
|
120
|
+
isError: event.isError,
|
|
121
|
+
attempt: event.attempt,
|
|
122
|
+
maxAttempts: event.maxAttempts,
|
|
123
|
+
delayMs: event.delayMs,
|
|
124
|
+
success: event.success,
|
|
125
|
+
errorMessage: event.errorMessage,
|
|
126
|
+
finalError: event.finalError,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function finalizeTurn({ prompt, userMessage, userRecallHints, currentProject, memoryStore, engine, ui, turnState, midTurnRecallHints, syncCurrentPiSidecar, autoNameSession }) {
|
|
131
|
+
closeAssistantReply({ ui, state: turnState });
|
|
132
|
+
const assistantRecallHints = flushAssistantRecall({ memoryStore, engine, turnState, currentProject });
|
|
133
|
+
engine.setPendingAssistantRecallHints?.(assistantRecallHints);
|
|
134
|
+
const recordedAssistantRecallHints = uniqueHints([...midTurnRecallHints, ...assistantRecallHints]);
|
|
135
|
+
|
|
136
|
+
engine.recordTurn({
|
|
137
|
+
userMessage: userMessage ?? prompt.slice(0, 300),
|
|
138
|
+
assistantMessage: turnState.draft,
|
|
139
|
+
assistantContext: compactAssistantContext(turnState),
|
|
140
|
+
userRecallHints,
|
|
141
|
+
assistantRecallHints: recordedAssistantRecallHints,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
autoNameSession?.();
|
|
145
|
+
syncCurrentPiSidecar();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function flushAssistantRecall({ memoryStore, engine, turnState, currentProject }) {
|
|
149
|
+
if (!memoryStore) return [];
|
|
150
|
+
const text = assistantRecallDeltaText(turnState);
|
|
151
|
+
advanceAssistantRecallCursor(turnState);
|
|
152
|
+
if (!text.trim()) return [];
|
|
153
|
+
return memoryStore.recallForAssistant(text, {
|
|
154
|
+
currentProject,
|
|
155
|
+
excludedIds: engine.getRecentRecallMemoryIds?.() ?? [],
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function assistantRecallDeltaText(turnState) {
|
|
160
|
+
const cursor = turnState.recallCursor ?? { draftLength: 0, thinkingLength: 0 };
|
|
161
|
+
const thinking = assistantThinkingText(turnState);
|
|
162
|
+
return [
|
|
163
|
+
turnState.draft.slice(cursor.draftLength),
|
|
164
|
+
thinking.slice(cursor.thinkingLength),
|
|
165
|
+
]
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
.join("\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function advanceAssistantRecallCursor(turnState) {
|
|
171
|
+
turnState.recallCursor = {
|
|
172
|
+
draftLength: turnState.draft.length,
|
|
173
|
+
thinkingLength: assistantThinkingText(turnState).length,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function assistantThinkingText(turnState) {
|
|
178
|
+
return `${turnState.thinkingAccumulator}${turnState.thinkingText}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function uniqueHints(hints) {
|
|
182
|
+
const seen = new Set();
|
|
183
|
+
const unique = [];
|
|
184
|
+
for (const hint of hints) {
|
|
185
|
+
if (!hint?.id || seen.has(hint.id)) continue;
|
|
186
|
+
seen.add(hint.id);
|
|
187
|
+
unique.push(hint);
|
|
188
|
+
}
|
|
189
|
+
return unique;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function resetPiMessageHistory(session) {
|
|
193
|
+
if (Array.isArray(session?.agent?.state?.messages)) {
|
|
194
|
+
session.agent.state.messages = [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export function modelSupportsImageInput(model) {
|
|
2
|
-
if (!model || typeof model !== "object") return false;
|
|
3
|
-
if (Array.isArray(model.input) && model.input.includes("image")) return true;
|
|
4
|
-
if (model.capabilities?.images === true || model.capabilities?.vision === true) return true;
|
|
5
|
-
return false;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function currentModelImageInputError(getCurrentModel) {
|
|
9
|
-
if (typeof getCurrentModel !== "function") return null;
|
|
10
|
-
const model = getCurrentModel();
|
|
11
|
-
if (modelSupportsImageInput(model)) return null;
|
|
12
|
-
const label = model ? `${model.name || model.id || "unknown"} (${model.provider || "unknown provider"})` : "unknown";
|
|
13
|
-
return `Current model does not support image input: ${label}. Switch to a vision-capable model before using read_image or screen.`;
|
|
14
|
-
}
|
|
1
|
+
export function modelSupportsImageInput(model) {
|
|
2
|
+
if (!model || typeof model !== "object") return false;
|
|
3
|
+
if (Array.isArray(model.input) && model.input.includes("image")) return true;
|
|
4
|
+
if (model.capabilities?.images === true || model.capabilities?.vision === true) return true;
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function currentModelImageInputError(getCurrentModel) {
|
|
9
|
+
if (typeof getCurrentModel !== "function") return null;
|
|
10
|
+
const model = getCurrentModel();
|
|
11
|
+
if (modelSupportsImageInput(model)) return null;
|
|
12
|
+
const label = model ? `${model.name || model.id || "unknown"} (${model.provider || "unknown provider"})` : "unknown";
|
|
13
|
+
return `Current model does not support image input: ${label}. Switch to a vision-capable model before using read_image or screen.`;
|
|
14
|
+
}
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import { createInterface } from "node:readline";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { AuthStorage } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { getMarchAuthPath } from "./storage.mjs";
|
|
5
|
-
|
|
6
|
-
export async function runLoginCommand({
|
|
7
|
-
providerId,
|
|
8
|
-
homeDir = homedir(),
|
|
9
|
-
authPath = getMarchAuthPath(homeDir),
|
|
10
|
-
authStorage = AuthStorage.create(authPath),
|
|
11
|
-
input = process.stdin,
|
|
12
|
-
output = process.stdout,
|
|
13
|
-
} = {}) {
|
|
14
|
-
const providers = authStorage.getOAuthProviders?.() ?? [];
|
|
15
|
-
const selectedProvider = providerId || await selectProvider({ providers, input, output });
|
|
16
|
-
if (!selectedProvider) {
|
|
17
|
-
output.write("Login cancelled.\n");
|
|
18
|
-
return 1;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!providers.some((provider) => provider.id === selectedProvider)) {
|
|
22
|
-
output.write(`Unknown OAuth provider: ${selectedProvider}\n`);
|
|
23
|
-
output.write(formatProviderList(providers));
|
|
24
|
-
return 1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const rl = createInterface({ input, output });
|
|
28
|
-
try {
|
|
29
|
-
output.write(`Logging in to ${selectedProvider}...\n`);
|
|
30
|
-
await authStorage.login(selectedProvider, {
|
|
31
|
-
onAuth: (info) => {
|
|
32
|
-
output.write(`\nOpen this URL in your browser:\n${info.url}\n`);
|
|
33
|
-
if (info.instructions) output.write(`${info.instructions}\n`);
|
|
34
|
-
output.write("\n");
|
|
35
|
-
},
|
|
36
|
-
onPrompt: (prompt) => ask(rl, `${prompt.message}${prompt.placeholder ? ` (${prompt.placeholder})` : ""}: `),
|
|
37
|
-
onManualCodeInput: () => ask(rl, "Paste redirect URL or code: "),
|
|
38
|
-
onProgress: (message) => output.write(`${message}\n`),
|
|
39
|
-
onSelect: async (prompt) => selectOption({ prompt, rl, output }),
|
|
40
|
-
});
|
|
41
|
-
output.write(`\nCredentials saved to ${authPath}\n`);
|
|
42
|
-
return 0;
|
|
43
|
-
} finally {
|
|
44
|
-
rl.close();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function formatProviderList(providers) {
|
|
49
|
-
if (!providers.length) return "No OAuth providers are available.\n";
|
|
50
|
-
const lines = ["Available OAuth providers:"];
|
|
51
|
-
for (const provider of providers) {
|
|
52
|
-
lines.push(` ${provider.id.padEnd(20)} ${provider.name}`);
|
|
53
|
-
}
|
|
54
|
-
return `${lines.join("\n")}\n`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function selectProvider({ providers, input, output }) {
|
|
58
|
-
if (providers.length === 1) return providers[0].id;
|
|
59
|
-
output.write(`${formatProviderList(providers)}\n`);
|
|
60
|
-
const rl = createInterface({ input, output });
|
|
61
|
-
try {
|
|
62
|
-
const answer = await ask(rl, `Enter provider id or number (1-${providers.length}): `);
|
|
63
|
-
return resolveSelection(answer, providers.map((provider) => ({ id: provider.id, label: provider.name })));
|
|
64
|
-
} finally {
|
|
65
|
-
rl.close();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function selectOption({ prompt, rl, output }) {
|
|
70
|
-
output.write(`${prompt.message}\n`);
|
|
71
|
-
for (let i = 0; i < prompt.options.length; i++) {
|
|
72
|
-
const option = prompt.options[i];
|
|
73
|
-
output.write(` ${i + 1}. ${option.label} (${option.id})\n`);
|
|
74
|
-
}
|
|
75
|
-
const answer = await ask(rl, `Enter option id or number (1-${prompt.options.length}): `);
|
|
76
|
-
return resolveSelection(answer, prompt.options);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function resolveSelection(answer, options) {
|
|
80
|
-
const trimmed = answer.trim();
|
|
81
|
-
const index = Number.parseInt(trimmed, 10);
|
|
82
|
-
if (Number.isInteger(index) && String(index) === trimmed && index >= 1 && index <= options.length) {
|
|
83
|
-
return options[index - 1].id;
|
|
84
|
-
}
|
|
85
|
-
return options.some((option) => option.id === trimmed) ? trimmed : undefined;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function ask(rl, question) {
|
|
89
|
-
return new Promise((resolve) => rl.question(question, resolve));
|
|
90
|
-
}
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { AuthStorage } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { getMarchAuthPath } from "./storage.mjs";
|
|
5
|
+
|
|
6
|
+
export async function runLoginCommand({
|
|
7
|
+
providerId,
|
|
8
|
+
homeDir = homedir(),
|
|
9
|
+
authPath = getMarchAuthPath(homeDir),
|
|
10
|
+
authStorage = AuthStorage.create(authPath),
|
|
11
|
+
input = process.stdin,
|
|
12
|
+
output = process.stdout,
|
|
13
|
+
} = {}) {
|
|
14
|
+
const providers = authStorage.getOAuthProviders?.() ?? [];
|
|
15
|
+
const selectedProvider = providerId || await selectProvider({ providers, input, output });
|
|
16
|
+
if (!selectedProvider) {
|
|
17
|
+
output.write("Login cancelled.\n");
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!providers.some((provider) => provider.id === selectedProvider)) {
|
|
22
|
+
output.write(`Unknown OAuth provider: ${selectedProvider}\n`);
|
|
23
|
+
output.write(formatProviderList(providers));
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rl = createInterface({ input, output });
|
|
28
|
+
try {
|
|
29
|
+
output.write(`Logging in to ${selectedProvider}...\n`);
|
|
30
|
+
await authStorage.login(selectedProvider, {
|
|
31
|
+
onAuth: (info) => {
|
|
32
|
+
output.write(`\nOpen this URL in your browser:\n${info.url}\n`);
|
|
33
|
+
if (info.instructions) output.write(`${info.instructions}\n`);
|
|
34
|
+
output.write("\n");
|
|
35
|
+
},
|
|
36
|
+
onPrompt: (prompt) => ask(rl, `${prompt.message}${prompt.placeholder ? ` (${prompt.placeholder})` : ""}: `),
|
|
37
|
+
onManualCodeInput: () => ask(rl, "Paste redirect URL or code: "),
|
|
38
|
+
onProgress: (message) => output.write(`${message}\n`),
|
|
39
|
+
onSelect: async (prompt) => selectOption({ prompt, rl, output }),
|
|
40
|
+
});
|
|
41
|
+
output.write(`\nCredentials saved to ${authPath}\n`);
|
|
42
|
+
return 0;
|
|
43
|
+
} finally {
|
|
44
|
+
rl.close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function formatProviderList(providers) {
|
|
49
|
+
if (!providers.length) return "No OAuth providers are available.\n";
|
|
50
|
+
const lines = ["Available OAuth providers:"];
|
|
51
|
+
for (const provider of providers) {
|
|
52
|
+
lines.push(` ${provider.id.padEnd(20)} ${provider.name}`);
|
|
53
|
+
}
|
|
54
|
+
return `${lines.join("\n")}\n`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function selectProvider({ providers, input, output }) {
|
|
58
|
+
if (providers.length === 1) return providers[0].id;
|
|
59
|
+
output.write(`${formatProviderList(providers)}\n`);
|
|
60
|
+
const rl = createInterface({ input, output });
|
|
61
|
+
try {
|
|
62
|
+
const answer = await ask(rl, `Enter provider id or number (1-${providers.length}): `);
|
|
63
|
+
return resolveSelection(answer, providers.map((provider) => ({ id: provider.id, label: provider.name })));
|
|
64
|
+
} finally {
|
|
65
|
+
rl.close();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function selectOption({ prompt, rl, output }) {
|
|
70
|
+
output.write(`${prompt.message}\n`);
|
|
71
|
+
for (let i = 0; i < prompt.options.length; i++) {
|
|
72
|
+
const option = prompt.options[i];
|
|
73
|
+
output.write(` ${i + 1}. ${option.label} (${option.id})\n`);
|
|
74
|
+
}
|
|
75
|
+
const answer = await ask(rl, `Enter option id or number (1-${prompt.options.length}): `);
|
|
76
|
+
return resolveSelection(answer, prompt.options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveSelection(answer, options) {
|
|
80
|
+
const trimmed = answer.trim();
|
|
81
|
+
const index = Number.parseInt(trimmed, 10);
|
|
82
|
+
if (Number.isInteger(index) && String(index) === trimmed && index >= 1 && index <= options.length) {
|
|
83
|
+
return options[index - 1].id;
|
|
84
|
+
}
|
|
85
|
+
return options.some((option) => option.id === trimmed) ? trimmed : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ask(rl, question) {
|
|
89
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
90
|
+
}
|