ralph-flow-pi 0.1.0
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/LICENSE +21 -0
- package/README.md +428 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/prompts.d.ts +25 -0
- package/dist/commands/prompts.d.ts.map +1 -0
- package/dist/commands/prompts.js +249 -0
- package/dist/commands/prompts.js.map +1 -0
- package/dist/commands/tools.d.ts +47 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +633 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/engine/check-bash.d.ts +121 -0
- package/dist/engine/check-bash.d.ts.map +1 -0
- package/dist/engine/check-bash.js +373 -0
- package/dist/engine/check-bash.js.map +1 -0
- package/dist/engine/check.d.ts +47 -0
- package/dist/engine/check.d.ts.map +1 -0
- package/dist/engine/check.js +298 -0
- package/dist/engine/check.js.map +1 -0
- package/dist/engine/core.d.ts +153 -0
- package/dist/engine/core.d.ts.map +1 -0
- package/dist/engine/core.js +1984 -0
- package/dist/engine/core.js.map +1 -0
- package/dist/engine/lock.d.ts +27 -0
- package/dist/engine/lock.d.ts.map +1 -0
- package/dist/engine/lock.js +121 -0
- package/dist/engine/lock.js.map +1 -0
- package/dist/engine/runner.d.ts +108 -0
- package/dist/engine/runner.d.ts.map +1 -0
- package/dist/engine/runner.js +510 -0
- package/dist/engine/runner.js.map +1 -0
- package/dist/engine/skills.d.ts +53 -0
- package/dist/engine/skills.d.ts.map +1 -0
- package/dist/engine/skills.js +109 -0
- package/dist/engine/skills.js.map +1 -0
- package/dist/engine/step-tools.d.ts +22 -0
- package/dist/engine/step-tools.d.ts.map +1 -0
- package/dist/engine/step-tools.js +45 -0
- package/dist/engine/step-tools.js.map +1 -0
- package/dist/engine/types.d.ts +136 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +20 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/headless.d.ts +57 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +318 -0
- package/dist/headless.js.map +1 -0
- package/dist/pi/adapter.d.ts +135 -0
- package/dist/pi/adapter.d.ts.map +1 -0
- package/dist/pi/adapter.js +231 -0
- package/dist/pi/adapter.js.map +1 -0
- package/dist/pi/interactive.d.ts +28 -0
- package/dist/pi/interactive.d.ts.map +1 -0
- package/dist/pi/interactive.js +58 -0
- package/dist/pi/interactive.js.map +1 -0
- package/dist/pi/tui.d.ts +12 -0
- package/dist/pi/tui.d.ts.map +1 -0
- package/dist/pi/tui.js +12 -0
- package/dist/pi/tui.js.map +1 -0
- package/dist/tui/app.d.ts +25 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +47 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/embed.d.ts +42 -0
- package/dist/tui/embed.d.ts.map +1 -0
- package/dist/tui/embed.js +38 -0
- package/dist/tui/embed.js.map +1 -0
- package/dist/tui/extension.d.ts +88 -0
- package/dist/tui/extension.d.ts.map +1 -0
- package/dist/tui/extension.js +114 -0
- package/dist/tui/extension.js.map +1 -0
- package/dist/tui/history-editor.d.ts +38 -0
- package/dist/tui/history-editor.d.ts.map +1 -0
- package/dist/tui/history-editor.js +55 -0
- package/dist/tui/history-editor.js.map +1 -0
- package/dist/tui/launcher.d.ts +24 -0
- package/dist/tui/launcher.d.ts.map +1 -0
- package/dist/tui/launcher.js +97 -0
- package/dist/tui/launcher.js.map +1 -0
- package/dist/tui/render.d.ts +87 -0
- package/dist/tui/render.d.ts.map +1 -0
- package/dist/tui/render.js +266 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/run-app.d.ts +49 -0
- package/dist/tui/run-app.d.ts.map +1 -0
- package/dist/tui/run-app.js +317 -0
- package/dist/tui/run-app.js.map +1 -0
- package/dist/tui/run-model.d.ts +162 -0
- package/dist/tui/run-model.d.ts.map +1 -0
- package/dist/tui/run-model.js +280 -0
- package/dist/tui/run-model.js.map +1 -0
- package/dist/tui/run-view.d.ts +71 -0
- package/dist/tui/run-view.d.ts.map +1 -0
- package/dist/tui/run-view.js +167 -0
- package/dist/tui/run-view.js.map +1 -0
- package/dist/tui/welcome-header.d.ts +40 -0
- package/dist/tui/welcome-header.d.ts.map +1 -0
- package/dist/tui/welcome-header.js +90 -0
- package/dist/tui/welcome-header.js.map +1 -0
- package/package.json +55 -0
- package/skills/c-to-rust-audit/SKILL.md +67 -0
- package/skills/c-to-rust-implement/SKILL.md +151 -0
- package/skills/c-to-rust-implement/references/c-to-rust-patterns.md +86 -0
- package/skills/c-to-rust-implement/references/conditional-compilation.md +47 -0
- package/skills/c-to-rust-implement/references/crate-reference.md +15 -0
- package/skills/c-to-rust-implement/references/error-strategies.md +80 -0
- package/skills/c-to-rust-implement/references/inline-asm.md +37 -0
- package/skills/c-to-rust-plan/SKILL.md +166 -0
- package/skills/c-to-rust-plan/references/detection-commands.md +66 -0
- package/skills/c-to-rust-test-gen/SKILL.md +130 -0
- package/skills/c-to-rust-test-gen/references/proptest-patterns.md +81 -0
- package/skills/c-to-rust-test-gen/references/test-porting.md +56 -0
- package/skills/c-to-rust-validate/SKILL.md +121 -0
- package/skills/everything2rust-audit/SKILL.md +69 -0
- package/skills/everything2rust-design/SKILL.md +121 -0
- package/skills/everything2rust-design/references/domain-playbooks.md +68 -0
- package/skills/everything2rust-design/references/paradigm-map.md +99 -0
- package/skills/everything2rust-implement/SKILL.md +101 -0
- package/skills/everything2rust-spec/SKILL.md +86 -0
- package/skills/everything2rust-spec/references/oracle-strategies.md +96 -0
- package/skills/everything2rust-survey/SKILL.md +99 -0
- package/skills/everything2rust-test-gen/SKILL.md +68 -0
- package/skills/everything2rust-test-gen/references/harness-patterns.md +186 -0
- package/skills/everything2rust-validate/SKILL.md +85 -0
- package/workflows/c-to-rust.yaml +202 -0
- package/workflows/everything2rust.yaml +259 -0
- package/workflows/loop.yaml +68 -0
- package/workflows/spec.yaml +183 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rendering: RunModel → terminal lines.
|
|
3
|
+
*
|
|
4
|
+
* No pi-tui, no side effects, no I/O. Given a model and a width it returns the
|
|
5
|
+
* exact lines the run view shows, so the entire look of the product is unit-
|
|
6
|
+
* testable without a terminal. run-view.ts is a thin pi-tui wrapper that pipes
|
|
7
|
+
* these lines to the screen and calls back in on input.
|
|
8
|
+
*
|
|
9
|
+
* The layout is a growing stream on top with a persistent status block pinned at
|
|
10
|
+
* the bottom (pi-tui's TUI keeps the tail visible, so "bottom" is always on
|
|
11
|
+
* screen while stream history scrolls up — the same shape as a chat's fixed
|
|
12
|
+
* input bar). This is what a chat transcript could never give: a live pipeline
|
|
13
|
+
* you can see all of, with each step's real work streaming in place.
|
|
14
|
+
*/
|
|
15
|
+
import { isTerminal, needsUser, } from "./run-model.js";
|
|
16
|
+
import { visibleWidth, wrapTextWithAnsi, truncateToWidth } from "../pi/tui.js";
|
|
17
|
+
// ─── minimal ANSI (kept local so this file stays pure and dependency-free) ────
|
|
18
|
+
const ESC = "\x1b[";
|
|
19
|
+
const codes = {
|
|
20
|
+
reset: `${ESC}0m`,
|
|
21
|
+
dim: `${ESC}2m`,
|
|
22
|
+
bold: `${ESC}1m`,
|
|
23
|
+
red: `${ESC}31m`,
|
|
24
|
+
green: `${ESC}32m`,
|
|
25
|
+
yellow: `${ESC}33m`,
|
|
26
|
+
blue: `${ESC}34m`,
|
|
27
|
+
magenta: `${ESC}35m`,
|
|
28
|
+
cyan: `${ESC}36m`,
|
|
29
|
+
gray: `${ESC}90m`,
|
|
30
|
+
};
|
|
31
|
+
const c = (code, s) => `${code}${s}${codes.reset}`;
|
|
32
|
+
export const dim = (s) => c(codes.dim, s);
|
|
33
|
+
export const bold = (s) => c(codes.bold, s);
|
|
34
|
+
export const green = (s) => c(codes.green, s);
|
|
35
|
+
export const red = (s) => c(codes.red, s);
|
|
36
|
+
export const yellow = (s) => c(codes.yellow, s);
|
|
37
|
+
export const cyan = (s) => c(codes.cyan, s);
|
|
38
|
+
export const gray = (s) => c(codes.gray, s);
|
|
39
|
+
export const magenta = (s) => c(codes.magenta, s);
|
|
40
|
+
/**
|
|
41
|
+
* Display width, counting CJK/emoji as 2 columns (delegates to pi-tui so it
|
|
42
|
+
* agrees exactly with the width pi-tui's renderer enforces — a mismatch makes
|
|
43
|
+
* the TUI throw "line exceeds terminal width").
|
|
44
|
+
*/
|
|
45
|
+
export function visibleLength(s) {
|
|
46
|
+
return visibleWidth(s);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Wrap a logical line to `width` display columns, ANSI- and wide-char-aware.
|
|
50
|
+
* Uses pi-tui's own wrapper, so every line we emit is guaranteed to fit.
|
|
51
|
+
*/
|
|
52
|
+
export function wrap(text, width) {
|
|
53
|
+
if (width <= 0)
|
|
54
|
+
return [text];
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const raw of text.split("\n")) {
|
|
57
|
+
const wrapped = wrapTextWithAnsi(raw, width);
|
|
58
|
+
if (wrapped.length === 0)
|
|
59
|
+
out.push("");
|
|
60
|
+
else
|
|
61
|
+
for (const l of wrapped)
|
|
62
|
+
out.push(l);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
// ─── step list (the compact pipeline row that stays visible) ──────────────────
|
|
67
|
+
const STEP_ICON = {
|
|
68
|
+
pending: gray("○"),
|
|
69
|
+
do: cyan("▶"),
|
|
70
|
+
check: yellow("🔍"),
|
|
71
|
+
passed: green("✓"),
|
|
72
|
+
failed: red("✗"),
|
|
73
|
+
gate: magenta("📋"),
|
|
74
|
+
done: green("✓"),
|
|
75
|
+
};
|
|
76
|
+
function stepIcon(status) {
|
|
77
|
+
return STEP_ICON[status] ?? gray("○");
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Pipeline strip: an icon-only progress row, then a dedicated line naming
|
|
81
|
+
* the active step — number, description, DO/CHECK, elapsed. Two lines, not
|
|
82
|
+
* one, on purpose: cramming "步骤 3/7:技术方案设计 DO 45s" onto the same
|
|
83
|
+
* line as the icon row (the original design) made a user report they could
|
|
84
|
+
* only tell "some step is running" from a glance — the icons read fine as a
|
|
85
|
+
* progress bar, but the one piece of text answering "which step, doing
|
|
86
|
+
* what, DO or CHECK" was easy to miss appended after seven icons. Giving it
|
|
87
|
+
* its own line, led with the step's ordinal position, fixes that without
|
|
88
|
+
* adding new information — everything here was already computed, just
|
|
89
|
+
* under-emphasized. `now` defaults to the real clock but is an explicit
|
|
90
|
+
* param so tests stay deterministic — this is the only place render.ts
|
|
91
|
+
* touches wall-clock time at all.
|
|
92
|
+
*/
|
|
93
|
+
export function renderStepStrip(m, width, now = Date.now()) {
|
|
94
|
+
const icons = m.steps.map((s) => (s.id === m.activeStepId ? bold(stepIcon(s.status)) : stepIcon(s.status))).join(" ");
|
|
95
|
+
const active = m.steps.find((s) => s.id === m.activeStepId);
|
|
96
|
+
const out = wrap(icons, width);
|
|
97
|
+
if (active)
|
|
98
|
+
out.push(...wrap(activeLabel(m, active, now), width));
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function activeLabel(m, step, now) {
|
|
102
|
+
const ordinal = m.steps.findIndex((s) => s.id === step.id) + 1;
|
|
103
|
+
const stepNum = bold(`步骤 ${ordinal}/${m.steps.length}`);
|
|
104
|
+
const phaseTag = m.activePhase === "do" ? cyan("DO") : m.activePhase === "check" ? yellow("CHECK") : "";
|
|
105
|
+
const elapsed = m.activePhase && m.phaseStartedAt ? dim(` ${formatElapsed(now - m.phaseStartedAt)}`) : "";
|
|
106
|
+
return `${stepNum}${dim(":")}${step.desc}${phaseTag ? " " + phaseTag : ""}${elapsed}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* "3m05s" / "42s" — the only way to tell "still thinking" from "stuck" during
|
|
110
|
+
* a long silent stretch (no reasoning/tool events at all) is a ticking clock,
|
|
111
|
+
* not the stream itself.
|
|
112
|
+
*/
|
|
113
|
+
function formatElapsed(ms) {
|
|
114
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
115
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
116
|
+
const seconds = totalSeconds % 60;
|
|
117
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
118
|
+
}
|
|
119
|
+
// ─── the stream (the active step's live work) ─────────────────────────────────
|
|
120
|
+
/** Render one block. Reasoning is dimmed; tools show their key arg + a few result lines. */
|
|
121
|
+
export function renderBlock(b, width) {
|
|
122
|
+
switch (b.kind) {
|
|
123
|
+
case "phase":
|
|
124
|
+
return [dim("─".repeat(Math.min(width, 40))), ...wrap(bold(b.phase === "do" ? `▶ DO${b.attempt > 1 ? ` · 第 ${b.attempt} 次` : ""}` : `🔍 CHECK(独立只读验证)`), width)];
|
|
125
|
+
case "reasoning": {
|
|
126
|
+
const lines = wrap(b.text.trim(), width);
|
|
127
|
+
return lines.map((l) => dim(l));
|
|
128
|
+
}
|
|
129
|
+
case "text":
|
|
130
|
+
return wrap(b.text.trim(), width);
|
|
131
|
+
case "tool": {
|
|
132
|
+
const lines = wrap(toolHead(b), width);
|
|
133
|
+
if (b.result && b.result.trim()) {
|
|
134
|
+
const resultLines = wrap(b.result.trim(), Math.max(1, width - 2)).slice(0, 12);
|
|
135
|
+
for (const rl of resultLines)
|
|
136
|
+
lines.push(" " + dim(rl));
|
|
137
|
+
}
|
|
138
|
+
return lines;
|
|
139
|
+
}
|
|
140
|
+
case "verdict": {
|
|
141
|
+
if (b.infra)
|
|
142
|
+
return [yellow(`⚠ 验证未能运行:${b.reason}`)];
|
|
143
|
+
const head = b.passed ? green(`✓ 通过`) : red(`✗ 未通过`);
|
|
144
|
+
return wrap(`${head} ${dim("— " + b.reason)}`, width);
|
|
145
|
+
}
|
|
146
|
+
case "notice":
|
|
147
|
+
return wrap(b.text, width);
|
|
148
|
+
case "user":
|
|
149
|
+
return wrap(bold(cyan("› 你:")) + " " + b.text, width);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function toolHead(b) {
|
|
153
|
+
const mark = b.status === "running" ? cyan("▸") : b.status === "error" ? red("✗") : green("▸");
|
|
154
|
+
const arg = b.arg ? " " + dim(b.arg) : "";
|
|
155
|
+
return `${mark} ${b.name}${arg}`;
|
|
156
|
+
}
|
|
157
|
+
/** The active step's stream as lines. */
|
|
158
|
+
export function renderStream(m, width) {
|
|
159
|
+
const blocks = m.activeStepId ? m.streams.get(m.activeStepId) ?? [] : [];
|
|
160
|
+
const out = [];
|
|
161
|
+
for (const b of blocks) {
|
|
162
|
+
for (const line of renderBlock(b, width))
|
|
163
|
+
out.push(line);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
// ─── the persistent status block (pinned at the bottom) ───────────────────────
|
|
168
|
+
const STATUS_HEAD = {
|
|
169
|
+
running: cyan("▶ 运行中"),
|
|
170
|
+
gate: magenta("📋 等待审查"),
|
|
171
|
+
paused: yellow("⏸ 已暂停"),
|
|
172
|
+
stalled: yellow("⚠ 已停止驱动"),
|
|
173
|
+
completed: green("✓ 已完成"),
|
|
174
|
+
cancelled: gray("已取消"),
|
|
175
|
+
};
|
|
176
|
+
/** Header + step strip + action prompt + hints — always visible at the bottom. */
|
|
177
|
+
export function renderStatus(m, width, now = Date.now()) {
|
|
178
|
+
const out = [];
|
|
179
|
+
out.push(dim("─".repeat(width)));
|
|
180
|
+
// Title line: workflow · task.
|
|
181
|
+
const title = `${bold(m.workflowName)}${dim(" · ")}${truncate(m.task, Math.max(10, width - visibleLength(m.workflowName) - 4))}`;
|
|
182
|
+
out.push(...wrap(title, width));
|
|
183
|
+
// Pipeline strip (icons, then "步骤 N/Total:desc DO/CHECK elapsed" — the
|
|
184
|
+
// step counter lives there now, right next to what it's counting; restating
|
|
185
|
+
// it here would just be the same number twice).
|
|
186
|
+
out.push(...renderStepStrip(m, width, now));
|
|
187
|
+
// Run status (running / gate / paused / stalled / completed / cancelled).
|
|
188
|
+
out.push(...wrap(STATUS_HEAD[m.status], width));
|
|
189
|
+
// Action prompt when the user is needed.
|
|
190
|
+
for (const line of renderAction(m, width))
|
|
191
|
+
out.push(line);
|
|
192
|
+
// Key hints.
|
|
193
|
+
out.push(...wrap(dim(hintLine(m)), width));
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/** The actionable block for gate / pause / stall / completion. */
|
|
197
|
+
export function renderAction(m, width) {
|
|
198
|
+
if (m.status === "gate") {
|
|
199
|
+
return [
|
|
200
|
+
...wrap(magenta("📋 ") + m.statusDetail, width),
|
|
201
|
+
...wrap(`直接打字提修改意见,或输入 ${bold("/ralphflow-continue")} 通过、${bold("/ralphflow-cancel")} 取消`, width),
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
if (m.status === "paused" || m.status === "stalled") {
|
|
205
|
+
return [
|
|
206
|
+
...wrap(yellow("⏸ ") + m.statusDetail, width),
|
|
207
|
+
...wrap(`可以先打字留一句补充说明,再输入 ${bold("/ralphflow-continue")} 继续、${bold("/ralphflow-cancel")} 取消`, width),
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
if (m.status === "completed") {
|
|
211
|
+
const rp = m.reportPath ? dim(`报告:${m.reportPath}`) : "";
|
|
212
|
+
return wrap(green("✓ 工作流完成。") + (rp ? " " + rp : ""), width);
|
|
213
|
+
}
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
function hintLine(m) {
|
|
217
|
+
if (isTerminal(m))
|
|
218
|
+
return "Esc 退出 · ↑↓ 滚动";
|
|
219
|
+
if (m.activePhase === "check")
|
|
220
|
+
return "🔍 独立验证中,不可插话 · ↑↓ 滚动 · Esc 退出";
|
|
221
|
+
if (needsUser(m))
|
|
222
|
+
return "↑↓ 滚动 · Esc 退出(输入框为空时)";
|
|
223
|
+
return "随时打字插话,模型会接着聊 · ↑↓ 滚动 · Esc 退出(输入框为空时)";
|
|
224
|
+
}
|
|
225
|
+
// ─── whole screen (stream on top, status pinned below) ────────────────────────
|
|
226
|
+
/**
|
|
227
|
+
* The complete document. pi-tui's `TUI` has no real scrollable viewport —
|
|
228
|
+
* there's no API for "pin this footer, scroll that region above it" (checked:
|
|
229
|
+
* `TUI extends Container`'s public surface has nothing scroll-related; the
|
|
230
|
+
* `previousViewportTop` field it does have is a private diffing internal, not
|
|
231
|
+
* something a Component can use). What `TUI` actually does is redraw the last
|
|
232
|
+
* `terminalRows` lines of *whatever this function returns this frame* — so
|
|
233
|
+
* "scrolling" only has one degree of freedom available: change how many lines
|
|
234
|
+
* this function returns, in front of a renderer that always anchors to the
|
|
235
|
+
* bottom of that array.
|
|
236
|
+
*
|
|
237
|
+
* The bug this used to have: the earlier version dropped lines straight off
|
|
238
|
+
* the end of `stream` as `scrollBack` grew, which shrinks the *whole returned
|
|
239
|
+
* array* by that much every keypress — since the status block sits at the very
|
|
240
|
+
* end, its position relative to the top of the (now shorter) array moves every
|
|
241
|
+
* time, and because the renderer bottom-anchors, that reads as the status
|
|
242
|
+
* block itself sliding up the screen the moment you press Up, before there's
|
|
243
|
+
* any real "scrolling" sensation at all. A user reported exactly this: "方向键
|
|
244
|
+
* 上去看的时候,不是滚动看,而是下面分割线的布局先往上跑."
|
|
245
|
+
*
|
|
246
|
+
* The fix: keep the returned array's total length **invariant to
|
|
247
|
+
* `scrollBack`** by left-padding with blank lines instead of shrinking the
|
|
248
|
+
* array. The status block then always sits at the same offset from the end of
|
|
249
|
+
* the document no matter how far back you've scrolled, so the bottom-anchored
|
|
250
|
+
* renderer keeps it visually still — only the *content* revealed above it
|
|
251
|
+
* changes as you page up, which is what "scrolling" is supposed to look like
|
|
252
|
+
* within a renderer that has no actual scroll primitive.
|
|
253
|
+
*/
|
|
254
|
+
export function renderScreen(m, width, scrollBack = 0, now = Date.now()) {
|
|
255
|
+
const stream = renderStream(m, width);
|
|
256
|
+
const status = renderStatus(m, width, now);
|
|
257
|
+
const visibleCount = Math.max(0, stream.length - scrollBack);
|
|
258
|
+
const revealed = stream.slice(0, visibleCount);
|
|
259
|
+
const padding = new Array(stream.length - revealed.length).fill("");
|
|
260
|
+
return [...padding, ...revealed, "", ...status];
|
|
261
|
+
}
|
|
262
|
+
function truncate(s, width) {
|
|
263
|
+
const clean = s.replace(/\s+/g, " ").trim();
|
|
264
|
+
return visibleWidth(clean) <= width ? clean : truncateToWidth(clean, Math.max(1, width));
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/tui/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dedicated run view: renders one workflow instance running, live.
|
|
3
|
+
*
|
|
4
|
+
* `runInstanceInTui` is the reusable core — given an existing engine/runner
|
|
5
|
+
* and a TUI to render into, it drives one instance to completion/cancellation
|
|
6
|
+
* or until the user detaches, translating keystrokes into engine actions
|
|
7
|
+
* (approve / revise / resume / cancel / steer). The runner drives the
|
|
8
|
+
* workflow autonomously; this file just shows it and steers it. No LLM sits
|
|
9
|
+
* in the loop.
|
|
10
|
+
*
|
|
11
|
+
* Two callers:
|
|
12
|
+
* - `runApp(cwd)`: the standalone CLI path (own process, own TUI, own
|
|
13
|
+
* launcher to pick a workflow). Kept for direct invocation; no longer the
|
|
14
|
+
* default `ralphflow` entry point (see cli.ts) — that's now the chat.
|
|
15
|
+
* - `attachRunView` (tui/embed.ts): the chat session hands this its OWN
|
|
16
|
+
* already-running TUI/engine/runner via `ctx.ui.custom()`, so starting a
|
|
17
|
+
* workflow from chat takes over the SAME terminal instead of a second one.
|
|
18
|
+
*/
|
|
19
|
+
import type { Engine } from "../engine/core.js";
|
|
20
|
+
import { type Runner } from "../engine/runner.js";
|
|
21
|
+
import { TUI } from "../pi/tui.js";
|
|
22
|
+
export declare function runApp(cwd: string): Promise<void>;
|
|
23
|
+
export interface RunInstanceOutcome {
|
|
24
|
+
/**
|
|
25
|
+
* "completed"/"cancelled" mirror the instance's terminal fate. "detached"
|
|
26
|
+
* means the user left the run view (Esc on an empty input) while the
|
|
27
|
+
* workflow was still gate/paused/stalled/running — the runner keeps
|
|
28
|
+
* driving it untouched; nothing here pauses or aborts anything on detach.
|
|
29
|
+
*/
|
|
30
|
+
outcome: "completed" | "cancelled" | "detached";
|
|
31
|
+
reportPath?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Drive one instance to its end (or the user detaching), rendering it live
|
|
35
|
+
* into `tui`. Does not own `engine`/`runner`/`sessionId`/`tui` — the caller
|
|
36
|
+
* decides their lifetime, which is what lets this run against either a
|
|
37
|
+
* process's own dedicated TUI (`runApp`) or a chat session's TUI borrowed via
|
|
38
|
+
* `ctx.ui.custom()` (`attachRunView`).
|
|
39
|
+
*/
|
|
40
|
+
export declare function runInstanceInTui(opts: {
|
|
41
|
+
engine: Engine;
|
|
42
|
+
tui: TUI;
|
|
43
|
+
sessionId: string;
|
|
44
|
+
instId: string;
|
|
45
|
+
runner: Runner;
|
|
46
|
+
}): Promise<RunInstanceOutcome>;
|
|
47
|
+
/** Fold an attachNote'd note into whatever failure reason a resume already carries. */
|
|
48
|
+
export declare function combineReason(prevReason: string | undefined, note: string | undefined): string | undefined;
|
|
49
|
+
//# sourceMappingURL=run-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-app.d.ts","sourceRoot":"","sources":["../../src/tui/run-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EAAgB,KAAK,MAAM,EAAqB,MAAM,qBAAqB,CAAC;AAUnF,OAAO,EAAE,GAAG,EAA8B,MAAM,cAAc,CAAC;AAsB/D,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCvD;AAqCD,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqF9B;AAqBD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG1G"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dedicated run view: renders one workflow instance running, live.
|
|
3
|
+
*
|
|
4
|
+
* `runInstanceInTui` is the reusable core — given an existing engine/runner
|
|
5
|
+
* and a TUI to render into, it drives one instance to completion/cancellation
|
|
6
|
+
* or until the user detaches, translating keystrokes into engine actions
|
|
7
|
+
* (approve / revise / resume / cancel / steer). The runner drives the
|
|
8
|
+
* workflow autonomously; this file just shows it and steers it. No LLM sits
|
|
9
|
+
* in the loop.
|
|
10
|
+
*
|
|
11
|
+
* Two callers:
|
|
12
|
+
* - `runApp(cwd)`: the standalone CLI path (own process, own TUI, own
|
|
13
|
+
* launcher to pick a workflow). Kept for direct invocation; no longer the
|
|
14
|
+
* default `ralphflow` entry point (see cli.ts) — that's now the chat.
|
|
15
|
+
* - `attachRunView` (tui/embed.ts): the chat session hands this its OWN
|
|
16
|
+
* already-running TUI/engine/runner via `ctx.ui.custom()`, so starting a
|
|
17
|
+
* workflow from chat takes over the SAME terminal instead of a second one.
|
|
18
|
+
*/
|
|
19
|
+
import fs from "fs";
|
|
20
|
+
import { createEngine } from "../engine/core.js";
|
|
21
|
+
import { abortActiveCheck } from "../engine/check.js";
|
|
22
|
+
import { createRunner } from "../engine/runner.js";
|
|
23
|
+
import { withInstanceLock, isInstanceGone } from "../engine/lock.js";
|
|
24
|
+
import { loadSkillIndex } from "../engine/skills.js";
|
|
25
|
+
import { isSubWorkflowStep } from "../engine/types.js";
|
|
26
|
+
import { applyCancelled, applyCompleted, applyGate, applyPaused, applyStalled, applyStepEvent, applyStepStart, applyUserMessage, applyVerdict, initRunModel, primeForAttach, } from "./run-model.js";
|
|
27
|
+
import { runLauncher } from "./launcher.js";
|
|
28
|
+
import { createRunView } from "./run-view.js";
|
|
29
|
+
import { TUI, ProcessTerminal, initTheme } from "../pi/tui.js";
|
|
30
|
+
/** A stable owner token for this process's run. */
|
|
31
|
+
function makeSessionId() {
|
|
32
|
+
return `ralphflow-run-${process.pid}-${Date.now().toString(36)}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Terminal bell (BEL, \x07) on every state a human might need to come back
|
|
36
|
+
* for: gate, paused, stalled, completed. Cheapest possible "come look" signal
|
|
37
|
+
* that works identically whether the run view is the whole process (runApp)
|
|
38
|
+
* or a chat session's borrowed terminal (attachRunView) — most terminals/
|
|
39
|
+
* terminal multiplexers turn this into a visible tab/window flash or an
|
|
40
|
+
* audible ding even when the pane isn't focused, which a rendered line never
|
|
41
|
+
* achieves on its own. No config flag: this fires at most once per state
|
|
42
|
+
* transition (called from the same event handler that already renders once),
|
|
43
|
+
* never in a loop, so there is no plausible "too noisy" failure mode to gate.
|
|
44
|
+
*/
|
|
45
|
+
function bell() {
|
|
46
|
+
try {
|
|
47
|
+
process.stdout.write("\x07");
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
}
|
|
51
|
+
export async function runApp(cwd) {
|
|
52
|
+
initTheme();
|
|
53
|
+
const engine = createEngine(cwd, { abortActiveCheck });
|
|
54
|
+
engine.ensureProjectWorkflows();
|
|
55
|
+
const sessionId = makeSessionId();
|
|
56
|
+
const tui = new TUI(new ProcessTerminal());
|
|
57
|
+
tui.start();
|
|
58
|
+
const runner = createRunner(engine, {}, {
|
|
59
|
+
skillPaths: () => loadSkillIndex(engine.getRalphFlowDir(), engine.getGlobalConfigHome()).paths,
|
|
60
|
+
});
|
|
61
|
+
engine.setAbortActiveStep?.(runner.abortActiveStep);
|
|
62
|
+
// Park interrupted instances on the way out so a restart resumes cleanly.
|
|
63
|
+
let shuttingDown = false;
|
|
64
|
+
const shutdown = () => {
|
|
65
|
+
if (shuttingDown)
|
|
66
|
+
return;
|
|
67
|
+
shuttingDown = true;
|
|
68
|
+
try {
|
|
69
|
+
runner.pauseAllForShutdown();
|
|
70
|
+
}
|
|
71
|
+
catch { }
|
|
72
|
+
try {
|
|
73
|
+
tui.stop();
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
};
|
|
77
|
+
process.on("SIGINT", () => { shutdown(); process.exit(0); });
|
|
78
|
+
process.on("SIGTERM", () => { shutdown(); process.exit(0); });
|
|
79
|
+
try {
|
|
80
|
+
const choice = await runLauncher(tui, engine);
|
|
81
|
+
if (choice.kind === "quit")
|
|
82
|
+
return;
|
|
83
|
+
const instId = choice.kind === "resume"
|
|
84
|
+
? choice.instanceId
|
|
85
|
+
: createInstance(engine, choice.workflow, choice.task, sessionId);
|
|
86
|
+
if (!instId)
|
|
87
|
+
return; // creation failed (surfaced by createInstance)
|
|
88
|
+
await runInstanceInTui({ engine, tui, sessionId, instId, runner });
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
shutdown();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a fresh instance owned by this session and cache its first DO prompt.
|
|
96
|
+
* Mirrors ralphflow_start's creation path (normal or sub-workflow first step),
|
|
97
|
+
* minus the chat-surface text and auto-run — the run app drives it itself.
|
|
98
|
+
*/
|
|
99
|
+
function createInstance(engine, workflow, task, sessionId) {
|
|
100
|
+
const wf = engine.loadWorkflow(workflow);
|
|
101
|
+
if (!wf || wf.steps.length === 0)
|
|
102
|
+
return null;
|
|
103
|
+
const firstStep = wf.steps[0];
|
|
104
|
+
const instId = engine.generateInstanceId(workflow);
|
|
105
|
+
fs.mkdirSync(engine.getInstanceDir(instId), { recursive: true });
|
|
106
|
+
engine.writeArtifactsDirName(instId, task);
|
|
107
|
+
const baseState = {
|
|
108
|
+
active: true, workflow_name: workflow, current_step: firstStep.id, current_phase: "do",
|
|
109
|
+
fail_count: 0, user_task: task, paused: false, session_id: sessionId,
|
|
110
|
+
};
|
|
111
|
+
engine.writeState(baseState, instId);
|
|
112
|
+
engine.recordStepStart(instId, firstStep.id, "do");
|
|
113
|
+
engine.logEvent(instId, "info", "workflow_start", { workflow, instance: instId });
|
|
114
|
+
if (isSubWorkflowStep(firstStep)) {
|
|
115
|
+
engine.pushState(baseState, instId);
|
|
116
|
+
const sub = engine.resolveSubWorkflowEntry(instId, firstStep.workflow, task, firstStep);
|
|
117
|
+
if (sub.error) {
|
|
118
|
+
try {
|
|
119
|
+
fs.rmSync(engine.getInstanceDir(instId), { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (wf.manual_step.includes(firstStep.id))
|
|
127
|
+
engine.writeManualStepMarker(instId);
|
|
128
|
+
engine.buildDoPrompt(instId, firstStep, task);
|
|
129
|
+
}
|
|
130
|
+
return instId;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Drive one instance to its end (or the user detaching), rendering it live
|
|
134
|
+
* into `tui`. Does not own `engine`/`runner`/`sessionId`/`tui` — the caller
|
|
135
|
+
* decides their lifetime, which is what lets this run against either a
|
|
136
|
+
* process's own dedicated TUI (`runApp`) or a chat session's TUI borrowed via
|
|
137
|
+
* `ctx.ui.custom()` (`attachRunView`).
|
|
138
|
+
*/
|
|
139
|
+
export function runInstanceInTui(opts) {
|
|
140
|
+
const { engine, tui, sessionId, instId, runner } = opts;
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
const workflow = engine.loadWorkflow(engine.readState(instId).workflow_name);
|
|
143
|
+
const model = buildInitialModel(engine, instId, workflow);
|
|
144
|
+
// Reassigned to view.requestRender once the view exists (it also syncs the
|
|
145
|
+
// persistent input's mount/focus state — see run-view.ts).
|
|
146
|
+
let render = () => tui.requestRender();
|
|
147
|
+
/** Text left at a paused/stalled state via attachNote, consumed on resume. */
|
|
148
|
+
const pendingResumeNotes = new Map();
|
|
149
|
+
let removeInputListener = null;
|
|
150
|
+
let removeRunnerListener = null;
|
|
151
|
+
// Everything else re-renders reactively off runner events, but elapsed
|
|
152
|
+
// time needs a clock tick even during a long silent stretch (the model
|
|
153
|
+
// reasoning with no tool calls at all) — otherwise the timer freezes and
|
|
154
|
+
// looks exactly like the stuck-vs-thinking ambiguity it exists to solve.
|
|
155
|
+
// Cheap to over-fire: renderStepStrip only shows elapsed while a phase is
|
|
156
|
+
// active, and pi-tui's differential renderer no-ops an unchanged frame.
|
|
157
|
+
const tickTimer = setInterval(() => { if (model.activePhase)
|
|
158
|
+
render(); }, 1000);
|
|
159
|
+
let done = false;
|
|
160
|
+
const finish = () => {
|
|
161
|
+
if (done)
|
|
162
|
+
return;
|
|
163
|
+
done = true;
|
|
164
|
+
clearInterval(tickTimer);
|
|
165
|
+
removeInputListener?.();
|
|
166
|
+
removeRunnerListener?.();
|
|
167
|
+
resolve({
|
|
168
|
+
outcome: model.status === "completed" ? "completed" : model.status === "cancelled" ? "cancelled" : "detached",
|
|
169
|
+
reportPath: model.reportPath,
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
// Structured events → the pure model → a render request. No parsing, no LLM.
|
|
173
|
+
const events = {
|
|
174
|
+
onStepStart: (_i, sid, phase, attempt) => { applyStepStart(model, sid, phase, attempt); render(); },
|
|
175
|
+
onStepEvent: (_i, sid, phase, e) => { applyStepEvent(model, sid, phase, e); render(); },
|
|
176
|
+
onVerdict: (_i, sid, r) => { applyVerdict(model, sid, r); render(); },
|
|
177
|
+
onGate: (_i, sid) => { applyGate(model, sid); render(); bell(); },
|
|
178
|
+
onPaused: (_i, st) => { applyPaused(model, st); render(); bell(); },
|
|
179
|
+
onStalled: (_i, sid, a) => { applyStalled(model, sid, a); render(); bell(); },
|
|
180
|
+
onCompleted: (_i, rp) => { applyCompleted(model, rp); render(); bell(); },
|
|
181
|
+
};
|
|
182
|
+
removeRunnerListener = runner.addEventListener(events);
|
|
183
|
+
const view = createRunView({
|
|
184
|
+
tui,
|
|
185
|
+
getModel: () => model,
|
|
186
|
+
onQuit: finish,
|
|
187
|
+
actions: {
|
|
188
|
+
approveGate: () => { void continueInstance(engine, runner, sessionId, instId); },
|
|
189
|
+
reviseGate: (instruction) => runner.reviseGate(instId, instruction),
|
|
190
|
+
resume: () => {
|
|
191
|
+
const note = pendingResumeNotes.get(instId);
|
|
192
|
+
pendingResumeNotes.delete(instId);
|
|
193
|
+
void continueInstance(engine, runner, sessionId, instId, note);
|
|
194
|
+
},
|
|
195
|
+
cancel: () => { void cancelInstance(engine, runner, instId).then(() => { applyCancelled(model); render(); finish(); }); },
|
|
196
|
+
sendMessage: (text) => {
|
|
197
|
+
// Optimistic local echo — same style as the other actions here, no
|
|
198
|
+
// need to round-trip through a RunnerEvents callback for this.
|
|
199
|
+
const sid = model.activeStepId;
|
|
200
|
+
if (sid && model.activePhase === "do") {
|
|
201
|
+
applyUserMessage(model, sid, text);
|
|
202
|
+
render();
|
|
203
|
+
}
|
|
204
|
+
runner.sendUserMessage(instId, text);
|
|
205
|
+
},
|
|
206
|
+
attachNote: (text) => {
|
|
207
|
+
const note = text.trim();
|
|
208
|
+
if (!note)
|
|
209
|
+
return;
|
|
210
|
+
pendingResumeNotes.set(instId, note);
|
|
211
|
+
const sid = model.activeStepId;
|
|
212
|
+
if (sid) {
|
|
213
|
+
applyUserMessage(model, sid, note);
|
|
214
|
+
render();
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
render = view.requestRender;
|
|
220
|
+
// Put the view on screen and route keys to it.
|
|
221
|
+
tui.clear();
|
|
222
|
+
tui.addChild(view.screen);
|
|
223
|
+
render();
|
|
224
|
+
removeInputListener = tui.addInputListener((data) => (view.handleInput(data) ? { consume: true } : undefined));
|
|
225
|
+
// Kick off the run.
|
|
226
|
+
runner.ensureRunning(instId);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/** Build the initial model, reflecting a resumed instance's current position. */
|
|
230
|
+
function buildInitialModel(engine, instId, workflow) {
|
|
231
|
+
const state = engine.readState(instId);
|
|
232
|
+
const model = initRunModel(instId, workflow, state.user_task);
|
|
233
|
+
// Reflect where the instance actually is — this view may be attaching to
|
|
234
|
+
// one that's already running (ralphflow_watch, or ralphflow_continue
|
|
235
|
+
// landing back in the view), not one it just launched itself. See
|
|
236
|
+
// primeForAttach's own doc comment for why this is safe to call
|
|
237
|
+
// unconditionally, including for a genuinely fresh start.
|
|
238
|
+
const passedStepIds = [...new Set(engine.loadStepRecords(instId).filter((r) => r.phase === "check" && r.status === "passed").map((r) => r.stepId))];
|
|
239
|
+
primeForAttach(model, state, passedStepIds);
|
|
240
|
+
if (state.paused) {
|
|
241
|
+
applyPaused(model, state); // overrides activePhase back to null; keeps activeStepId/done-steps from above
|
|
242
|
+
}
|
|
243
|
+
return model;
|
|
244
|
+
}
|
|
245
|
+
/** Fold an attachNote'd note into whatever failure reason a resume already carries. */
|
|
246
|
+
export function combineReason(prevReason, note) {
|
|
247
|
+
if (!note)
|
|
248
|
+
return prevReason;
|
|
249
|
+
return prevReason ? `${prevReason}\n\n用户补充说明:\n${note}` : `用户补充说明:\n${note}`;
|
|
250
|
+
}
|
|
251
|
+
/** Approve a gate / resume a pause or stall — the ralphflow_continue branches, run-view side. */
|
|
252
|
+
async function continueInstance(engine, runner, sessionId, instId, note) {
|
|
253
|
+
try {
|
|
254
|
+
await withInstanceLock(engine.getInstanceDir(instId), instId, () => {
|
|
255
|
+
const state = engine.readState(instId);
|
|
256
|
+
if (!state || !state.active)
|
|
257
|
+
return;
|
|
258
|
+
engine.bindInstance(instId, sessionId);
|
|
259
|
+
// check_error pause → reset to check, re-verify.
|
|
260
|
+
if (state.paused && state.pause_reason === "check_error" && state.current_phase === "check") {
|
|
261
|
+
engine.writeState({ ...state, paused: false, pause_reason: undefined }, instId);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
// manual gate → clearing it IS the approval.
|
|
265
|
+
if (engine.markerExists(".manual-gate", instId)) {
|
|
266
|
+
engine.clearManualStepMarker(instId);
|
|
267
|
+
engine.clearManualGate(instId);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// other pause → reset fail count, re-issue the DO prompt for a fresh attempt.
|
|
271
|
+
if (state.paused) {
|
|
272
|
+
const prevReason = combineReason(state.last_failure_reason, note);
|
|
273
|
+
const prevFail = state.fail_count;
|
|
274
|
+
engine.clearReinjectCounter(instId);
|
|
275
|
+
engine.clearDoneReported(instId);
|
|
276
|
+
engine.writeState({ ...state, current_phase: "do", paused: false, pause_reason: undefined, fail_count: 0 }, instId);
|
|
277
|
+
const step = engine.getStep(engine.loadWorkflow(state.workflow_name), state.current_step);
|
|
278
|
+
if (step && !isSubWorkflowStep(step))
|
|
279
|
+
engine.buildDoPrompt(instId, step, state.user_task, prevReason, prevFail);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// stalled → the keep-alive budget ran out and the loop exited on its own
|
|
283
|
+
// WITHOUT pausing the state (runner.ts: "the workflow is fine, the model
|
|
284
|
+
// is just stuck"), so none of the branches above fire for it. Rearm the
|
|
285
|
+
// budget explicitly, and fold in a note the same way a real retry would.
|
|
286
|
+
if (state.current_phase === "do" && !runner.isRunning(instId)) {
|
|
287
|
+
engine.clearReinjectCounter(instId);
|
|
288
|
+
if (note) {
|
|
289
|
+
const step = engine.getStep(engine.loadWorkflow(state.workflow_name), state.current_step);
|
|
290
|
+
if (step && !isSubWorkflowStep(step)) {
|
|
291
|
+
engine.clearDoPromptCache(instId);
|
|
292
|
+
engine.buildDoPrompt(instId, step, state.user_task, note, state.fail_count || undefined);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch (e) {
|
|
299
|
+
if (!isInstanceGone(e))
|
|
300
|
+
throw e;
|
|
301
|
+
}
|
|
302
|
+
if (engine.instanceExists(instId))
|
|
303
|
+
runner.ensureRunning(instId);
|
|
304
|
+
}
|
|
305
|
+
async function cancelInstance(engine, runner, instId) {
|
|
306
|
+
try {
|
|
307
|
+
await withInstanceLock(engine.getInstanceDir(instId), instId, () => {
|
|
308
|
+
runner.abortActiveStep(instId);
|
|
309
|
+
engine.destroyInstance(instId, "cancelled");
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
if (!isInstanceGone(e))
|
|
314
|
+
throw e;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=run-app.js.map
|