micro-models-agent 2.13.1 → 2.15.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/CHANGELOG.md +23 -0
- package/dist/assets/app.js +3 -1
- package/dist/assets/index.html +1 -0
- package/dist/assets/js/dom.js +1 -0
- package/dist/assets/js/feed.js +149 -41
- package/dist/assets/js/settings.js +21 -1
- package/dist/assets/styles.css +53 -27
- package/dist/i18n/en.json +13 -4
- package/dist/i18n/ru.json +13 -4
- package/dist/main.js +279 -41
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.15.0] - 2026-10-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Web UI: stale-translations badge** (`src/modules/webui/i18n-stale.ts`, `i18n-api.ts`, `host-state.ts`, `core/host.ts`, `assets/index.html`, `js/dom.js`, `js/settings.js`, `app.js`, `styles.css`): `GET /api/i18n` and the state snapshot carry `i18nStale` (dict files on disk newer than the server process); the topbar shows a yellow badge with a reload action instead of silently rendering raw keys. New keys `web.i18n.stale`/`web.i18n.reload` (en+ru). Tests: `tests/webui-i18n.test.ts`, `tests/webui-usage.test.ts`, `tests/webui-assets.test.ts`.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Plan auto-advance no longer completes modify-steps on stale files** (`src/modules/execution/module.ts`): `advancePlanIfStepComplete` and final-audit `autoCloseEvidenceSteps` now require `provablyStale()` to be false — every named file must be created/modified after `plan.createdAt` (fail-open on bad dates and unreadable mtimes, delete-kind unchanged). Previously a step naming a long-existing file auto-completed before a line of code was written (observed: 6 plan steps auto-closed while the agent was still on step 1). Tests: `tests/execution-plan-deliverables.test.ts`.
|
|
18
|
+
- **Critic evidence includes git-tracked changes** (`src/modules/critic/evidence.ts`): `buildEvidencePack` unions tool-arg file lists with `git status --porcelain` (staged + unstaged + untracked, capped, fail-open outside repos), so edits made outside file tools (helper scripts via bash, heredocs) no longer produce false `concern` verdicts about missing changes. Tests: `tests/critic-evidence.test.ts`.
|
|
19
|
+
- **Repeated-failure memory writes are diagnosed, model misses skipped** (`src/core/agent/tool-batch.ts`, `loop-state.ts`): `diagnoseRepeatedFailures()` records distinct error headlines with signature-based cause/solution (edit_file anchor miss → CRLF/stale-read hypothesis) and skips global-memory writes for model errors (invented test paths, exit-code-only grep misses). Previously boilerplate causes and the model's own wrong guesses landed in `errors.md`. Tests: `tests/tool-failure-diagnosis.test.ts`.
|
|
20
|
+
- **Factual check resolves path suffixes** (`src/modules/hallucination/factual.ts`): `webui/state.ts` now matches `src/modules/webui/state.ts` via the cached basename index (no new FS walks), fixing false "mentioned files not found" warnings on short paths. Tests: `tests/hallucination-factual.test.ts`.
|
|
21
|
+
- **`edit_file` tolerates LF anchors in CRLF files** (`src/tools/edit-file.ts`): `spliceNormalized()` matches on normalized line endings and splices the original, keeping the file's ending style (no more "string not found" loops on Windows checkouts). Tests: `tests/edit-file.test.ts`.
|
|
22
|
+
- **Web usage re-render covers the context breakdown** (`src/modules/webui/assets/js/feed.js`): `h.contextBreakdown` added to the `renderUsage` signature so part shifts at constant used/limit repaint the segmented bar and legend. Guard: `tests/webui-assets.test.ts`.
|
|
23
|
+
- **Terminal context-budget part words are localized** (`src/cli/repl.ts`, `src/i18n/en.json`, `ru.json`): the startup banner showed untranslated latin `sys/res/hist` in both locales; now `repl.ctx_part_system/reserve/history` (`system/reserve/history` + `система/резерв/история`). Web legend (usage parts) intentionally unchanged. Tests: `tests/i18n.test.ts`.
|
|
24
|
+
- **Verification hygiene is model-facing** (`src/core/system-prompt.ts`, `src/tools/browser.ts`): verification `mma web` servers must run isolated (`MMA_CONFIG_DIR`), never against the user's live server; without vision capabilities the browser description steers to snapshot text and DOM/API assertions. Tests: `tests/core/bootstrap.test.ts`, `tests/tools/browser.test.ts`.
|
|
25
|
+
|
|
26
|
+
## [2.14.0] - 2026-10-01
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **Web UI**: file actions now render as quiet terminal-style lines instead of boxed cards (`src/modules/webui/assets/js/feed.js`, `styles.css`). Each file tool shows `◇ <label> · <path-link> [copy-icon] <status>` in the tool-category color — no more Open/Download/Path pill buttons (path click opens the viewer, copy is an icon-only hover action). Consecutive file rows collapse into an expandable `│ N files` group with a chevron toggle. File-type badges are gone (format detection stays silent for highlighting only; draw.io hint is verbose-only). Dead strings `web.card.open/download/copy_path/download_title` removed, count via new `web.card.files_*` (en+ru). Tests: `tests/webui-assets.test.ts`, `tests/webui-smoke.test.ts`, new `tests/e2e/web-filecard.e2e-test.ts`.
|
|
31
|
+
|
|
9
32
|
## [2.13.1] - 2026-09-30
|
|
10
33
|
|
|
11
34
|
### Fixed
|
package/dist/assets/app.js
CHANGED
|
@@ -15,7 +15,7 @@ loadCommands,
|
|
|
15
15
|
} from "./js/tabs.js";
|
|
16
16
|
import {
|
|
17
17
|
renderSelectors, renderSettings, loadConfig, loadModels, loadProviders,
|
|
18
|
-
loadMemory, loadSkills, loadPlugins, applyTheme, currentTheme, applyFontSize, setConnStatus,
|
|
18
|
+
loadMemory, loadSkills, loadPlugins, applyTheme, currentTheme, applyFontSize, setConnStatus, setStaleBar,
|
|
19
19
|
} from "./js/settings.js";
|
|
20
20
|
import {
|
|
21
21
|
initRail, setPlan as railPlan, railEvent, applyRailI18n,
|
|
@@ -88,6 +88,7 @@ function renderState(snapshot, busy) {
|
|
|
88
88
|
sidebarSessionChanged(sessionId);
|
|
89
89
|
renderWorkspace(S.state.host);
|
|
90
90
|
renderSessionsDropdown();
|
|
91
|
+
setStaleBar(!!(snapshot && snapshot.i18nStale));
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
async function refresh() {
|
|
@@ -259,6 +260,7 @@ function applyStaticI18n() {
|
|
|
259
260
|
applySidebarI18n();
|
|
260
261
|
if (msgs.querySelector(".empty")) showEmptyFeed();
|
|
261
262
|
if (connStatus && !connStatus.hidden) setConnStatus(true);
|
|
263
|
+
setStaleBar(undefined, true);
|
|
262
264
|
}
|
|
263
265
|
|
|
264
266
|
async function boot() {
|
package/dist/assets/index.html
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
<button class="icon-btn" id="toggleSidebar" title="sidebar" aria-label="sidebar" aria-expanded="false">▤</button>
|
|
30
30
|
<button class="icon-btn" id="openSettings" title="settings" aria-label="settings" aria-haspopup="dialog" aria-expanded="false">⚙</button>
|
|
31
31
|
<span class="conn" id="connStatus" role="status" aria-live="polite" hidden></span>
|
|
32
|
+
<span class="conn stale" id="staleBar" role="status" hidden></span>
|
|
32
33
|
</div>
|
|
33
34
|
|
|
34
35
|
<div class="bar-sub" id="sessBar">
|
package/dist/assets/js/dom.js
CHANGED
|
@@ -14,6 +14,7 @@ export const attachStrip = document.getElementById("attachStrip");
|
|
|
14
14
|
export const attachErr = document.getElementById("attachErr");
|
|
15
15
|
export const imgPicker = document.getElementById("imgPicker");
|
|
16
16
|
export const connStatus = document.getElementById("connStatus");
|
|
17
|
+
export const staleBar = document.getElementById("staleBar");
|
|
17
18
|
export const liveEl = document.getElementById("live");
|
|
18
19
|
export const sendBtn = document.getElementById("sendBtn");
|
|
19
20
|
|
package/dist/assets/js/feed.js
CHANGED
|
@@ -444,56 +444,91 @@ function safeUrl(url) {
|
|
|
444
444
|
return null;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
function
|
|
447
|
+
function maybeGroupFilerow(feed, row) {
|
|
448
|
+
const prev = row.previousElementSibling;
|
|
449
|
+
const group = prev && prev.classList && prev.classList.contains("filegroup") ? prev : null;
|
|
450
|
+
const prevRow = prev && prev.classList && prev.classList.contains("filerow") ? prev : null;
|
|
451
|
+
if (group && group.dataset.open !== "1") {
|
|
452
|
+
group.querySelector(".items").appendChild(row);
|
|
453
|
+
if (row.dataset.fail === "1") group.dataset.fail = "1";
|
|
454
|
+
const n = group.querySelectorAll(".items > .filerow").length;
|
|
455
|
+
group.querySelector(".count").textContent = pluralCount("web.card.files", n);
|
|
456
|
+
group.querySelector(".ok").textContent = group.dataset.fail === "1" ? " ✗" : " ✓";
|
|
457
|
+
return group;
|
|
458
|
+
}
|
|
459
|
+
if (prevRow) {
|
|
460
|
+
const wrap = el("div", "filegroup");
|
|
461
|
+
const head = el("button", "head");
|
|
462
|
+
head.setAttribute("aria-expanded", "false");
|
|
463
|
+
head.appendChild(el("span", "g", "│ "));
|
|
464
|
+
head.appendChild(el("span", "count", pluralCount("web.card.files", 2)));
|
|
465
|
+
const fail = prevRow.dataset.fail === "1" || row.dataset.fail === "1";
|
|
466
|
+
if (fail) wrap.dataset.fail = "1";
|
|
467
|
+
head.appendChild(el("span", "ok", fail ? " ✗" : " ✓"));
|
|
468
|
+
const chev = icon("chevron-down");
|
|
469
|
+
head.appendChild(chev);
|
|
470
|
+
const items = el("div", "items hidden");
|
|
471
|
+
feed.replaceChild(wrap, prevRow);
|
|
472
|
+
items.appendChild(prevRow);
|
|
473
|
+
items.appendChild(row);
|
|
474
|
+
wrap.appendChild(head);
|
|
475
|
+
wrap.appendChild(items);
|
|
476
|
+
head.addEventListener("click", () => {
|
|
477
|
+
const open = wrap.dataset.open === "1";
|
|
478
|
+
wrap.dataset.open = open ? "" : "1";
|
|
479
|
+
head.setAttribute("aria-expanded", open ? "false" : "true");
|
|
480
|
+
items.classList.toggle("hidden", open);
|
|
481
|
+
});
|
|
482
|
+
return wrap;
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function cardFor(name, args, sid, ok) {
|
|
448
488
|
const feed = feedForEvent(sid);
|
|
449
489
|
if (!feed) return null;
|
|
450
490
|
if (FILE_TOOLS.indexOf(name) !== -1) {
|
|
451
491
|
const path = args.path || args.file || args.dir;
|
|
452
492
|
if (!path) return null;
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
nameEl.title = path;
|
|
458
|
-
top.appendChild(nameEl);
|
|
459
|
-
card.appendChild(top);
|
|
460
|
-
const bottom = el("div", "fc-bottom");
|
|
461
|
-
bottom.appendChild(el("span", "lab", labelOf(name)));
|
|
462
|
-
const actions = el("span", "fc-actions");
|
|
463
|
-
const baseName = String(path.split(/[\\/]/).pop() || path);
|
|
493
|
+
const row = el("div", "filerow " + categoryOf(name));
|
|
494
|
+
row.appendChild(el("span", "g", "◇"));
|
|
495
|
+
row.appendChild(el("span", "lab", labelOf(name)));
|
|
496
|
+
row.appendChild(el("span", "sep", " · "));
|
|
464
497
|
if (OPENABLE_TOOLS.indexOf(name) !== -1) {
|
|
465
|
-
const openLink = el("a", "
|
|
498
|
+
const openLink = el("a", "path", String(path).split(/[\\/]/).pop() || path);
|
|
466
499
|
openLink.href = "/api/file?path=" + encodeURIComponent(path);
|
|
467
500
|
openLink.target = "_blank";
|
|
468
501
|
openLink.rel = "noopener noreferrer";
|
|
469
|
-
openLink.title = t("web.card.open_title");
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
502
|
+
openLink.title = t("web.card.open_title") + " · " + path;
|
|
503
|
+
row.appendChild(openLink);
|
|
504
|
+
const tail = String(path).split(/[\\/]/).slice(0, -1).join("/");
|
|
505
|
+
if (tail) row.appendChild(el("span", "tail", " · " + tail));
|
|
506
|
+
} else {
|
|
507
|
+
const s = el("span", "tail", path);
|
|
508
|
+
s.title = path;
|
|
509
|
+
row.appendChild(s);
|
|
476
510
|
}
|
|
477
|
-
const copyBtn = el("button", "
|
|
511
|
+
const copyBtn = el("button", "copy-btn");
|
|
478
512
|
copyBtn.type = "button";
|
|
479
513
|
copyBtn.title = t("web.card.copy_title");
|
|
514
|
+
copyBtn.setAttribute("aria-label", t("web.card.copy_title"));
|
|
515
|
+
copyBtn.appendChild(icon("copy"));
|
|
480
516
|
copyBtn.addEventListener("click", () => {
|
|
481
517
|
copyText(path).then((ok) => {
|
|
482
|
-
copyBtn.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}, 1400);
|
|
518
|
+
copyBtn.replaceChildren(icon(ok ? "check" : "copy"));
|
|
519
|
+
if (!ok) { const st = row.querySelector(".st"); if (st) st.textContent = " ⚠"; }
|
|
520
|
+
window.setTimeout(() => copyBtn.replaceChildren(icon("copy")), 1400);
|
|
486
521
|
});
|
|
487
522
|
});
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if (/\.drawio(\.xml)?$/i.test(path)) {
|
|
492
|
-
|
|
493
|
-
card.appendChild(hint);
|
|
523
|
+
row.appendChild(copyBtn);
|
|
524
|
+
row.appendChild(el("span", "st", ok === false ? " ✗" : ok === true ? " ✓" : ""));
|
|
525
|
+
if (ok === false) row.dataset.fail = "1";
|
|
526
|
+
if (/\.drawio(\.xml)?$/i.test(path) && verbosity() === "verbose") {
|
|
527
|
+
row.appendChild(el("span", "detail", "↳ " + t("web.card.drawio_hint")));
|
|
494
528
|
}
|
|
495
|
-
appendTo(feed,
|
|
496
|
-
|
|
529
|
+
appendTo(feed, row);
|
|
530
|
+
try { maybeGroupFilerow(feed, row); } catch (err) { /* fail-open: plain row stays */ }
|
|
531
|
+
return row;
|
|
497
532
|
}
|
|
498
533
|
if (LINK_TOOLS.indexOf(name) !== -1 && args.url) {
|
|
499
534
|
const line = el("div", "linkline");
|
|
@@ -543,13 +578,37 @@ export function onToolEnd(data) {
|
|
|
543
578
|
S.toolNodes.delete(data.toolCallId);
|
|
544
579
|
const args = S.pendingArgs.get(data.toolCallId) || {};
|
|
545
580
|
S.pendingArgs.delete(data.toolCallId);
|
|
581
|
+
// File tools render as a single filerow (viewer link + copy): the `.act`
|
|
582
|
+
// line from `onToolStart` is replaced, otherwise the feed shows the same
|
|
583
|
+
// action twice (act + filerow). Without a path `cardFor` renders nothing,
|
|
584
|
+
// so keep the generic act line in that case.
|
|
585
|
+
const filePath = args.path || args.file || args.dir;
|
|
586
|
+
if (FILE_TOOLS.indexOf(data.name) !== -1 && filePath) {
|
|
587
|
+
if (line && line.parentNode) line.remove();
|
|
588
|
+
if (verbosity() === "quiet") {
|
|
589
|
+
if (data.success) {
|
|
590
|
+
const card = cardFor(data.name, args, data.sessionId, data.success);
|
|
591
|
+
if (card) S.runNodes.push(card);
|
|
592
|
+
S.runDuration += typeof data.durationMs === "number" ? data.durationMs : 0;
|
|
593
|
+
} else {
|
|
594
|
+
const card = cardFor(data.name, args, data.sessionId, data.success);
|
|
595
|
+
const cause = firstOutputLine(data.output);
|
|
596
|
+
if (cause) insertAfter(card || line, el("div", "detail", "↳ " + cause));
|
|
597
|
+
if (card) S.runNodes.push(card);
|
|
598
|
+
}
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const card = cardFor(data.name, args, data.sessionId, data.success);
|
|
602
|
+
renderToolOutput(card || line, data.output);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
546
605
|
if (verbosity() === "quiet") {
|
|
547
606
|
// Quiet hides tool bodies. Track successful nodes so a run of read-only
|
|
548
607
|
// actions can fold at the next segment; a failure keeps its line and adds
|
|
549
608
|
// a single `↳` cause instead of the whole model-facing body.
|
|
550
609
|
if (data.success) {
|
|
551
610
|
if (line) S.runNodes.push(line);
|
|
552
|
-
const card = cardFor(data.name, args, data.sessionId);
|
|
611
|
+
const card = cardFor(data.name, args, data.sessionId, data.success);
|
|
553
612
|
if (card) S.runNodes.push(card);
|
|
554
613
|
S.runDuration += typeof data.durationMs === "number" ? data.durationMs : 0;
|
|
555
614
|
} else {
|
|
@@ -558,7 +617,7 @@ export function onToolEnd(data) {
|
|
|
558
617
|
}
|
|
559
618
|
return;
|
|
560
619
|
}
|
|
561
|
-
if (data.success) cardFor(data.name, args, data.sessionId);
|
|
620
|
+
if (data.success) cardFor(data.name, args, data.sessionId, data.success);
|
|
562
621
|
renderToolOutput(line, data.output);
|
|
563
622
|
}
|
|
564
623
|
|
|
@@ -980,6 +1039,58 @@ function usageRow(label, value, cls) {
|
|
|
980
1039
|
return row;
|
|
981
1040
|
}
|
|
982
1041
|
|
|
1042
|
+
// Segmented context bar: system · tools · history · reserve · free, widths
|
|
1043
|
+
// relative to the full context window so the parts always sum to one bar.
|
|
1044
|
+
function contextSegBar(h, used, limit) {
|
|
1045
|
+
const bar = el("div", "bar segbar");
|
|
1046
|
+
const bd = h.contextBreakdown;
|
|
1047
|
+
if (!bd || typeof bd.window !== "number" || bd.window <= 0) {
|
|
1048
|
+
// Legacy fallback (pre-breakdown payloads): single used/limit fill.
|
|
1049
|
+
const fill = el("i");
|
|
1050
|
+
fill.style.width = Math.min(100, Math.round((used / limit) * 100)) + "%";
|
|
1051
|
+
bar.appendChild(fill);
|
|
1052
|
+
return bar;
|
|
1053
|
+
}
|
|
1054
|
+
const denom = Math.max(bd.window, bd.used + bd.reserve);
|
|
1055
|
+
const parts = [
|
|
1056
|
+
["seg-system", bd.system],
|
|
1057
|
+
["seg-tools", bd.tools],
|
|
1058
|
+
["seg-history", bd.history],
|
|
1059
|
+
["seg-reserve", bd.reserve],
|
|
1060
|
+
["seg-free", bd.free],
|
|
1061
|
+
];
|
|
1062
|
+
for (const [cls, n] of parts) {
|
|
1063
|
+
if (!n || n <= 0) continue;
|
|
1064
|
+
const seg = el("i", cls);
|
|
1065
|
+
seg.style.width = (n / denom) * 100 + "%";
|
|
1066
|
+
bar.appendChild(seg);
|
|
1067
|
+
}
|
|
1068
|
+
return bar;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// Dim legend under the segmented bar: one dot+label+value per context part.
|
|
1072
|
+
function contextLegend(h) {
|
|
1073
|
+
const box = el("div", "seglegend");
|
|
1074
|
+
const bd = h.contextBreakdown;
|
|
1075
|
+
if (!bd) return box;
|
|
1076
|
+
const items = [
|
|
1077
|
+
["seg-system", "web.usage.part_system", bd.system],
|
|
1078
|
+
["seg-tools", "web.usage.part_tools", bd.tools],
|
|
1079
|
+
["seg-history", "web.usage.part_history", bd.history],
|
|
1080
|
+
["seg-reserve", "web.usage.part_reserve", bd.reserve],
|
|
1081
|
+
["seg-free", "web.usage.part_free", bd.free],
|
|
1082
|
+
];
|
|
1083
|
+
for (const [cls, labelKey, n] of items) {
|
|
1084
|
+
if (!n || n <= 0) continue;
|
|
1085
|
+
const item = el("span", "segl-item");
|
|
1086
|
+
item.appendChild(el("i", "segl-dot " + cls));
|
|
1087
|
+
item.appendChild(el("span", "segl-label", t(labelKey)));
|
|
1088
|
+
item.appendChild(el("b", "", fmtTokens(n)));
|
|
1089
|
+
box.appendChild(item);
|
|
1090
|
+
}
|
|
1091
|
+
return box;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
983
1094
|
// Session row with a copy button next to the id (icon-only, ✓ on success).
|
|
984
1095
|
function usageSessionRow(sessionId) {
|
|
985
1096
|
const row = el("div", "kv");
|
|
@@ -1010,7 +1121,7 @@ export function renderUsage(h) {
|
|
|
1010
1121
|
const cfg = S.state.config || {};
|
|
1011
1122
|
const sig = JSON.stringify([
|
|
1012
1123
|
h.sessionId, h.model, h.contextUsed, h.contextLimit, h.cacheHitRate, h.costUsd,
|
|
1013
|
-
h.compactionCount, h.contextQuality, h.contextProbe, cfg.model, cfg.contextWindow,
|
|
1124
|
+
h.compactionCount, h.contextQuality, h.contextProbe, h.contextBreakdown, cfg.model, cfg.contextWindow,
|
|
1014
1125
|
I18N.locale,
|
|
1015
1126
|
]);
|
|
1016
1127
|
if (sig === S.lastUsageSig) return;
|
|
@@ -1023,11 +1134,8 @@ export function renderUsage(h) {
|
|
|
1023
1134
|
const limit = typeof h.contextLimit === "number" ? h.contextLimit : cfg.contextWindow || null;
|
|
1024
1135
|
usageEl.appendChild(usageRow(t("web.usage.context"), used != null && limit ? fmtTokens(used) + " / " + fmtTokens(limit) : "—"));
|
|
1025
1136
|
if (used != null && limit) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
fill.style.width = Math.min(100, Math.round((used / limit) * 100)) + "%";
|
|
1029
|
-
bar.appendChild(fill);
|
|
1030
|
-
usageEl.appendChild(bar);
|
|
1137
|
+
usageEl.appendChild(contextSegBar(h, used, limit));
|
|
1138
|
+
usageEl.appendChild(contextLegend(h));
|
|
1031
1139
|
}
|
|
1032
1140
|
const hit = typeof h.cacheHitRate === "number" ? h.cacheHitRate : null;
|
|
1033
1141
|
usageEl.appendChild(usageRow(t("web.usage.cache"), hit != null ? Math.round(hit * 100) + "%" : "—", hit != null ? "" : "dim"));
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// share from tabs; confirm/toast from dialog. Never imports app (the locale
|
|
4
4
|
// refresh reaches the app shell through the store hooks registry).
|
|
5
5
|
import {
|
|
6
|
-
el, icon, link, inlineTextNodes, connStatus, makeToggle,
|
|
6
|
+
el, icon, link, inlineTextNodes, connStatus, staleBar, makeToggle,
|
|
7
7
|
} from "./dom.js";
|
|
8
8
|
import { api } from "./api.js";
|
|
9
9
|
import { t, loadI18n } from "./i18n.js";
|
|
@@ -324,6 +324,26 @@ export function setConnStatus(disconnected) {
|
|
|
324
324
|
connStatus.textContent = disconnected ? "⋯ " + t("web.conn.reconnecting") : "";
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
// Stale-translations banner: dict files on disk are newer than the running
|
|
328
|
+
// server process, so the chrome may show raw keys. Rebuilt only on S.state
|
|
329
|
+
// change (not on every 2s poll) to keep the reload-button focus stable;
|
|
330
|
+
// `force` re-renders the label after a locale switch.
|
|
331
|
+
let lastStale = null;
|
|
332
|
+
export function setStaleBar(stale, force) {
|
|
333
|
+
if (!staleBar) return;
|
|
334
|
+
// undefined = перерисовать последнее состояние (смена локали).
|
|
335
|
+
const on = stale === undefined ? !!lastStale : !!stale;
|
|
336
|
+
if (!force && on === lastStale) return;
|
|
337
|
+
lastStale = on;
|
|
338
|
+
staleBar.hidden = !on;
|
|
339
|
+
staleBar.textContent = "";
|
|
340
|
+
if (!on) return;
|
|
341
|
+
staleBar.appendChild(el("span", "", "⚠ " + t("web.i18n.stale") + " "));
|
|
342
|
+
staleBar.appendChild(link(t("web.i18n.reload"), "", () => location.reload()));
|
|
343
|
+
}
|
|
344
|
+
return lastStale;
|
|
345
|
+
}
|
|
346
|
+
|
|
327
347
|
// Panel → named sections lives in `settings-controls.js` (PANEL_SECTIONS).
|
|
328
348
|
// Custom panels (model/ui/browser…) render a bespoke body first and then their
|
|
329
349
|
// sections; purely generic panels (general/context/…) render sections only.
|
package/dist/assets/styles.css
CHANGED
|
@@ -54,6 +54,12 @@
|
|
|
54
54
|
--checker-border: #2e2e2e;
|
|
55
55
|
--ph-light: #eaeef2;
|
|
56
56
|
--track: #262626;
|
|
57
|
+
/* Context-breakdown segments (system/tools/history/reserve/free). */
|
|
58
|
+
--seg-system: #a78bfa;
|
|
59
|
+
--seg-tools: #38bdf8;
|
|
60
|
+
--seg-history: #34d399;
|
|
61
|
+
--seg-reserve: #fbbf24;
|
|
62
|
+
--seg-free: #3f3f46;
|
|
57
63
|
--plan-line: #2e2e2e;
|
|
58
64
|
--plan-bar-fg: #a3a3a3;
|
|
59
65
|
--sec-h: #a3a3a3;
|
|
@@ -124,6 +130,12 @@ body.light {
|
|
|
124
130
|
--checker-border: #d0d7de;
|
|
125
131
|
--ph-light: #eaeef2;
|
|
126
132
|
--track: #eaeef2;
|
|
133
|
+
/* Context-breakdown segments (system/tools/history/reserve/free). */
|
|
134
|
+
--seg-system: #7c3aed;
|
|
135
|
+
--seg-tools: #0284c7;
|
|
136
|
+
--seg-history: #059669;
|
|
137
|
+
--seg-reserve: #d97706;
|
|
138
|
+
--seg-free: #d0d7de;
|
|
127
139
|
--plan-line: #d0d7de;
|
|
128
140
|
--plan-bar-fg: #57606a;
|
|
129
141
|
--sec-h: #57606a;
|
|
@@ -167,6 +179,8 @@ body[data-font="large"] { font-size: 17px; }
|
|
|
167
179
|
#openSettings { margin-left: auto; margin-right: 6px; }
|
|
168
180
|
#toggleSidebar { display: none; }
|
|
169
181
|
.conn { color: var(--dim-a); font-size: 11.5px; white-space: nowrap; }
|
|
182
|
+
.conn.stale { color: var(--yellow); }
|
|
183
|
+
.conn.stale .linkbtn { font-size: 11.5px; }
|
|
170
184
|
.tabstrip {
|
|
171
185
|
display: flex; gap: 4px; align-items: center; overflow-x: auto;
|
|
172
186
|
flex: 1; min-width: 0;
|
|
@@ -377,31 +391,23 @@ body.light .codeblock { border: 1px solid var(--border); }
|
|
|
377
391
|
}
|
|
378
392
|
body.light .imgcard .ph { background: var(--ph-light); }
|
|
379
393
|
.imgcard .cap { padding: 6px 10px; color: var(--dim-a); font-size: 12px; }
|
|
380
|
-
.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
.
|
|
387
|
-
.
|
|
388
|
-
.
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
.
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
.
|
|
395
|
-
.
|
|
396
|
-
|
|
397
|
-
color: var(--accent); background: transparent; border: 1px solid var(--border-2);
|
|
398
|
-
border-radius: 4px; padding: 2px 10px; cursor: pointer; text-decoration: none;
|
|
399
|
-
}
|
|
400
|
-
.filecard .fc-open:hover { border-color: var(--accent); text-decoration: none; }
|
|
401
|
-
.filecard .fc-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
402
|
-
.filecard button.fc-open { font: inherit; }
|
|
403
|
-
.filecard .fc-actions { display: flex; gap: 6px; align-items: center; margin-left: auto; flex: 0 0 auto; }
|
|
404
|
-
.filecard .fc-hint { color: var(--dim-a); font-size: 12px; }
|
|
394
|
+
.filerow { margin-left: 26px; display: flex; gap: 8px; align-items: baseline; min-width: 0; font-size: 12.5px; color: var(--muted); }
|
|
395
|
+
.filerow .g { flex: 0 0 auto; }
|
|
396
|
+
.filerow.recon .g, .filerow.recon .lab { color: var(--cyan); }
|
|
397
|
+
.filerow.mutate .g, .filerow.mutate .lab { color: var(--magenta); }
|
|
398
|
+
.filerow .sep { color: var(--dim-b); }
|
|
399
|
+
.filerow a.path { color: var(--accent); text-decoration: none; overflow-wrap: anywhere; word-break: break-word; }
|
|
400
|
+
.filerow a.path:hover { text-decoration: underline; }
|
|
401
|
+
.filerow .tail { color: var(--dim-a); overflow-wrap: anywhere; word-break: break-word; }
|
|
402
|
+
.filerow .copy-btn { background: transparent; border: 0; padding: 2px; color: var(--dim-a); cursor: pointer; opacity: 0; }
|
|
403
|
+
.filerow:hover .copy-btn, .filerow:focus-within .copy-btn { opacity: 1; }
|
|
404
|
+
@media (pointer: coarse) { .filerow .copy-btn { opacity: 1; } }
|
|
405
|
+
.filerow .copy-btn:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
406
|
+
.filerow .copy-btn .svg-icon { width: 14px; height: 14px; }
|
|
407
|
+
.filegroup { margin-left: 26px; font-size: 12.5px; color: var(--muted); }
|
|
408
|
+
.filegroup .head { background: transparent; border: 0; color: inherit; font: inherit; cursor: pointer; display: flex; gap: 6px; align-items: baseline; padding: 0; }
|
|
409
|
+
.filegroup .items.hidden { display: none; }
|
|
410
|
+
.filegroup .items > .filerow { margin-left: 0; }
|
|
405
411
|
.linkline { margin-left: 26px; min-width: 0; overflow-wrap: anywhere; }
|
|
406
412
|
.linkline a { color: var(--accent); text-decoration: none; overflow-wrap: anywhere; word-break: break-word; }
|
|
407
413
|
.linkline a:hover { text-decoration: underline; }
|
|
@@ -613,7 +619,7 @@ body.light .sel > button { border: 1px solid var(--border); }
|
|
|
613
619
|
|
|
614
620
|
/* new rows rise in softly (a bulk history render animates once as a whole —
|
|
615
621
|
one shared fade, still cheap: opacity/transform only) */
|
|
616
|
-
.feed > :is(.u, .md, .act, .think, .q, .
|
|
622
|
+
.feed > :is(.u, .md, .act, .think, .q, .filerow, .filegroup, .planblock, .imgcard, .linkline,
|
|
617
623
|
.toolout, .metric, .detail, .metaln, .warnline, .errline, .cmdout, .sysline,
|
|
618
624
|
.startbanner, .grouped, .msg-actions, .busy) {
|
|
619
625
|
animation: rise .18s ease-out;
|
|
@@ -640,7 +646,7 @@ body.light .sel > button { border: 1px solid var(--border); }
|
|
|
640
646
|
.overlay.open .modal { animation: rise .18s ease-out; }
|
|
641
647
|
|
|
642
648
|
/* hover/active blends */
|
|
643
|
-
.sel > button, .menu .mi, .stab, .q .qopt, .q .qsend, .
|
|
649
|
+
.sel > button, .menu .mi, .stab, .q .qopt, .q .qsend, .filerow, .filegroup, .icon-btn,
|
|
644
650
|
.btn, .linkbtn, .chip, .cb-copy, .act-btn, .copy-btn, .composer .attach-btn,
|
|
645
651
|
.composer .send-btn, .dd-menu .si, .dd-menu .act, .cmdk-item, .toggle {
|
|
646
652
|
transition: background-color .12s, color .12s, border-color .12s;
|
|
@@ -849,6 +855,26 @@ body.light .kv .wn { color: var(--yellow); }
|
|
|
849
855
|
.bar { height: 4px; background: var(--track); border-radius: 2px; margin-top: 6px; overflow: hidden; }
|
|
850
856
|
.bar i { display: block; height: 100%; width: 0; background: var(--green-fill); transition: width .2s; }
|
|
851
857
|
|
|
858
|
+
/* Segmented context bar: flex row so widths add up to the full window. */
|
|
859
|
+
.bar.segbar { display: flex; }
|
|
860
|
+
.bar.segbar i { width: auto; flex: 0 0 auto; transition: width .25s; }
|
|
861
|
+
.bar.segbar .seg-system { background: var(--seg-system); }
|
|
862
|
+
.bar.segbar .seg-tools { background: var(--seg-tools); }
|
|
863
|
+
.bar.segbar .seg-history { background: var(--seg-history); }
|
|
864
|
+
.bar.segbar .seg-reserve { background: var(--seg-reserve); }
|
|
865
|
+
.bar.segbar .seg-free { background: var(--seg-free); }
|
|
866
|
+
/* Legend of context parts (dim dot + label + tokens): rows, no boxes. */
|
|
867
|
+
.seglegend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 5px; }
|
|
868
|
+
.segl-item { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dim-b); }
|
|
869
|
+
.segl-dot { width: 7px; height: 7px; border-radius: 2px; flex: 0 0 auto; }
|
|
870
|
+
.segl-dot.seg-system { background: var(--seg-system); }
|
|
871
|
+
.segl-dot.seg-tools { background: var(--seg-tools); }
|
|
872
|
+
.segl-dot.seg-history { background: var(--seg-history); }
|
|
873
|
+
.segl-dot.seg-reserve { background: var(--seg-reserve); }
|
|
874
|
+
.segl-dot.seg-free { background: var(--seg-free); }
|
|
875
|
+
.segl-item b { color: var(--muted); font-weight: 600; }
|
|
876
|
+
.segl-label { color: var(--dim-b); }
|
|
877
|
+
|
|
852
878
|
/* ---- settings modal + nested dialogs (provider, confirm) ---- */
|
|
853
879
|
.overlay {
|
|
854
880
|
display: none; position: fixed; inset: 0; z-index: 50;
|
package/dist/i18n/en.json
CHANGED
|
@@ -592,6 +592,9 @@
|
|
|
592
592
|
"repl.model": "Model:",
|
|
593
593
|
"repl.provider": "Provider:",
|
|
594
594
|
"repl.context": "Context:",
|
|
595
|
+
"repl.ctx_part_system": "system",
|
|
596
|
+
"repl.ctx_part_reserve": "reserve",
|
|
597
|
+
"repl.ctx_part_history": "history",
|
|
595
598
|
"repl.sysprompt_label": "System prompt:",
|
|
596
599
|
"repl.sysprompt_size": "{used} / {budget} tokens",
|
|
597
600
|
"repl.sysprompt_desc": "System prompt size and blocks (use --full for the text)",
|
|
@@ -1052,6 +1055,8 @@
|
|
|
1052
1055
|
"web.sessbar.prev": "previous session",
|
|
1053
1056
|
"web.sessbar.next": "next session",
|
|
1054
1057
|
"web.conn.reconnecting": "reconnecting…",
|
|
1058
|
+
"web.i18n.stale": "translations updated — restart the server",
|
|
1059
|
+
"web.i18n.reload": "reload page",
|
|
1055
1060
|
"web.sidebar.plan": "plan",
|
|
1056
1061
|
"web.sidebar.session": "session",
|
|
1057
1062
|
"web.sidebar.usage": "usage",
|
|
@@ -1118,6 +1123,11 @@
|
|
|
1118
1123
|
"web.usage.cache": "cache",
|
|
1119
1124
|
"web.usage.cost": "cost",
|
|
1120
1125
|
"web.usage.compactions": "compactions",
|
|
1126
|
+
"web.usage.part_system": "instructions",
|
|
1127
|
+
"web.usage.part_tools": "tools",
|
|
1128
|
+
"web.usage.part_history": "dialogue",
|
|
1129
|
+
"web.usage.part_reserve": "response reserve",
|
|
1130
|
+
"web.usage.part_free": "free",
|
|
1121
1131
|
"web.usage.quality": "quality",
|
|
1122
1132
|
"web.usage.probe": "probe",
|
|
1123
1133
|
"web.usage.probe_loaded": "{actual} loaded",
|
|
@@ -1500,13 +1510,12 @@
|
|
|
1500
1510
|
"web.code.copy_failed": "copy failed",
|
|
1501
1511
|
"web.msg.copy": "copy",
|
|
1502
1512
|
"web.msg.copy_md": "copy as markdown",
|
|
1503
|
-
"web.card.open": "open ↗",
|
|
1504
|
-
"web.card.download": "download ⤓",
|
|
1505
|
-
"web.card.copy_path": "copy path",
|
|
1506
1513
|
"web.card.open_title": "Preview in a browser tab",
|
|
1507
|
-
"web.card.download_title": "Download the file",
|
|
1508
1514
|
"web.card.copy_title": "Copy the workspace path",
|
|
1509
1515
|
"web.card.drawio_hint": "Draw.io diagram is XML — preview shows code; open it in draw.io / VS Code to see the scheme.",
|
|
1516
|
+
"web.card.files_one": "{count} file",
|
|
1517
|
+
"web.card.files_few": "{count} files",
|
|
1518
|
+
"web.card.files_many": "{count} files",
|
|
1510
1519
|
"web.card.screenshot": "screenshot",
|
|
1511
1520
|
"web.card.attached_image": "attached image",
|
|
1512
1521
|
"web.card.browser_viewport": "browser · viewport",
|
package/dist/i18n/ru.json
CHANGED
|
@@ -589,6 +589,9 @@
|
|
|
589
589
|
"repl.model": "Модель:",
|
|
590
590
|
"repl.provider": "Провайдер:",
|
|
591
591
|
"repl.context": "Контекст:",
|
|
592
|
+
"repl.ctx_part_system": "система",
|
|
593
|
+
"repl.ctx_part_reserve": "резерв",
|
|
594
|
+
"repl.ctx_part_history": "история",
|
|
592
595
|
"repl.sysprompt_label": "Системный промпт:",
|
|
593
596
|
"repl.sysprompt_size": "{used} / {budget} токенов",
|
|
594
597
|
"repl.sysprompt_desc": "Размер системного промпта и блоки (текст — через --full)",
|
|
@@ -1055,6 +1058,8 @@
|
|
|
1055
1058
|
"web.sessbar.prev": "предыдущая сессия",
|
|
1056
1059
|
"web.sessbar.next": "следующая сессия",
|
|
1057
1060
|
"web.conn.reconnecting": "переподключение…",
|
|
1061
|
+
"web.i18n.stale": "переводы обновлены — перезапустите сервер",
|
|
1062
|
+
"web.i18n.reload": "обновить страницу",
|
|
1058
1063
|
"web.sidebar.plan": "план",
|
|
1059
1064
|
"web.sidebar.session": "сессия",
|
|
1060
1065
|
"web.sidebar.usage": "использование",
|
|
@@ -1121,6 +1126,11 @@
|
|
|
1121
1126
|
"web.usage.cache": "кеш",
|
|
1122
1127
|
"web.usage.cost": "стоимость",
|
|
1123
1128
|
"web.usage.compactions": "сжатия",
|
|
1129
|
+
"web.usage.part_system": "инструкции",
|
|
1130
|
+
"web.usage.part_tools": "инструменты",
|
|
1131
|
+
"web.usage.part_history": "диалог",
|
|
1132
|
+
"web.usage.part_reserve": "резерв ответа",
|
|
1133
|
+
"web.usage.part_free": "свободно",
|
|
1124
1134
|
"web.usage.quality": "качество",
|
|
1125
1135
|
"web.usage.probe": "проб",
|
|
1126
1136
|
"web.usage.probe_loaded": "загружено {actual}",
|
|
@@ -1476,13 +1486,12 @@
|
|
|
1476
1486
|
"web.code.copy_failed": "не скопировано",
|
|
1477
1487
|
"web.msg.copy": "копировать",
|
|
1478
1488
|
"web.msg.copy_md": "копировать как markdown",
|
|
1479
|
-
"web.card.open": "открыть ↗",
|
|
1480
|
-
"web.card.download": "скачать ⤓",
|
|
1481
|
-
"web.card.copy_path": "путь",
|
|
1482
1489
|
"web.card.open_title": "Предпросмотр во вкладке браузера",
|
|
1483
|
-
"web.card.download_title": "Скачать файл",
|
|
1484
1490
|
"web.card.copy_title": "Скопировать путь в рабочей папке",
|
|
1485
1491
|
"web.card.drawio_hint": "Диаграмма draw.io — это XML: предпросмотр покажет код, схему смотри в draw.io / VS Code.",
|
|
1492
|
+
"web.card.files_one": "{count} файл",
|
|
1493
|
+
"web.card.files_few": "{count} файла",
|
|
1494
|
+
"web.card.files_many": "{count} файлов",
|
|
1486
1495
|
"web.card.screenshot": "снимок экрана",
|
|
1487
1496
|
"web.card.attached_image": "прикреплённое изображение",
|
|
1488
1497
|
"web.card.browser_viewport": "браузер · область просмотра",
|
package/dist/main.js
CHANGED
|
@@ -3073,6 +3073,9 @@ Available commands:`,
|
|
|
3073
3073
|
"repl.model": "Model:",
|
|
3074
3074
|
"repl.provider": "Provider:",
|
|
3075
3075
|
"repl.context": "Context:",
|
|
3076
|
+
"repl.ctx_part_system": "system",
|
|
3077
|
+
"repl.ctx_part_reserve": "reserve",
|
|
3078
|
+
"repl.ctx_part_history": "history",
|
|
3076
3079
|
"repl.sysprompt_label": "System prompt:",
|
|
3077
3080
|
"repl.sysprompt_size": "{used} / {budget} tokens",
|
|
3078
3081
|
"repl.sysprompt_desc": "System prompt size and blocks (use --full for the text)",
|
|
@@ -3577,6 +3580,8 @@ Updated: {updatedAt}`,
|
|
|
3577
3580
|
"web.sessbar.prev": "previous session",
|
|
3578
3581
|
"web.sessbar.next": "next session",
|
|
3579
3582
|
"web.conn.reconnecting": "reconnecting…",
|
|
3583
|
+
"web.i18n.stale": "translations updated — restart the server",
|
|
3584
|
+
"web.i18n.reload": "reload page",
|
|
3580
3585
|
"web.sidebar.plan": "plan",
|
|
3581
3586
|
"web.sidebar.session": "session",
|
|
3582
3587
|
"web.sidebar.usage": "usage",
|
|
@@ -3643,6 +3648,11 @@ Updated: {updatedAt}`,
|
|
|
3643
3648
|
"web.usage.cache": "cache",
|
|
3644
3649
|
"web.usage.cost": "cost",
|
|
3645
3650
|
"web.usage.compactions": "compactions",
|
|
3651
|
+
"web.usage.part_system": "instructions",
|
|
3652
|
+
"web.usage.part_tools": "tools",
|
|
3653
|
+
"web.usage.part_history": "dialogue",
|
|
3654
|
+
"web.usage.part_reserve": "response reserve",
|
|
3655
|
+
"web.usage.part_free": "free",
|
|
3646
3656
|
"web.usage.quality": "quality",
|
|
3647
3657
|
"web.usage.probe": "probe",
|
|
3648
3658
|
"web.usage.probe_loaded": "{actual} loaded",
|
|
@@ -4025,13 +4035,12 @@ Updated: {updatedAt}`,
|
|
|
4025
4035
|
"web.code.copy_failed": "copy failed",
|
|
4026
4036
|
"web.msg.copy": "copy",
|
|
4027
4037
|
"web.msg.copy_md": "copy as markdown",
|
|
4028
|
-
"web.card.open": "open ↗",
|
|
4029
|
-
"web.card.download": "download ⤓",
|
|
4030
|
-
"web.card.copy_path": "copy path",
|
|
4031
4038
|
"web.card.open_title": "Preview in a browser tab",
|
|
4032
|
-
"web.card.download_title": "Download the file",
|
|
4033
4039
|
"web.card.copy_title": "Copy the workspace path",
|
|
4034
4040
|
"web.card.drawio_hint": "Draw.io diagram is XML — preview shows code; open it in draw.io / VS Code to see the scheme.",
|
|
4041
|
+
"web.card.files_one": "{count} file",
|
|
4042
|
+
"web.card.files_few": "{count} files",
|
|
4043
|
+
"web.card.files_many": "{count} files",
|
|
4035
4044
|
"web.card.screenshot": "screenshot",
|
|
4036
4045
|
"web.card.attached_image": "attached image",
|
|
4037
4046
|
"web.card.browser_viewport": "browser · viewport",
|
|
@@ -4973,6 +4982,9 @@ var init_ru = __esm(() => {
|
|
|
4973
4982
|
"repl.model": "Модель:",
|
|
4974
4983
|
"repl.provider": "Провайдер:",
|
|
4975
4984
|
"repl.context": "Контекст:",
|
|
4985
|
+
"repl.ctx_part_system": "система",
|
|
4986
|
+
"repl.ctx_part_reserve": "резерв",
|
|
4987
|
+
"repl.ctx_part_history": "история",
|
|
4976
4988
|
"repl.sysprompt_label": "Системный промпт:",
|
|
4977
4989
|
"repl.sysprompt_size": "{used} / {budget} токенов",
|
|
4978
4990
|
"repl.sysprompt_desc": "Размер системного промпта и блоки (текст — через --full)",
|
|
@@ -5484,6 +5496,8 @@ var init_ru = __esm(() => {
|
|
|
5484
5496
|
"web.sessbar.prev": "предыдущая сессия",
|
|
5485
5497
|
"web.sessbar.next": "следующая сессия",
|
|
5486
5498
|
"web.conn.reconnecting": "переподключение…",
|
|
5499
|
+
"web.i18n.stale": "переводы обновлены — перезапустите сервер",
|
|
5500
|
+
"web.i18n.reload": "обновить страницу",
|
|
5487
5501
|
"web.sidebar.plan": "план",
|
|
5488
5502
|
"web.sidebar.session": "сессия",
|
|
5489
5503
|
"web.sidebar.usage": "использование",
|
|
@@ -5550,6 +5564,11 @@ var init_ru = __esm(() => {
|
|
|
5550
5564
|
"web.usage.cache": "кеш",
|
|
5551
5565
|
"web.usage.cost": "стоимость",
|
|
5552
5566
|
"web.usage.compactions": "сжатия",
|
|
5567
|
+
"web.usage.part_system": "инструкции",
|
|
5568
|
+
"web.usage.part_tools": "инструменты",
|
|
5569
|
+
"web.usage.part_history": "диалог",
|
|
5570
|
+
"web.usage.part_reserve": "резерв ответа",
|
|
5571
|
+
"web.usage.part_free": "свободно",
|
|
5553
5572
|
"web.usage.quality": "качество",
|
|
5554
5573
|
"web.usage.probe": "проб",
|
|
5555
5574
|
"web.usage.probe_loaded": "загружено {actual}",
|
|
@@ -5905,13 +5924,12 @@ var init_ru = __esm(() => {
|
|
|
5905
5924
|
"web.code.copy_failed": "не скопировано",
|
|
5906
5925
|
"web.msg.copy": "копировать",
|
|
5907
5926
|
"web.msg.copy_md": "копировать как markdown",
|
|
5908
|
-
"web.card.open": "открыть ↗",
|
|
5909
|
-
"web.card.download": "скачать ⤓",
|
|
5910
|
-
"web.card.copy_path": "путь",
|
|
5911
5927
|
"web.card.open_title": "Предпросмотр во вкладке браузера",
|
|
5912
|
-
"web.card.download_title": "Скачать файл",
|
|
5913
5928
|
"web.card.copy_title": "Скопировать путь в рабочей папке",
|
|
5914
5929
|
"web.card.drawio_hint": "Диаграмма draw.io — это XML: предпросмотр покажет код, схему смотри в draw.io / VS Code.",
|
|
5930
|
+
"web.card.files_one": "{count} файл",
|
|
5931
|
+
"web.card.files_few": "{count} файла",
|
|
5932
|
+
"web.card.files_many": "{count} файлов",
|
|
5915
5933
|
"web.card.screenshot": "снимок экрана",
|
|
5916
5934
|
"web.card.attached_image": "прикреплённое изображение",
|
|
5917
5935
|
"web.card.browser_viewport": "браузер · область просмотра",
|
|
@@ -11095,7 +11113,7 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
11095
11113
|
if (config.autoPlan) {
|
|
11096
11114
|
lines.push(`PLAN (MANDATORY): file creation, package installs, or multi-action tasks MUST create a "plan" BEFORE starting.`);
|
|
11097
11115
|
}
|
|
11098
|
-
lines.push(`Runtime verification: when a bug is described in terms of runtime behavior`, `(what happens when the app runs), reproduce it —`, `run the app / build / browser tool — confirm the fix against actual runtime output`, `before calling "plan_step step=N status=done".`, `Check existing servers first (process_list/process_log): a stale dev server`, `runs the old pipeline and keeps failing after the fix.`, `Restart/kill it (process_kill) and verify the exact endpoint/port reported —`, `a fresh port does NOT prove the user's error is gone.`);
|
|
11116
|
+
lines.push(`Runtime verification: when a bug is described in terms of runtime behavior`, `(what happens when the app runs), reproduce it —`, `run the app / build / browser tool — confirm the fix against actual runtime output`, `before calling "plan_step step=N status=done".`, `Check existing servers first (process_list/process_log): a stale dev server`, `runs the old pipeline and keeps failing after the fix.`, `Restart/kill it (process_kill) and verify the exact endpoint/port reported —`, `a fresh port does NOT prove the user's error is gone.`, `Verification "mma web" servers run isolated (MMA_CONFIG_DIR=<empty tmp dir>) —`, `never verify against the user's live server/sessions/state.`);
|
|
11099
11117
|
const sec = config.security;
|
|
11100
11118
|
if (sec?.enabled) {
|
|
11101
11119
|
const secLines = [
|
|
@@ -11908,6 +11926,18 @@ var init_walker = __esm(() => {
|
|
|
11908
11926
|
// src/modules/indexer/cache.ts
|
|
11909
11927
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync18, mkdirSync as mkdirSync11, rmSync } from "fs";
|
|
11910
11928
|
import { join as join20 } from "path";
|
|
11929
|
+
function isIndexedFile(v) {
|
|
11930
|
+
if (typeof v !== "object" || v === null)
|
|
11931
|
+
return false;
|
|
11932
|
+
const f = v;
|
|
11933
|
+
return typeof f.path === "string" && typeof f.language === "string" && Array.isArray(f.exports) && typeof f.size === "number";
|
|
11934
|
+
}
|
|
11935
|
+
function isIndexResult(v) {
|
|
11936
|
+
if (typeof v !== "object" || v === null)
|
|
11937
|
+
return false;
|
|
11938
|
+
const r = v;
|
|
11939
|
+
return Array.isArray(r.files) && r.files.every(isIndexedFile) && typeof r.totalSize === "number";
|
|
11940
|
+
}
|
|
11911
11941
|
|
|
11912
11942
|
class IndexCache {
|
|
11913
11943
|
cachePath;
|
|
@@ -11927,7 +11957,7 @@ class IndexCache {
|
|
|
11927
11957
|
return null;
|
|
11928
11958
|
try {
|
|
11929
11959
|
const parsed = JSON.parse(readFileSync14(this.cachePath, "utf-8"));
|
|
11930
|
-
if (parsed.version !== CACHE_VERSION || !parsed.result)
|
|
11960
|
+
if (parsed.version !== CACHE_VERSION || !isIndexResult(parsed.result))
|
|
11931
11961
|
return null;
|
|
11932
11962
|
if (this.root !== undefined && parsed.root !== this.root)
|
|
11933
11963
|
return null;
|
|
@@ -14394,6 +14424,40 @@ var init_write_file = __esm(() => {
|
|
|
14394
14424
|
// src/tools/edit-file.ts
|
|
14395
14425
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
14396
14426
|
import { resolve as resolve6 } from "path";
|
|
14427
|
+
function spliceNormalized(content, oldStr, newStr) {
|
|
14428
|
+
const normOld = oldStr.replace(/\r\n/g, `
|
|
14429
|
+
`);
|
|
14430
|
+
const norm = [];
|
|
14431
|
+
const map = [];
|
|
14432
|
+
for (let i = 0;i < content.length; i++) {
|
|
14433
|
+
if (content[i] === "\r" && content[i + 1] === `
|
|
14434
|
+
`) {
|
|
14435
|
+
norm.push(`
|
|
14436
|
+
`);
|
|
14437
|
+
map.push(i);
|
|
14438
|
+
i++;
|
|
14439
|
+
} else {
|
|
14440
|
+
norm.push(content[i]);
|
|
14441
|
+
map.push(i);
|
|
14442
|
+
}
|
|
14443
|
+
}
|
|
14444
|
+
const normContent = norm.join("");
|
|
14445
|
+
const idx = normContent.indexOf(normOld);
|
|
14446
|
+
if (idx === -1 || normOld.length === 0)
|
|
14447
|
+
return null;
|
|
14448
|
+
const startOrig = map[idx];
|
|
14449
|
+
const endNorm = idx + normOld.length;
|
|
14450
|
+
const endOrig = endNorm < map.length ? map[endNorm] : content.length;
|
|
14451
|
+
let insert = newStr;
|
|
14452
|
+
if (content.includes(`\r
|
|
14453
|
+
`) && !newStr.includes(`\r
|
|
14454
|
+
`) && newStr.includes(`
|
|
14455
|
+
`)) {
|
|
14456
|
+
insert = newStr.replace(/\n/g, `\r
|
|
14457
|
+
`);
|
|
14458
|
+
}
|
|
14459
|
+
return content.slice(0, startOrig) + insert + content.slice(endOrig);
|
|
14460
|
+
}
|
|
14397
14461
|
var editFileTool;
|
|
14398
14462
|
var init_edit_file = __esm(() => {
|
|
14399
14463
|
init_i18n();
|
|
@@ -14478,13 +14542,19 @@ var init_edit_file = __esm(() => {
|
|
|
14478
14542
|
output: t("file.read_failed", { path, error: errMsg(err) })
|
|
14479
14543
|
};
|
|
14480
14544
|
}
|
|
14481
|
-
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14545
|
+
let updated;
|
|
14546
|
+
if (content.includes(oldStr)) {
|
|
14547
|
+
updated = content.replace(oldStr, () => newStr);
|
|
14548
|
+
} else {
|
|
14549
|
+
const spliced = spliceNormalized(content, oldStr, newStr);
|
|
14550
|
+
if (!spliced) {
|
|
14551
|
+
return {
|
|
14552
|
+
success: false,
|
|
14553
|
+
output: t("file.string_not_found", { str: oldStr, path })
|
|
14554
|
+
};
|
|
14555
|
+
}
|
|
14556
|
+
updated = spliced;
|
|
14486
14557
|
}
|
|
14487
|
-
const updated = content.replace(oldStr, () => newStr);
|
|
14488
14558
|
if (securityConfig?.enabled && securityConfig?.contentScan?.enabled) {
|
|
14489
14559
|
const scanResult = scanContent(updated, path, securityConfig.contentScan);
|
|
14490
14560
|
if (!scanResult.allowed) {
|
|
@@ -21450,6 +21520,27 @@ function queueToolImages(sink, images) {
|
|
|
21450
21520
|
}
|
|
21451
21521
|
|
|
21452
21522
|
// src/core/agent/tool-batch.ts
|
|
21523
|
+
function diagnoseRepeatedFailures(tool, headlines) {
|
|
21524
|
+
const genericCause = "Repeated failures suggest a systemic problem (config, environment, or a broken tool), not a one-off";
|
|
21525
|
+
const genericSolution = "Check the error message, verify the tool's dependencies are installed/configured, and consider a different tool";
|
|
21526
|
+
const modelMiss = tool === "bash" ? [/did not match any test files?/i, /no such file/i, /cannot find (the )?path/i] : [];
|
|
21527
|
+
if (headlines.length > 0 && headlines.every((h) => /^\(exit code \d+\)$/.test(h.trim()) || modelMiss.some((re) => re.test(h)))) {
|
|
21528
|
+
return {
|
|
21529
|
+
skip: true,
|
|
21530
|
+
cause: "Model named a wrong target (path/filter); the tool itself works",
|
|
21531
|
+
solution: "Re-check the target path or filter instead of blaming the tool"
|
|
21532
|
+
};
|
|
21533
|
+
}
|
|
21534
|
+
if (tool === "edit_file" && headlines.some((h) => /string not found|не найдена/i.test(h))) {
|
|
21535
|
+
return {
|
|
21536
|
+
skip: false,
|
|
21537
|
+
cause: "Anchor mismatch between the model anchor and file content — often CRLF vs LF line endings or a stale read, not a broken tool",
|
|
21538
|
+
solution: "Re-read the exact block with read_file and retry with a short unique single-line anchor"
|
|
21539
|
+
};
|
|
21540
|
+
}
|
|
21541
|
+
return { skip: false, cause: genericCause, solution: genericSolution };
|
|
21542
|
+
}
|
|
21543
|
+
|
|
21453
21544
|
class ToolBatchExecutor {
|
|
21454
21545
|
deps;
|
|
21455
21546
|
constructor(deps) {
|
|
@@ -21609,15 +21700,22 @@ ${result.diff}
|
|
|
21609
21700
|
this.deps.compactionService.compactAfterTool(state);
|
|
21610
21701
|
if (!result.success && !result.blocked && !infoTools.has(call.name)) {
|
|
21611
21702
|
const key = call.name;
|
|
21612
|
-
const prev = state.toolFailureCounts.get(key) ?? { count: 0, error: "" };
|
|
21703
|
+
const prev = state.toolFailureCounts.get(key) ?? { count: 0, error: "", samples: [] };
|
|
21613
21704
|
prev.count++;
|
|
21614
|
-
|
|
21705
|
+
const errText = String(result.output ?? "");
|
|
21706
|
+
prev.error = errText.slice(0, 200);
|
|
21707
|
+
const headline = errText.split(`
|
|
21708
|
+
`)[0].slice(0, 160).trim();
|
|
21709
|
+
if (headline && !prev.samples.includes(headline) && prev.samples.length < 3) {
|
|
21710
|
+
prev.samples.push(headline);
|
|
21711
|
+
}
|
|
21615
21712
|
state.toolFailureCounts.set(key, prev);
|
|
21616
21713
|
if (prev.count >= MIN_REPEATED_TOOL_FAILURES && !state.memoryRuleRecorded.has(key)) {
|
|
21617
21714
|
state.memoryRuleRecorded.add(key);
|
|
21618
21715
|
const memStore = this.deps.memoryStore;
|
|
21619
|
-
|
|
21620
|
-
|
|
21716
|
+
const diagnosis = diagnoseRepeatedFailures(key, prev.samples);
|
|
21717
|
+
if (memStore && !diagnosis.skip) {
|
|
21718
|
+
memStore.appendRule("errors", `Tool ${key} failed ${prev.count}x (${prev.samples.join(" | ") || prev.error})`, diagnosis.cause, diagnosis.solution);
|
|
21621
21719
|
logger3.diagnostic("warn", `Recorded repeated ${key} failures to memory (${prev.count}x)`);
|
|
21622
21720
|
}
|
|
21623
21721
|
}
|
|
@@ -22449,6 +22547,7 @@ var init_provider = __esm(() => {
|
|
|
22449
22547
|
|
|
22450
22548
|
// src/modules/critic/evidence.ts
|
|
22451
22549
|
import { readFileSync as readFileSync26 } from "fs";
|
|
22550
|
+
import { execFileSync } from "node:child_process";
|
|
22452
22551
|
import { resolve as resolve15 } from "path";
|
|
22453
22552
|
function isWorkMutation(name) {
|
|
22454
22553
|
return MUTATING_TOOLS.has(name) || name === "bash";
|
|
@@ -22466,6 +22565,10 @@ function buildEvidencePack(input) {
|
|
|
22466
22565
|
if (cmd)
|
|
22467
22566
|
commands.push(cmd);
|
|
22468
22567
|
}
|
|
22568
|
+
if (input.baseDir) {
|
|
22569
|
+
for (const f of gitChangedFiles(input.baseDir))
|
|
22570
|
+
changedFiles.add(f);
|
|
22571
|
+
}
|
|
22469
22572
|
return {
|
|
22470
22573
|
task: input.task,
|
|
22471
22574
|
planSummary: input.planSummary,
|
|
@@ -22490,6 +22593,45 @@ function changedFileOf(ev) {
|
|
|
22490
22593
|
return;
|
|
22491
22594
|
return typeof ev.args?.path === "string" ? ev.args.path : undefined;
|
|
22492
22595
|
}
|
|
22596
|
+
function parsePorcelainLine(line) {
|
|
22597
|
+
if (line.length < 4)
|
|
22598
|
+
return null;
|
|
22599
|
+
let p = line.slice(3).trim();
|
|
22600
|
+
const arrow = p.indexOf(" -> ");
|
|
22601
|
+
if (arrow !== -1)
|
|
22602
|
+
p = p.slice(arrow + 4).trim();
|
|
22603
|
+
if (p.length >= 2 && p.startsWith('"') && p.endsWith('"')) {
|
|
22604
|
+
try {
|
|
22605
|
+
p = JSON.parse(p);
|
|
22606
|
+
} catch {
|
|
22607
|
+
p = p.slice(1, -1);
|
|
22608
|
+
}
|
|
22609
|
+
}
|
|
22610
|
+
return p || null;
|
|
22611
|
+
}
|
|
22612
|
+
function gitChangedFiles(baseDir) {
|
|
22613
|
+
let out;
|
|
22614
|
+
try {
|
|
22615
|
+
out = execFileSync("git", ["status", "--porcelain"], {
|
|
22616
|
+
cwd: resolve15(baseDir),
|
|
22617
|
+
encoding: "utf-8",
|
|
22618
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
22619
|
+
timeout: 1e4
|
|
22620
|
+
});
|
|
22621
|
+
} catch {
|
|
22622
|
+
return [];
|
|
22623
|
+
}
|
|
22624
|
+
const files = [];
|
|
22625
|
+
for (const line of out.split(`
|
|
22626
|
+
`)) {
|
|
22627
|
+
const p = parsePorcelainLine(line);
|
|
22628
|
+
if (p)
|
|
22629
|
+
files.push(p);
|
|
22630
|
+
if (files.length >= MAX_GIT_FILES)
|
|
22631
|
+
break;
|
|
22632
|
+
}
|
|
22633
|
+
return files;
|
|
22634
|
+
}
|
|
22493
22635
|
function commandOf(ev) {
|
|
22494
22636
|
if (ev.tool !== "bash" || typeof ev.args?.command !== "string")
|
|
22495
22637
|
return;
|
|
@@ -22513,7 +22655,7 @@ function readExcerpts(baseDir, paths) {
|
|
|
22513
22655
|
}
|
|
22514
22656
|
return out;
|
|
22515
22657
|
}
|
|
22516
|
-
var MUTATING_TOOLS, MAX_DIGEST_LINES = 40, MAX_DIGEST_OUTPUT_CHARS = 300, MAX_EXCERPT_FILES = 5, MAX_EXCERPT_CHARS = 1200, MAX_ANSWER_CHARS = 2000;
|
|
22658
|
+
var MUTATING_TOOLS, MAX_DIGEST_LINES = 40, MAX_DIGEST_OUTPUT_CHARS = 300, MAX_EXCERPT_FILES = 5, MAX_EXCERPT_CHARS = 1200, MAX_ANSWER_CHARS = 2000, MAX_GIT_FILES = 20;
|
|
22517
22659
|
var init_evidence = __esm(() => {
|
|
22518
22660
|
init_tool_output();
|
|
22519
22661
|
init_context_log();
|
|
@@ -23650,7 +23792,8 @@ class Agent {
|
|
|
23650
23792
|
costBreakdown: this.costTracker.breakdown(),
|
|
23651
23793
|
cache: cacheStats,
|
|
23652
23794
|
compactionCount: contextManager.getCompactionCount(),
|
|
23653
|
-
contextQuality: contextManager.getQuality()
|
|
23795
|
+
contextQuality: contextManager.getQuality(),
|
|
23796
|
+
contextBreakdown: contextManager.getBreakdown?.()
|
|
23654
23797
|
};
|
|
23655
23798
|
}
|
|
23656
23799
|
return {
|
|
@@ -23668,6 +23811,7 @@ class Agent {
|
|
|
23668
23811
|
cache: cacheStats,
|
|
23669
23812
|
compactionCount: contextManager.getCompactionCount(),
|
|
23670
23813
|
contextQuality: contextManager.getQuality(),
|
|
23814
|
+
contextBreakdown: contextManager.getBreakdown?.(),
|
|
23671
23815
|
llmDurationMs: state.totalLlmDuration
|
|
23672
23816
|
};
|
|
23673
23817
|
}
|
|
@@ -24280,6 +24424,28 @@ ${lines.join(`
|
|
|
24280
24424
|
summary: this.compactedBlock ?? ""
|
|
24281
24425
|
};
|
|
24282
24426
|
}
|
|
24427
|
+
systemTokens() {
|
|
24428
|
+
const sys = this.messages.find((m) => m.role === "system");
|
|
24429
|
+
return sys ? this.estimateMessageTokensCached(sys) : 0;
|
|
24430
|
+
}
|
|
24431
|
+
getBreakdown() {
|
|
24432
|
+
const used = this.getEstimatedTokens();
|
|
24433
|
+
const system = this.systemTokens();
|
|
24434
|
+
const tools = this.toolTokens;
|
|
24435
|
+
const history = Math.max(0, used - system - tools);
|
|
24436
|
+
const b = this.budget;
|
|
24437
|
+
return {
|
|
24438
|
+
system,
|
|
24439
|
+
tools,
|
|
24440
|
+
history,
|
|
24441
|
+
used,
|
|
24442
|
+
systemBudget: b.systemPrompt,
|
|
24443
|
+
historyBudget: b.history,
|
|
24444
|
+
reserve: b.responseReserve,
|
|
24445
|
+
free: Math.max(0, this.contextWindow - used - b.responseReserve),
|
|
24446
|
+
window: this.contextWindow
|
|
24447
|
+
};
|
|
24448
|
+
}
|
|
24283
24449
|
getSnapshot() {
|
|
24284
24450
|
return {
|
|
24285
24451
|
window: this.contextWindow,
|
|
@@ -24289,7 +24455,8 @@ ${lines.join(`
|
|
|
24289
24455
|
messageCount: this.messages.length,
|
|
24290
24456
|
quality: this.getQuality(),
|
|
24291
24457
|
compactionCount: this.compactionCount,
|
|
24292
|
-
iterationsSinceCompaction: this.iterationsSinceCompaction
|
|
24458
|
+
iterationsSinceCompaction: this.iterationsSinceCompaction,
|
|
24459
|
+
breakdown: this.getBreakdown()
|
|
24293
24460
|
};
|
|
24294
24461
|
}
|
|
24295
24462
|
updateSystemPrompt(content) {
|
|
@@ -24494,7 +24661,33 @@ var init_factual = __esm(() => {
|
|
|
24494
24661
|
if (!fp.includes("/") && !fp.includes("\\")) {
|
|
24495
24662
|
return this.bareNameExists(fp);
|
|
24496
24663
|
}
|
|
24497
|
-
return
|
|
24664
|
+
return this.suffixExists(fp);
|
|
24665
|
+
}
|
|
24666
|
+
suffixExists(fp) {
|
|
24667
|
+
const norm = fp.replace(/\\/g, "/");
|
|
24668
|
+
const tail = `/${norm}`;
|
|
24669
|
+
const base = norm.split("/").pop() ?? norm;
|
|
24670
|
+
const fwd = (p) => p.replace(/\\/g, "/");
|
|
24671
|
+
for (const known of this.knownFiles) {
|
|
24672
|
+
if (fwd(known).endsWith(tail))
|
|
24673
|
+
return true;
|
|
24674
|
+
}
|
|
24675
|
+
const checkIndex = () => {
|
|
24676
|
+
for (const full of this.getNameIndex().get(base) ?? []) {
|
|
24677
|
+
if (fwd(full).endsWith(tail))
|
|
24678
|
+
return true;
|
|
24679
|
+
}
|
|
24680
|
+
return false;
|
|
24681
|
+
};
|
|
24682
|
+
if (checkIndex())
|
|
24683
|
+
return true;
|
|
24684
|
+
const now = Date.now();
|
|
24685
|
+
if (now - this.lastRescan < FactualCheck.RESCAN_COOLDOWN_MS)
|
|
24686
|
+
return false;
|
|
24687
|
+
this.lastRescan = now;
|
|
24688
|
+
this.nameCache = null;
|
|
24689
|
+
this.nameCacheTime = 0;
|
|
24690
|
+
return checkIndex();
|
|
24498
24691
|
}
|
|
24499
24692
|
dotfileVariants(fp) {
|
|
24500
24693
|
const variants = [fp];
|
|
@@ -28741,6 +28934,7 @@ function createBrowserTool() {
|
|
|
28741
28934
|
"Do NOT use it for search queries: search engines (Google, Yandex, Bing) block headless browsers with CAPTCHA/429/redirect pages — use the web_search tool for searching.",
|
|
28742
28935
|
"Each snapshot returns: a numbered list of interactive elements, the visible page text (Content section), browser console messages, and network errors.",
|
|
28743
28936
|
"Use the Content section to understand what the page says; use element numbers as targets for click/type.",
|
|
28937
|
+
"Without vision capabilities screenshots return only a text note — verify visuals through snapshot text, and for local web UI prefer DOM/API assertions (fetch /api/* endpoints, grep built assets) over driving the browser.",
|
|
28744
28938
|
"Actions: open (url), click (target), type (target, text), scroll (direction), back, forward, screenshot, snapshot, close, wait (ms)."
|
|
28745
28939
|
].join(" "),
|
|
28746
28940
|
parameters: {
|
|
@@ -32383,11 +32577,26 @@ var init_plan_tool = __esm(() => {
|
|
|
32383
32577
|
});
|
|
32384
32578
|
|
|
32385
32579
|
// src/modules/execution/module.ts
|
|
32386
|
-
import { existsSync as existsSync51, readFileSync as readFileSync37 } from "fs";
|
|
32580
|
+
import { existsSync as existsSync51, readFileSync as readFileSync37, statSync as statSync11 } from "fs";
|
|
32387
32581
|
import { resolve as resolve22 } from "path";
|
|
32388
32582
|
function planPathToken(p) {
|
|
32389
32583
|
return toForwardSlash(p).toLowerCase();
|
|
32390
32584
|
}
|
|
32585
|
+
function provablyStale(resolved, sinceMs) {
|
|
32586
|
+
if (Number.isNaN(sinceMs))
|
|
32587
|
+
return false;
|
|
32588
|
+
for (const r of resolved) {
|
|
32589
|
+
if (!r)
|
|
32590
|
+
return false;
|
|
32591
|
+
try {
|
|
32592
|
+
if (statSync11(r).mtimeMs > sinceMs)
|
|
32593
|
+
return false;
|
|
32594
|
+
} catch {
|
|
32595
|
+
return false;
|
|
32596
|
+
}
|
|
32597
|
+
}
|
|
32598
|
+
return true;
|
|
32599
|
+
}
|
|
32391
32600
|
|
|
32392
32601
|
class ExecutionModule {
|
|
32393
32602
|
name = "execution";
|
|
@@ -32631,6 +32840,8 @@ class ExecutionModule {
|
|
|
32631
32840
|
});
|
|
32632
32841
|
if (empty)
|
|
32633
32842
|
continue;
|
|
32843
|
+
if (provablyStale(resolved, Date.parse(plan.createdAt)))
|
|
32844
|
+
continue;
|
|
32634
32845
|
}
|
|
32635
32846
|
step.status = "done";
|
|
32636
32847
|
step.note = `auto-closed by final audit: ${step.kind === "delete" ? "all named files removed" : "all named files exist with content"} (${tokens.join(", ")})`;
|
|
@@ -32858,6 +33069,11 @@ class ExecutionModule {
|
|
|
32858
33069
|
const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
|
|
32859
33070
|
if (!satisfied)
|
|
32860
33071
|
return;
|
|
33072
|
+
if (step.kind !== "delete") {
|
|
33073
|
+
const plan = this.tracker?.getPlan();
|
|
33074
|
+
if (plan && provablyStale(resolved, Date.parse(plan.createdAt)))
|
|
33075
|
+
return;
|
|
33076
|
+
}
|
|
32861
33077
|
if (step.kind !== "delete") {
|
|
32862
33078
|
const emptyFiles = [];
|
|
32863
33079
|
for (const r of resolved) {
|
|
@@ -44557,7 +44773,7 @@ var init_loader3 = __esm(() => {
|
|
|
44557
44773
|
});
|
|
44558
44774
|
|
|
44559
44775
|
// src/modules/certification/fact-checker.ts
|
|
44560
|
-
import { existsSync as existsSync56, readFileSync as readFileSync40, statSync as
|
|
44776
|
+
import { existsSync as existsSync56, readFileSync as readFileSync40, statSync as statSync12 } from "fs";
|
|
44561
44777
|
import { join as join50 } from "path";
|
|
44562
44778
|
function checkSandbox(sandboxDir, checks, exitCode, output) {
|
|
44563
44779
|
const failures = [];
|
|
@@ -44603,14 +44819,14 @@ function runCheck2(sandboxDir, check, exitCode, output) {
|
|
|
44603
44819
|
}
|
|
44604
44820
|
function isFile(p) {
|
|
44605
44821
|
try {
|
|
44606
|
-
return existsSync56(p) &&
|
|
44822
|
+
return existsSync56(p) && statSync12(p).isFile();
|
|
44607
44823
|
} catch {
|
|
44608
44824
|
return false;
|
|
44609
44825
|
}
|
|
44610
44826
|
}
|
|
44611
44827
|
function isDir(p) {
|
|
44612
44828
|
try {
|
|
44613
|
-
return existsSync56(p) &&
|
|
44829
|
+
return existsSync56(p) && statSync12(p).isDirectory();
|
|
44614
44830
|
} catch {
|
|
44615
44831
|
return false;
|
|
44616
44832
|
}
|
|
@@ -49232,7 +49448,7 @@ import { homedir as homedir8 } from "os";
|
|
|
49232
49448
|
// src/cli/completer.ts
|
|
49233
49449
|
init_registry2();
|
|
49234
49450
|
init_i18n();
|
|
49235
|
-
import { readdirSync as readdirSync19, statSync as
|
|
49451
|
+
import { readdirSync as readdirSync19, statSync as statSync13 } from "fs";
|
|
49236
49452
|
import { join as join54 } from "path";
|
|
49237
49453
|
function buildSlashCompletions(commands) {
|
|
49238
49454
|
return Array.from(commands.entries()).map(([name, cmd]) => ({
|
|
@@ -49306,7 +49522,7 @@ class FilePathProvider {
|
|
|
49306
49522
|
const matches = [];
|
|
49307
49523
|
for (const entry of entries) {
|
|
49308
49524
|
const entryPath = join54(fullPath, entry);
|
|
49309
|
-
const stat2 =
|
|
49525
|
+
const stat2 = statSync13(entryPath);
|
|
49310
49526
|
const completion = prefix + entry + (stat2.isDirectory() ? "/" : "");
|
|
49311
49527
|
if (completion.startsWith(ctx.partial)) {
|
|
49312
49528
|
matches.push(completion);
|
|
@@ -51310,7 +51526,7 @@ ${divider()}`);
|
|
|
51310
51526
|
row(t("repl.model"), pc.white(this.config.model));
|
|
51311
51527
|
const endpoint = activeProviderEndpoint(this.config.provider);
|
|
51312
51528
|
row(t("repl.provider"), `${endpoint.type} → ${pc.dim(endpoint.baseUrl)}`);
|
|
51313
|
-
row(t("repl.context"), `${pc.white(String(ctx))} ${pc.dim(`(
|
|
51529
|
+
row(t("repl.context"), `${pc.white(String(ctx))} ${pc.dim(`(${t("repl.ctx_part_system")}:${sysBudget} ${t("repl.ctx_part_reserve")}:${resBudget} ${t("repl.ctx_part_history")}:${histBudget})`)}`);
|
|
51314
51530
|
const si = this.agent.getSystemPromptInfo();
|
|
51315
51531
|
row(t("repl.sysprompt_label"), pc.dim(t("repl.sysprompt_size", { used: si.tokenCount, budget: sysBudget })));
|
|
51316
51532
|
if (this.skillsModule) {
|
|
@@ -51534,7 +51750,7 @@ init_output();
|
|
|
51534
51750
|
init_i18n();
|
|
51535
51751
|
init_colors();
|
|
51536
51752
|
import { existsSync as existsSync63 } from "node:fs";
|
|
51537
|
-
import { join as
|
|
51753
|
+
import { join as join61 } from "node:path";
|
|
51538
51754
|
|
|
51539
51755
|
// src/modules/webui/module.ts
|
|
51540
51756
|
init_config2();
|
|
@@ -52750,10 +52966,28 @@ function handleModelsGet(res, s) {
|
|
|
52750
52966
|
|
|
52751
52967
|
// src/modules/webui/i18n-api.ts
|
|
52752
52968
|
init_i18n();
|
|
52969
|
+
|
|
52970
|
+
// src/modules/webui/i18n-stale.ts
|
|
52971
|
+
import { statSync as statSync14 } from "fs";
|
|
52972
|
+
import { join as join60 } from "path";
|
|
52973
|
+
var serverStartedAt = Date.now();
|
|
52974
|
+
var dictFiles = ["en.json", "ru.json"].map((f) => join60(import.meta.dirname, "..", "..", "i18n", f));
|
|
52975
|
+
function isI18nStale() {
|
|
52976
|
+
for (const p of dictFiles) {
|
|
52977
|
+
try {
|
|
52978
|
+
if (statSync14(p).mtimeMs > serverStartedAt)
|
|
52979
|
+
return true;
|
|
52980
|
+
} catch {}
|
|
52981
|
+
}
|
|
52982
|
+
return false;
|
|
52983
|
+
}
|
|
52984
|
+
|
|
52985
|
+
// src/modules/webui/i18n-api.ts
|
|
52753
52986
|
function handleI18nGet(res, s) {
|
|
52754
52987
|
sendJson(res, 200, {
|
|
52755
52988
|
locale: s.config?.locale ?? "en",
|
|
52756
|
-
dicts: getFlatDicts()
|
|
52989
|
+
dicts: getFlatDicts(),
|
|
52990
|
+
i18nStale: isI18nStale()
|
|
52757
52991
|
});
|
|
52758
52992
|
}
|
|
52759
52993
|
|
|
@@ -54574,6 +54808,8 @@ function captureUsage(result, fallbackLimit) {
|
|
|
54574
54808
|
}
|
|
54575
54809
|
if (typeof result.contextQuality === "number")
|
|
54576
54810
|
usage.contextQuality = result.contextQuality;
|
|
54811
|
+
if (result.contextBreakdown)
|
|
54812
|
+
usage.contextBreakdown = result.contextBreakdown;
|
|
54577
54813
|
return usage;
|
|
54578
54814
|
}
|
|
54579
54815
|
function planEventFor(toolName, execModule) {
|
|
@@ -54596,10 +54832,12 @@ function buildHostState(deps, busy) {
|
|
|
54596
54832
|
compactionCount: deps.usage?.compactionCount,
|
|
54597
54833
|
cacheHitRate: deps.usage?.cacheHitRate,
|
|
54598
54834
|
contextQuality: deps.usage?.contextQuality,
|
|
54835
|
+
contextBreakdown: deps.usage?.contextBreakdown,
|
|
54599
54836
|
contextProbe: deps.contextProbe,
|
|
54600
54837
|
workspace: deps.workspace,
|
|
54601
54838
|
instructions: deps.instructions,
|
|
54602
54839
|
projectMap: deps.projectMap ?? undefined,
|
|
54840
|
+
i18nStale: isI18nStale(),
|
|
54603
54841
|
plan: planSnapshotFromModule(adaptActivePlan(deps.execModule?.getActivePlan()))
|
|
54604
54842
|
};
|
|
54605
54843
|
}
|
|
@@ -54801,7 +55039,7 @@ async function runWeb(opts) {
|
|
|
54801
55039
|
process.env[MMA_ENV.WEBUI_OPEN] = "0";
|
|
54802
55040
|
const mmaDir = globalMmaDir();
|
|
54803
55041
|
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
54804
|
-
const virgin = !existsSync63(
|
|
55042
|
+
const virgin = !existsSync63(join61(mmaDir, "config.json")) && !hasDomainFiles2(mmaDir);
|
|
54805
55043
|
const {
|
|
54806
55044
|
agent,
|
|
54807
55045
|
config,
|
|
@@ -54923,7 +55161,7 @@ init_config2();
|
|
|
54923
55161
|
init_i18n();
|
|
54924
55162
|
init_colors();
|
|
54925
55163
|
import { existsSync as existsSync64, readFileSync as readFileSync45 } from "fs";
|
|
54926
|
-
import { join as
|
|
55164
|
+
import { join as join63, dirname as dirname30 } from "path";
|
|
54927
55165
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
54928
55166
|
|
|
54929
55167
|
// src/modules/updater/index.ts
|
|
@@ -55045,7 +55283,7 @@ init_i18n();
|
|
|
55045
55283
|
init_utils();
|
|
55046
55284
|
init_paths();
|
|
55047
55285
|
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync30 } from "fs";
|
|
55048
|
-
import { join as
|
|
55286
|
+
import { join as join62 } from "path";
|
|
55049
55287
|
import { homedir as homedir9 } from "os";
|
|
55050
55288
|
var CRASH_LOG_DIR = globalLogsDir();
|
|
55051
55289
|
var CRASH_LOG_FILE = "crash.jsonl";
|
|
@@ -55063,7 +55301,7 @@ function formatCrashEntry(type2, err) {
|
|
|
55063
55301
|
function writeCrashEntry(dir, entry) {
|
|
55064
55302
|
try {
|
|
55065
55303
|
mkdirSync30(dir, { recursive: true });
|
|
55066
|
-
appendFileSync8(
|
|
55304
|
+
appendFileSync8(join62(dir, CRASH_LOG_FILE), `${JSON.stringify(entry)}
|
|
55067
55305
|
`, "utf-8");
|
|
55068
55306
|
} catch {}
|
|
55069
55307
|
}
|
|
@@ -55134,7 +55372,7 @@ function resolveEarlyLocale() {
|
|
|
55134
55372
|
if (env3)
|
|
55135
55373
|
return env3;
|
|
55136
55374
|
const dir = globalMmaDir();
|
|
55137
|
-
for (const p of [
|
|
55375
|
+
for (const p of [join63(dir, "config", "core.json"), join63(dir, "config.json")]) {
|
|
55138
55376
|
try {
|
|
55139
55377
|
const parsed = JSON.parse(readFileSync45(p, "utf-8"));
|
|
55140
55378
|
if (typeof parsed.locale === "string")
|
|
@@ -55234,7 +55472,7 @@ async function main() {
|
|
|
55234
55472
|
const prompt = program2.args.join(" ");
|
|
55235
55473
|
const mmaDir = globalMmaDir();
|
|
55236
55474
|
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
55237
|
-
const virgin = !existsSync64(
|
|
55475
|
+
const virgin = !existsSync64(join63(mmaDir, "config.json")) && !hasDomainFiles2(mmaDir);
|
|
55238
55476
|
const { agent, config, baseDir, legacyDetected, contextProbe } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel, contextLog);
|
|
55239
55477
|
if (verboseOpt)
|
|
55240
55478
|
config.ui.verbosity = "verbose";
|
|
@@ -55325,7 +55563,7 @@ async function main() {
|
|
|
55325
55563
|
if (onboardingStart === "init")
|
|
55326
55564
|
process.env[MMA_ENV.START_INIT] = "1";
|
|
55327
55565
|
const mmaDir = globalMmaDir();
|
|
55328
|
-
const legacyConfigPath =
|
|
55566
|
+
const legacyConfigPath = join63(mmaDir, "config.json");
|
|
55329
55567
|
let hasAnyConfig = existsSync64(legacyConfigPath);
|
|
55330
55568
|
if (!hasAnyConfig) {
|
|
55331
55569
|
try {
|
|
@@ -55333,7 +55571,7 @@ async function main() {
|
|
|
55333
55571
|
hasAnyConfig = hasDomainFiles2(mmaDir);
|
|
55334
55572
|
} catch {}
|
|
55335
55573
|
}
|
|
55336
|
-
const projectConfigPath = projectDir ?
|
|
55574
|
+
const projectConfigPath = projectDir ? join63(projectDir, ".mmrc") : join63(process.cwd(), ".mmrc");
|
|
55337
55575
|
if (!hasAnyConfig && !exitOnComplete && readEnv("POST_SETUP") !== "1") {
|
|
55338
55576
|
getDefaultChannel().writeLine(`
|
|
55339
55577
|
${t("cli.first_run")}
|