@siuver/omp-debug-mode 0.1.6 → 0.1.7
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/CHANGELOG.md +9 -0
- package/README.md +14 -10
- package/package.json +1 -1
- package/src/debug-mode.ts +45 -0
- package/src/log-files.ts +7 -0
- package/src/methodology.ts +20 -10
- package/src/state.ts +3 -3
- package/src/ui.ts +65 -31
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7 - 2026-09-09
|
|
4
|
+
|
|
5
|
+
- Added `/debug-clear`, which empties this round's `current.jsonl` without advancing. A botched reproduction can be thrown away and rerun on the same round; archived runs stay on disk. The live counter on `/debug-proceed` drops to zero, and a busy truncate is retried the same way as rotation.
|
|
6
|
+
- Cut the repetition out of the user-turn widgets, where one screen said `/debug-proceed` three times (call to action, the agent's closing step, the actions strip), the round number three times (footer plus both exit lines), and "nothing was captured" twice (the proceed line plus a `run <id> — 0 log entries` footer). The round number now lives only in the footer, the captured counts live only on the `/debug-proceed` line that acts on them — still climbing live while you reproduce — and the run id is gone from the widget entirely, since it is not something you type.
|
|
7
|
+
- The actions strip under the editor is a dim cheat-sheet headed `Tips:`, not a second headline. `/debug-proceed` no longer lights up when logs arrive (empty vs captured stays in the wording), the leftover-commands line stays dim, and `Reply in the editor` is gone: that exit is still legal, but listing it next to the editor restated what the cursor already is.
|
|
8
|
+
- The widget renumbers reproduction steps itself and drops any step naming `/debug-proceed` or `/debug-done`. The agent numbers its list about as often as it does not, and an unnumbered run of dim lines reads as one paragraph instead of a sequence — while the injected contract used to *require* a closing line naming the commands, which the model then also wrote in as a final numbered step the user cannot perform until every other step is done. The methodology, the start/proceed prompts, and the user-turn briefing now forbid naming either command in prose or in the steps, and ask for one short line saying what to do instead.
|
|
9
|
+
- Widget lines are clipped by terminal columns instead of character count. A CJK step counts one unit per character while occupying two columns, so a Chinese reproduction step passed the 90-character budget untouched at 180 columns wide and was wrapped or truncated by the host.
|
|
10
|
+
- Stopped spelling the `-` sentinel as `<request-id|->` under the editor: it renders as an arrow directly beneath three lines that use `→` to mean "and then this happens". The command's own completion already offers `-` as its first entry.
|
|
11
|
+
|
|
3
12
|
## 0.1.6 - 2026-08-31
|
|
4
13
|
|
|
5
14
|
- `/debug-proceed` now recreates a missing log directory, stops the live log watcher before rotating `current.jsonl`, retries a busy truncate, and reports the OS error instead of a bare "could not initialize the next run log file". A failed truncate no longer deletes the archive it just copied — that rollback destroyed the completed run when the instrumented app still had the file open.
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ The plugin makes the agent form 3-5 hypotheses, choose the cheapest reliable evi
|
|
|
13
13
|
| `/debug-evidence <request-id\|-> <path>` | Attaches one user-provided evidence file. The first argument always names what the file answers: an evidence request ID, or `-` for a file no request asked for. Typing the command lists the available selectors under the editor. Files are referenced in place and never copied or deleted. |
|
|
14
14
|
| `/debug-done` | Marks the problem as fixed: the agent removes every probe and summarizes the result. |
|
|
15
15
|
| `/debug-status` | Shows the current stage, round, run, how the round was handed back, live probes, captured log counts, pending evidence requests, and attached/unavailable artifacts. |
|
|
16
|
+
| `/debug-clear` | Empties the current run's `current.jsonl` so you can reproduce again on this round. Archived runs are left alone. |
|
|
16
17
|
| `/debug-abort` | Stops debug mode and removes its logs after confirmation while leaving code changes in the working tree. |
|
|
17
18
|
|
|
18
19
|
## Stages
|
|
@@ -92,23 +93,26 @@ Footer status stays a one-line label (`🐞 round N · reproduce`). The round it
|
|
|
92
93
|
**Above the editor** — what this round asked you to do:
|
|
93
94
|
|
|
94
95
|
```text
|
|
95
|
-
Reproduce the bug now
|
|
96
|
+
Reproduce the bug now.
|
|
96
97
|
↪ E1 Capture a frame: Capture the failing frame.
|
|
97
98
|
1. Restart the game.
|
|
98
99
|
2. Jump near the pit.
|
|
99
100
|
```
|
|
100
101
|
|
|
101
|
-
**Below the editor** —
|
|
102
|
+
**Below the editor** — a dim cheat-sheet of the commands:
|
|
102
103
|
|
|
103
104
|
```text
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
Tips:
|
|
106
|
+
/debug-proceed → close this round, analyze 14 log entries, 1 artifact
|
|
106
107
|
/debug-done → bug is fixed: remove probes and summarize
|
|
107
|
-
/debug-evidence <request-id
|
|
108
|
-
run run2-abc — 14 log entries
|
|
108
|
+
/debug-evidence <request-id> <path> · /debug-clear · /debug-abort · /debug-status
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
The `/debug-proceed` line
|
|
111
|
+
Each fact appears once. The round number lives in the footer, so the exit lines say "this round"; the captured counts live on the `/debug-proceed` line, which is the only thing that acts on them, and they climb in place while you reproduce. The strip is all dim so it sits next to the prompt as a reminder rather than as a second headline — empty vs captured is in the wording (`close this round with nothing captured yet`), not the color. Replying in the editor is still a legal exit; it is just not listed here, because the editor is already where that line would have pointed.
|
|
112
|
+
|
|
113
|
+
Steps are renumbered by the widget, so an unnumbered list from the agent still reads as a sequence, and a step that names `/debug-proceed` or `/debug-done` is dropped: the commands belong under the editor, and the injected contract tells the agent not to write them into the steps in the first place. Lines are clipped by terminal columns rather than by character count, so a step written in Chinese or Japanese is not sent out at double width.
|
|
114
|
+
|
|
115
|
+
The same commands are offered in every mode, including `incomplete`. `/debug-evidence` also accepts `-` in place of a request ID for a file no request asked for; the command's own completion offers it. There is no Proceed/Mark as fixed button: type the slash command.
|
|
112
116
|
|
|
113
117
|
Every round stops at these widgets and you interact through commands only.
|
|
114
118
|
|
|
@@ -149,9 +153,9 @@ Example: for a GPU-rendering symptom whose disputed state only exists inside a f
|
|
|
149
153
|
1. Run `/debug-mode <problem description>`.
|
|
150
154
|
2. The Agent records 3-5 hypotheses and picks an evidence method for each under the least-user-intervention priority. If the route is `runtime_probe`, it inserts minimal probes marked with `@omp-probe <id>` inside `#region agent log` blocks in the same turn, before closing the round; round 1 does not apply a product fix (probe edits are not a product fix). The start prompt and a per-turn `debug-mode-context` message name the exact JSONL path and require every probe to append to it, including `hypothesisId`; console output (including Unity `Debug.Log`) is supplemental only. The context filter keeps the newest of those messages so the model sees the contract instead of dropping it. Steps are user actions only (reproduce, capture, restart).
|
|
151
155
|
|
|
152
|
-
3. The Agent closes the round with `hand_off_to_user`, which is what puts the steps in your widget. Follow them in the real application so the instrumented code writes its observations, watching the
|
|
156
|
+
3. The Agent closes the round with `hand_off_to_user`, which is what puts the steps in your widget. Follow them in the real application so the instrumented code writes its observations, watching the count on the `/debug-proceed` line climb — or supply the requested evidence with `/debug-evidence` or `/debug-proceed <details>`. Restart the app or service if the instrumented build would otherwise be stale.
|
|
153
157
|
|
|
154
|
-
4. Continue with `/debug-proceed [details]` (optionally record a user observation, then evaluate evidence), `/debug-done` (clean up and summarize), `/debug-evidence` (attach a file), or `/debug-status` (inspect the current round state). Replying normally instead hands the round back to the agent without closing it. Each round stops at the widget; all interaction is command-driven.
|
|
158
|
+
4. Continue with `/debug-proceed [details]` (optionally record a user observation, then evaluate evidence), `/debug-clear` (empty this round's log and reproduce again), `/debug-done` (clean up and summarize), `/debug-evidence` (attach a file), or `/debug-status` (inspect the current round state). Replying normally instead hands the round back to the agent without closing it. Each round stops at the widget; all interaction is command-driven.
|
|
155
159
|
5. On Proceed, the Agent reads the evidence (logs, your observations, attached artifacts), cites the evidence method plus log-line numbers or artifact/report paths for each hypothesis, and only then may apply a fix. Probes stay in place. Reproduce again to verify. If verification fails, rejected-hypothesis code changes are reverted before the next round.
|
|
156
160
|
6. On `/debug-done`, the Agent removes every probe and summarizes the root cause and fix in 1-2 lines. If the ledger is still not empty when that turn settles, cleanup is sent back once with the remaining markers listed; a second incomplete attempt tears down anyway and warns you which probes are still in your source.
|
|
157
161
|
|
|
@@ -177,7 +181,7 @@ That exact stable path is injected into the Agent prompt. The prompt requires ev
|
|
|
177
181
|
|
|
178
182
|
`get_debug_logs`, the proceed prompt, and `/debug-status` summarize each run by `hypothesisId`, so you can see which hypotheses produced evidence before reading a single raw line. A hypothesis with no entries was not exercised, which is not the same as being rejected.
|
|
179
183
|
|
|
180
|
-
When you run `/debug-proceed [details]`, the plugin records non-empty details as a user observation, archives the completed file as `<run-id>.jsonl`, and creates an empty `current.jsonl` for the next reproduction. If that rotation fails (typical on Windows while the instrumented app still has `current.jsonl` open), the round stays open, the archive is kept, and the error names the OS reason — close the app and retry. Because the active run is truncated at the start of every round, `get_debug_logs` defaults to the newest run that actually captured observations and names the run it read; `previous=true` forces the last completed run. `/debug-status` shows the current absolute file path and the per-run log counts.
|
|
184
|
+
When you run `/debug-proceed [details]`, the plugin records non-empty details as a user observation, archives the completed file as `<run-id>.jsonl`, and creates an empty `current.jsonl` for the next reproduction. If that rotation fails (typical on Windows while the instrumented app still has `current.jsonl` open), the round stays open, the archive is kept, and the error names the OS reason — close the app and retry. `/debug-clear` truncates `current.jsonl` in place without archiving or opening a new round, so a reproduction you want to throw away does not become the evidence for `/debug-proceed`. Because the active run is truncated at the start of every round, `get_debug_logs` defaults to the newest run that actually captured observations and names the run it read; `previous=true` forces the last completed run. `/debug-status` shows the current absolute file path and the per-run log counts.
|
|
181
185
|
|
|
182
186
|
## Probe Ledger
|
|
183
187
|
|
package/package.json
CHANGED
package/src/debug-mode.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
JsonlLineCounter,
|
|
22
22
|
describeHypotheses,
|
|
23
23
|
prepareRunLog,
|
|
24
|
+
clearActiveLog,
|
|
24
25
|
readJsonlLines,
|
|
25
26
|
summarizeHypotheses,
|
|
26
27
|
} from "./log-files";
|
|
@@ -55,6 +56,7 @@ const COMMAND_PROCEED = "debug-proceed";
|
|
|
55
56
|
const COMMAND_EVIDENCE = "debug-evidence";
|
|
56
57
|
const COMMAND_ABORT = "debug-abort";
|
|
57
58
|
const COMMAND_STATUS = "debug-status";
|
|
59
|
+
const COMMAND_CLEAR = "debug-clear";
|
|
58
60
|
|
|
59
61
|
/** Compact transcript lines for the prompts this extension injects. */
|
|
60
62
|
const MESSAGE_SUMMARIES: Record<string, string> = {
|
|
@@ -587,6 +589,41 @@ export function registerDebugMode(pi: ExtensionAPI): void {
|
|
|
587
589
|
await setDebugToolsActive(false);
|
|
588
590
|
}
|
|
589
591
|
|
|
592
|
+
/** Empty this round's live log without advancing. Archived runs stay. */
|
|
593
|
+
async function clearDebugLog(ctx: ExtensionCommandContext): Promise<void> {
|
|
594
|
+
if (!activeSession(ctx)) return;
|
|
595
|
+
await ctx.waitForIdle();
|
|
596
|
+
if (!state.active || !state.debugDir) return;
|
|
597
|
+
refreshLogCounts();
|
|
598
|
+
if (!state.active) return;
|
|
599
|
+
const discarded = currentLogCount();
|
|
600
|
+
unwatchLogFile();
|
|
601
|
+
try {
|
|
602
|
+
clearActiveLog(state.debugDir);
|
|
603
|
+
} catch (err) {
|
|
604
|
+
watchLogFile();
|
|
605
|
+
const error = fsErrorMessage(err);
|
|
606
|
+
pi.logger.error("debug-mode: cannot clear run log", {
|
|
607
|
+
file: path.join(state.debugDir, ACTIVE_LOG_FILE),
|
|
608
|
+
err,
|
|
609
|
+
});
|
|
610
|
+
ctx.ui.notify(
|
|
611
|
+
`debug-mode: could not clear the run log file (${error}).${runLogHint(error)}`,
|
|
612
|
+
"error",
|
|
613
|
+
);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
refreshLogCounts();
|
|
617
|
+
watchLogFile();
|
|
618
|
+
refreshUi();
|
|
619
|
+
ctx.ui.notify(
|
|
620
|
+
discarded === 0
|
|
621
|
+
? "debug-mode: current run log is already empty"
|
|
622
|
+
: `debug-mode: cleared ${discarded === 1 ? "1 log entry" : `${discarded} log entries`} from this round`,
|
|
623
|
+
"info",
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
590
627
|
/**
|
|
591
628
|
* Attach one user-provided evidence file to the current round. The file is
|
|
592
629
|
* referenced in place — never copied, moved or deleted — and the action does
|
|
@@ -704,6 +741,14 @@ export function registerDebugMode(pi: ExtensionAPI): void {
|
|
|
704
741
|
},
|
|
705
742
|
});
|
|
706
743
|
|
|
744
|
+
pi.registerCommand(COMMAND_CLEAR, {
|
|
745
|
+
description: "Empty the current run log so this round can record a fresh reproduction",
|
|
746
|
+
handler: async (_args, ctx) => {
|
|
747
|
+
uiCtx = ctx;
|
|
748
|
+
await clearDebugLog(ctx);
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
|
|
707
752
|
pi.registerCommand(COMMAND_STATUS, {
|
|
708
753
|
description: "Show debug mode state",
|
|
709
754
|
handler: async (_args, ctx) => {
|
package/src/log-files.ts
CHANGED
|
@@ -140,6 +140,13 @@ function truncateActiveLog(activeFile: string): void {
|
|
|
140
140
|
throw last;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
export function clearActiveLog(debugDir: string): string {
|
|
144
|
+
fs.mkdirSync(debugDir, { recursive: true });
|
|
145
|
+
const activeFile = path.join(debugDir, ACTIVE_LOG_FILE);
|
|
146
|
+
truncateActiveLog(activeFile);
|
|
147
|
+
return activeFile;
|
|
148
|
+
}
|
|
149
|
+
|
|
143
150
|
export function prepareRunLog(debugDir: string, previousRun: string | null): string {
|
|
144
151
|
fs.mkdirSync(debugDir, { recursive: true });
|
|
145
152
|
const activeFile = path.join(debugDir, ACTIVE_LOG_FILE);
|
package/src/methodology.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The widget under the editor names every command already, so prose that
|
|
3
|
+
* repeats them is the same instruction three times on one screen: the call to
|
|
4
|
+
* action, the model's own closing line, and the actions strip all said
|
|
5
|
+
* `/debug-proceed`. A model told to "tell the user to run /debug-proceed" also
|
|
6
|
+
* writes it into `<reproduction_steps>`, where it becomes a numbered step the
|
|
7
|
+
* user cannot perform until every other step is done.
|
|
8
|
+
*/
|
|
9
|
+
export const COMMAND_ECHO_RULE =
|
|
10
|
+
"Do NOT name /debug-proceed or /debug-done in your closing prose or inside <reproduction_steps>: the widget " +
|
|
11
|
+
"under the user's editor already offers both, and repeating them puts the same instruction on screen three " +
|
|
12
|
+
"times. Close with one short line saying what to do — reproduce, capture, answer — never which command to type.";
|
|
3
13
|
|
|
4
14
|
export const EVIDENCE_PLAN_TAG = "evidence_plan";
|
|
5
15
|
|
|
@@ -94,13 +104,13 @@ This is OMP Debug Mode. Follow the steps in order. Do not skip them.
|
|
|
94
104
|
combined reproduction/capture sequence the USER performs now as the steps
|
|
95
105
|
argument of ${HANDOFF_TOOL} — the widget the user reads is built from that
|
|
96
106
|
call, not from your prose. A <reproduction_steps> numbered list (no header
|
|
97
|
-
inside the tag)
|
|
98
|
-
|
|
107
|
+
inside the tag) is read as content but closes nothing, so make the call even
|
|
108
|
+
when you also wrote the prose.
|
|
99
109
|
${CLOSE_ROUND_RULES}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
reply "done". Remind them to restart the app or service if
|
|
103
|
-
code would otherwise be stale.
|
|
110
|
+
${COMMAND_ECHO_RULE}
|
|
111
|
+
Never say "click" or "press Proceed": those buttons do not exist, and never
|
|
112
|
+
ask the user to reply "done". Remind them to restart the app or service if
|
|
113
|
+
the instrumented code would otherwise be stale.
|
|
104
114
|
Then STOP. The user reproduces out-of-band.
|
|
105
115
|
No logs may be expected for user_report/user_artifact plans: evaluate the
|
|
106
116
|
requested user evidence instead of treating absent probes as a failed round.
|
|
@@ -150,7 +160,7 @@ export function buildStartMessage(problem: string, logFile: string): string {
|
|
|
150
160
|
"Call list_debug_evidence whenever you need the request/observation/artifact ledger. " +
|
|
151
161
|
"Cite evidence as hypothesis ID plus log-line number or attached observation/artifact/report path. " +
|
|
152
162
|
`Then close with ${HANDOFF_TOOL} — prose alone never closes a round, so make the call even if you also wrote ` +
|
|
153
|
-
`a <${EVIDENCE_PLAN_TAG}> block
|
|
163
|
+
`a <${EVIDENCE_PLAN_TAG}> block. ${COMMAND_ECHO_RULE} Then STOP.`
|
|
154
164
|
);
|
|
155
165
|
}
|
|
156
166
|
|
|
@@ -187,7 +197,7 @@ export function buildProceedMessage(args: {
|
|
|
187
197
|
" " +
|
|
188
198
|
HANDOFF_RULE +
|
|
189
199
|
" " +
|
|
190
|
-
`Then end with ${HANDOFF_TOOL} — prose alone never closes a round
|
|
200
|
+
`Then end with ${HANDOFF_TOOL} — prose alone never closes a round. ${COMMAND_ECHO_RULE} Then STOP.`
|
|
191
201
|
);
|
|
192
202
|
}
|
|
193
203
|
|
package/src/state.ts
CHANGED
|
@@ -499,9 +499,9 @@ export function stageBriefing(session: DebugSession): string {
|
|
|
499
499
|
"finishing the turn in which you just called hand_off_to_user, or you were resumed by a reminder or " +
|
|
500
500
|
"another automatic continuation. Either way the user has not acted, no reproduction has run, and no new " +
|
|
501
501
|
"observation exists. Do not read logs, do not analyze, do not resume the plan. Write one short line " +
|
|
502
|
-
"addressed to the user
|
|
503
|
-
"/debug-done
|
|
504
|
-
"that you were reminded and never restate this briefing back to them."
|
|
502
|
+
"addressed to the user saying what to do now — reproduce, capture, or answer — and end the turn. Do " +
|
|
503
|
+
"not name /debug-proceed or /debug-done: the widget under their editor already offers both. Never " +
|
|
504
|
+
"report that you were reminded and never restate this briefing back to them."
|
|
505
505
|
);
|
|
506
506
|
}
|
|
507
507
|
return (
|
package/src/ui.ts
CHANGED
|
@@ -28,8 +28,24 @@ export function statusLabel(session: DebugSession): string {
|
|
|
28
28
|
return `🐞 round ${round.index} · needs input`;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Clip to terminal columns, not to code units. Reproduction steps are written
|
|
33
|
+
* in whatever language the user reports the bug in, and a CJK step counts one
|
|
34
|
+
* unit per character while occupying two columns — measured by `length` a line
|
|
35
|
+
* of Chinese fits the budget at twice the real width and the host wraps or
|
|
36
|
+
* truncates it instead.
|
|
37
|
+
*/
|
|
31
38
|
function clip(text: string): string {
|
|
32
|
-
|
|
39
|
+
if (Bun.stringWidth(text) <= WIDGET_MAX_WIDTH) return text;
|
|
40
|
+
let width = 0;
|
|
41
|
+
let kept = "";
|
|
42
|
+
for (const char of text) {
|
|
43
|
+
const next = width + Bun.stringWidth(char);
|
|
44
|
+
if (next > WIDGET_MAX_WIDTH - 1) break;
|
|
45
|
+
width = next;
|
|
46
|
+
kept += char;
|
|
47
|
+
}
|
|
48
|
+
return `${kept}…`;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
function paint(ctx: ExtensionContext, lines: WidgetLine[]): string[] {
|
|
@@ -41,8 +57,15 @@ function clearWidgets(ctx: ExtensionContext): void {
|
|
|
41
57
|
ctx.ui.setWidget(WIDGET_ACTIONS_KEY, undefined);
|
|
42
58
|
}
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
const TIPS_HEADER = "Tips:";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Remaining commands after the exits have their own lines. The `-`
|
|
64
|
+
* sentinel is deliberately not spelled out here: `<request-id|->` renders as an
|
|
65
|
+
* arrow next to lines that use `→` to mean "and then this happens", and
|
|
66
|
+
* the command's own completion offers `-` as its first entry anyway.
|
|
67
|
+
*/
|
|
68
|
+
const OTHER_COMMANDS = "/debug-evidence <request-id> <path> · /debug-clear · /debug-abort · /debug-status";
|
|
46
69
|
|
|
47
70
|
function plural(count: number, singular: string): string {
|
|
48
71
|
return `${count} ${singular}${count === 1 ? "" : "s"}`;
|
|
@@ -52,20 +75,23 @@ function logEntries(count: number): string {
|
|
|
52
75
|
return count === 1 ? "1 log entry" : `${count} log entries`;
|
|
53
76
|
}
|
|
54
77
|
|
|
78
|
+
/**
|
|
79
|
+
* What this round wants from the user, without naming a command: the actions
|
|
80
|
+
* widget sits directly under the editor and names all of them already.
|
|
81
|
+
*/
|
|
55
82
|
function callToAction(mode: HandoffMode): string {
|
|
56
|
-
if (mode === "reproduce") return "Reproduce the bug now
|
|
57
|
-
if (mode === "capture") return "Supply the requested report or file
|
|
83
|
+
if (mode === "reproduce") return "Reproduce the bug now.";
|
|
84
|
+
if (mode === "capture") return "Supply the requested report or file.";
|
|
58
85
|
if (mode === "question") return "Reply in the editor. The agent is waiting on an answer, not a reproduction.";
|
|
59
86
|
return "The agent stopped without saying what to reproduce or capture.";
|
|
60
87
|
}
|
|
61
88
|
|
|
62
89
|
/**
|
|
63
90
|
* What `/debug-proceed` does right now, with the numbers it would act on.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
91
|
+
* The actions strip is a dim cheat-sheet under the editor, so this line never
|
|
92
|
+
* lights up: captured vs empty is in the wording, not the tone.
|
|
66
93
|
*/
|
|
67
94
|
export function proceedLine(session: DebugSession, logCount: number): WidgetLine {
|
|
68
|
-
const round = currentRound(session);
|
|
69
95
|
const captured: string[] = [];
|
|
70
96
|
if (logCount > 0) captured.push(logEntries(logCount));
|
|
71
97
|
if (session.observations.length > 0) captured.push(plural(session.observations.length, "observation"));
|
|
@@ -73,23 +99,14 @@ export function proceedLine(session: DebugSession, logCount: number): WidgetLine
|
|
|
73
99
|
return {
|
|
74
100
|
text: clip(
|
|
75
101
|
captured.length > 0
|
|
76
|
-
? `/debug-proceed → close
|
|
77
|
-
:
|
|
102
|
+
? `/debug-proceed → close this round, analyze ${captured.join(", ")}`
|
|
103
|
+
: "/debug-proceed → close this round with nothing captured yet",
|
|
78
104
|
),
|
|
79
|
-
tone: captured.length > 0 ? "accent" : "warning",
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** The other half of the same choice, so declining `/debug-proceed` is informed too. */
|
|
84
|
-
export function replyLine(session: DebugSession): WidgetLine {
|
|
85
|
-
const round = currentRound(session);
|
|
86
|
-
return {
|
|
87
|
-
text: clip(`Reply in the editor → round ${round.index} stays open, the run keeps recording`),
|
|
88
105
|
tone: "dim",
|
|
89
106
|
};
|
|
90
107
|
}
|
|
91
108
|
|
|
92
|
-
/** The
|
|
109
|
+
/** The other exit: the bug is actually gone. */
|
|
93
110
|
export function doneLine(): WidgetLine {
|
|
94
111
|
return {
|
|
95
112
|
text: clip("/debug-done → bug is fixed: remove probes and summarize"),
|
|
@@ -97,6 +114,27 @@ export function doneLine(): WidgetLine {
|
|
|
97
114
|
};
|
|
98
115
|
}
|
|
99
116
|
|
|
117
|
+
/** A model ordinal in any of the shapes the prompt's numbered list produces. */
|
|
118
|
+
const LEADING_ORDINAL = /^\s*\d+\s*[.)、]\s*/;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Every step carries the widget's own ordinal. The model numbers its list about
|
|
122
|
+
* as often as it does not, and an unnumbered run of dim lines reads as one
|
|
123
|
+
* paragraph rather than as a sequence to work through.
|
|
124
|
+
*/
|
|
125
|
+
export function numberedSteps(steps: readonly string[]): string[] {
|
|
126
|
+
const kept: string[] = [];
|
|
127
|
+
for (const step of steps) {
|
|
128
|
+
// The commands are named once, under the editor. A step that repeats them
|
|
129
|
+
// is the third copy on one screen, and the prompt forbids writing it.
|
|
130
|
+
if (/\/debug-(?:proceed|done)\b/i.test(step)) continue;
|
|
131
|
+
const body = step.replace(LEADING_ORDINAL, "").trim();
|
|
132
|
+
if (body.length === 0) continue;
|
|
133
|
+
kept.push(`${kept.length + 1}. ${body}`);
|
|
134
|
+
}
|
|
135
|
+
return kept;
|
|
136
|
+
}
|
|
137
|
+
|
|
100
138
|
function appendFoldedSteps(lines: WidgetLine[], steps: readonly string[]): void {
|
|
101
139
|
const stepBudget = Math.max(0, WIDGET_MAX_LINES - lines.length);
|
|
102
140
|
if (stepBudget === 0 || steps.length === 0) return;
|
|
@@ -121,27 +159,23 @@ export function userTurnContextLines(
|
|
|
121
159
|
for (const request of pendingEvidence.slice(0, 2)) {
|
|
122
160
|
lines.push({ text: clip(`↪ ${request.id} ${request.title}: ${request.instructions[0] ?? ""}`), tone: "accent" });
|
|
123
161
|
}
|
|
124
|
-
const steps = mode === "reproduce" || mode === "capture" ? round.reproductionSteps : [];
|
|
162
|
+
const steps = mode === "reproduce" || mode === "capture" ? numberedSteps(round.reproductionSteps) : [];
|
|
125
163
|
appendFoldedSteps(lines, steps);
|
|
126
164
|
return lines.slice(0, WIDGET_MAX_LINES);
|
|
127
165
|
}
|
|
128
166
|
|
|
129
167
|
/**
|
|
130
|
-
* Below the editor:
|
|
131
|
-
*
|
|
132
|
-
*
|
|
168
|
+
* Below the editor: a dim cheat-sheet next to the prompt so the user does not
|
|
169
|
+
* have to hunt above the transcript for what to type. A reply in the editor is
|
|
170
|
+
* still a legal exit — it just is not listed here, because the editor is
|
|
171
|
+
* already where that line would have pointed.
|
|
133
172
|
*/
|
|
134
173
|
export function userTurnActionLines(session: DebugSession, logCount: number): WidgetLine[] {
|
|
135
|
-
const round = currentRound(session);
|
|
136
174
|
return [
|
|
175
|
+
{ text: TIPS_HEADER, tone: "dim" },
|
|
137
176
|
proceedLine(session, logCount),
|
|
138
|
-
replyLine(session),
|
|
139
177
|
doneLine(),
|
|
140
|
-
{ text: clip(OTHER_COMMANDS), tone: "
|
|
141
|
-
{
|
|
142
|
-
text: `run ${round.runId ?? "none"} — ${logEntries(logCount)}`,
|
|
143
|
-
tone: logCount > 0 ? "accent" : "dim",
|
|
144
|
-
},
|
|
178
|
+
{ text: clip(OTHER_COMMANDS), tone: "dim" },
|
|
145
179
|
];
|
|
146
180
|
}
|
|
147
181
|
|