micro-models-agent 2.15.0 → 2.16.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 +21 -0
- package/dist/assets/app.js +6 -2
- package/dist/assets/index.html +1 -0
- package/dist/assets/js/feed.js +24 -47
- package/dist/assets/js/format.js +8 -0
- package/dist/assets/js/settings.js +74 -8
- package/dist/assets/js/sidebar.js +198 -5
- package/dist/assets/js/tabs.js +105 -18
- package/dist/assets/styles.css +73 -30
- package/dist/i18n/en.json +20 -1
- package/dist/i18n/ru.json +20 -1
- package/dist/main.js +355 -134
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2395,18 +2395,7 @@ var init_defaults = __esm(() => {
|
|
|
2395
2395
|
verbosity: "quiet",
|
|
2396
2396
|
fontSize: "comfortable"
|
|
2397
2397
|
},
|
|
2398
|
-
mcpServers: {
|
|
2399
|
-
context7: {
|
|
2400
|
-
name: "context7",
|
|
2401
|
-
transport: "http",
|
|
2402
|
-
url: "https://mcp.context7.com/mcp",
|
|
2403
|
-
headers: {
|
|
2404
|
-
CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY || ""
|
|
2405
|
-
},
|
|
2406
|
-
enabled: true,
|
|
2407
|
-
timeout: 30000
|
|
2408
|
-
}
|
|
2409
|
-
},
|
|
2398
|
+
mcpServers: {},
|
|
2410
2399
|
security: DEFAULT_SECURITY_CONFIG,
|
|
2411
2400
|
sessionIsolation: {
|
|
2412
2401
|
enabled: false,
|
|
@@ -3612,6 +3601,18 @@ Updated: {updatedAt}`,
|
|
|
3612
3601
|
"web.sidebar.trace_text": "composing… {count} chars",
|
|
3613
3602
|
"web.sidebar.trace_plan": "plan update",
|
|
3614
3603
|
"web.sidebar.trace_question": "question",
|
|
3604
|
+
"web.sidebar.trace_status": "status",
|
|
3605
|
+
"web.sidebar.trace_log": "log · {level}",
|
|
3606
|
+
"web.sidebar.trace_retract": "draft retracted",
|
|
3607
|
+
"web.sidebar.trace_index": "index · {phase}",
|
|
3608
|
+
"web.sidebar.trace_critic": "critic · {status}",
|
|
3609
|
+
"web.sidebar.trace_moe": "moe",
|
|
3610
|
+
"web.sidebar.trace_compaction": "compaction",
|
|
3611
|
+
"web.sidebar.trace_probe": "context probe",
|
|
3612
|
+
"web.sidebar.trace_usage": "llm usage",
|
|
3613
|
+
"web.sidebar.trace_audit": "audit",
|
|
3614
|
+
"web.sidebar.trace_claim": "claim retry · {kind}",
|
|
3615
|
+
"web.sidebar.trace_output": "output",
|
|
3615
3616
|
"web.sidebar.detail_metadata": "metadata",
|
|
3616
3617
|
"web.sidebar.detail_arguments": "arguments",
|
|
3617
3618
|
"web.sidebar.detail_output": "output",
|
|
@@ -3626,6 +3627,10 @@ Updated: {updatedAt}`,
|
|
|
3626
3627
|
"web.sidebar.detail_time": "time",
|
|
3627
3628
|
"web.sidebar.detail_tool_call": "tool call",
|
|
3628
3629
|
"web.sidebar.detail_session": "session",
|
|
3630
|
+
"web.sidebar.detail_turn": "turn",
|
|
3631
|
+
"web.sidebar.detail_iteration": "iteration",
|
|
3632
|
+
"web.sidebar.detail_provenance": "session-log provenance",
|
|
3633
|
+
"web.sidebar.detail_provenance_count": "{n} assistant messages in log",
|
|
3629
3634
|
"web.env.dir": "dir",
|
|
3630
3635
|
"web.env.agents": "AGENTS.md",
|
|
3631
3636
|
"web.env.map": "map",
|
|
@@ -3945,9 +3950,10 @@ Updated: {updatedAt}`,
|
|
|
3945
3950
|
"web.input.shift_enter": "Shift+Enter — newline",
|
|
3946
3951
|
"web.input.esc": "Esc — stop",
|
|
3947
3952
|
"web.input.attach": "Ctrl+V — attach",
|
|
3948
|
-
"web.input.busy": "still working — Esc to stop",
|
|
3949
3953
|
"web.input.send": "send",
|
|
3950
3954
|
"web.input.stop": "stop",
|
|
3955
|
+
"web.input.queued": "queued · position {n}",
|
|
3956
|
+
"web.input.queue_full": "message queue is full — wait for the turn to end",
|
|
3951
3957
|
"web.empty.title": "new chat",
|
|
3952
3958
|
"web.empty.hint": "type a message below · / — commands · model and context are chosen above the input",
|
|
3953
3959
|
"web.attach.title": "attach image",
|
|
@@ -3964,6 +3970,7 @@ Updated: {updatedAt}`,
|
|
|
3964
3970
|
"web.sessions.none_active": "— no active session —",
|
|
3965
3971
|
"web.sessions.none": "— none —",
|
|
3966
3972
|
"web.sessions.delete": "delete",
|
|
3973
|
+
"web.sessions.search": "search sessions…",
|
|
3967
3974
|
"web.sessions.create_failed": "session create failed",
|
|
3968
3975
|
"web.sessions.delete_failed": "delete failed",
|
|
3969
3976
|
"web.sessions.confirm_title": "Delete session?",
|
|
@@ -3976,6 +3983,7 @@ Updated: {updatedAt}`,
|
|
|
3976
3983
|
"web.selectors.switch_busy": "switch in progress, please wait",
|
|
3977
3984
|
"web.selectors.reasoning_off": "off (backend ignores it)",
|
|
3978
3985
|
"web.selectors.no_models": "no models",
|
|
3986
|
+
"web.selectors.search_models": "search models…",
|
|
3979
3987
|
"web.selectors.refresh_list": "↻ refresh list",
|
|
3980
3988
|
"web.selectors.custom": "custom…",
|
|
3981
3989
|
"web.selectors.current": "current",
|
|
@@ -5528,6 +5536,18 @@ var init_ru = __esm(() => {
|
|
|
5528
5536
|
"web.sidebar.trace_text": "печатает… {count} симв.",
|
|
5529
5537
|
"web.sidebar.trace_plan": "обновление плана",
|
|
5530
5538
|
"web.sidebar.trace_question": "вопрос",
|
|
5539
|
+
"web.sidebar.trace_status": "статус",
|
|
5540
|
+
"web.sidebar.trace_log": "лог · {level}",
|
|
5541
|
+
"web.sidebar.trace_retract": "черновик отозван",
|
|
5542
|
+
"web.sidebar.trace_index": "индекс · {phase}",
|
|
5543
|
+
"web.sidebar.trace_critic": "критик · {status}",
|
|
5544
|
+
"web.sidebar.trace_moe": "moe",
|
|
5545
|
+
"web.sidebar.trace_compaction": "компакция",
|
|
5546
|
+
"web.sidebar.trace_probe": "проба контекста",
|
|
5547
|
+
"web.sidebar.trace_usage": "расход llm",
|
|
5548
|
+
"web.sidebar.trace_audit": "аудит",
|
|
5549
|
+
"web.sidebar.trace_claim": "перепроверка · {kind}",
|
|
5550
|
+
"web.sidebar.trace_output": "вывод",
|
|
5531
5551
|
"web.sidebar.detail_metadata": "метаданные",
|
|
5532
5552
|
"web.sidebar.detail_arguments": "аргументы",
|
|
5533
5553
|
"web.sidebar.detail_output": "вывод",
|
|
@@ -5542,6 +5562,10 @@ var init_ru = __esm(() => {
|
|
|
5542
5562
|
"web.sidebar.detail_time": "время",
|
|
5543
5563
|
"web.sidebar.detail_tool_call": "вызов тула",
|
|
5544
5564
|
"web.sidebar.detail_session": "сессия",
|
|
5565
|
+
"web.sidebar.detail_turn": "ход",
|
|
5566
|
+
"web.sidebar.detail_iteration": "итерация",
|
|
5567
|
+
"web.sidebar.detail_provenance": "сверка с логом",
|
|
5568
|
+
"web.sidebar.detail_provenance_count": "assistant-сообщений в логе: {n}",
|
|
5545
5569
|
"web.env.dir": "директория",
|
|
5546
5570
|
"web.env.agents": "AGENTS.md",
|
|
5547
5571
|
"web.env.map": "карта",
|
|
@@ -5834,9 +5858,10 @@ var init_ru = __esm(() => {
|
|
|
5834
5858
|
"web.input.shift_enter": "Shift+Enter — новая строка",
|
|
5835
5859
|
"web.input.esc": "Esc — стоп",
|
|
5836
5860
|
"web.input.attach": "Ctrl+V — вложить",
|
|
5837
|
-
"web.input.busy": "ещё работаю — Esc для остановки",
|
|
5838
5861
|
"web.input.send": "отправить",
|
|
5839
5862
|
"web.input.stop": "остановить",
|
|
5863
|
+
"web.input.queued": "в очереди · позиция {n}",
|
|
5864
|
+
"web.input.queue_full": "очередь сообщений полна — дождись конца хода",
|
|
5840
5865
|
"web.empty.title": "новый чат",
|
|
5841
5866
|
"web.empty.hint": "введи сообщение ниже · / — команды · модель и контекст выбираются над полем",
|
|
5842
5867
|
"web.attach.title": "прикрепить изображение",
|
|
@@ -5853,6 +5878,7 @@ var init_ru = __esm(() => {
|
|
|
5853
5878
|
"web.sessions.none_active": "— нет активной сессии —",
|
|
5854
5879
|
"web.sessions.none": "— нет —",
|
|
5855
5880
|
"web.sessions.delete": "удалить",
|
|
5881
|
+
"web.sessions.search": "поиск сессий…",
|
|
5856
5882
|
"web.sessions.create_failed": "не удалось создать сессию",
|
|
5857
5883
|
"web.sessions.delete_failed": "не удалось удалить",
|
|
5858
5884
|
"web.sessions.confirm_title": "Удалить сессию?",
|
|
@@ -5865,6 +5891,7 @@ var init_ru = __esm(() => {
|
|
|
5865
5891
|
"web.selectors.switch_busy": "переключение уже идёт, подождите",
|
|
5866
5892
|
"web.selectors.reasoning_off": "выкл (бэкенд игнорирует)",
|
|
5867
5893
|
"web.selectors.no_models": "нет моделей",
|
|
5894
|
+
"web.selectors.search_models": "поиск моделей…",
|
|
5868
5895
|
"web.selectors.refresh_list": "↻ обновить список",
|
|
5869
5896
|
"web.selectors.custom": "другое…",
|
|
5870
5897
|
"web.selectors.current": "текущее",
|
|
@@ -21792,7 +21819,7 @@ function checkSuccessClaim(response, tools) {
|
|
|
21792
21819
|
return null;
|
|
21793
21820
|
if (HEDGE_RE.test(response))
|
|
21794
21821
|
return null;
|
|
21795
|
-
const recent = tools.filter((t2) => t2.text?.trim()).slice(-EVIDENCE_WINDOW);
|
|
21822
|
+
const recent = tools.filter((t2) => t2.text?.trim() && !BOOKKEEPING_TOOLS.has(t2.name ?? "")).slice(-EVIDENCE_WINDOW);
|
|
21796
21823
|
for (let i = recent.length - 1;i >= 0; i--) {
|
|
21797
21824
|
const entry = recent[i];
|
|
21798
21825
|
const text = entry.text.slice(0, EVIDENCE_TEXT_CAP);
|
|
@@ -21809,11 +21836,12 @@ function checkSuccessClaim(response, tools) {
|
|
|
21809
21836
|
}
|
|
21810
21837
|
return null;
|
|
21811
21838
|
}
|
|
21812
|
-
var WORD = "A-Za-zА-Яа-яЁё0-9_", wb = (body) => new RegExp(`(?<![${WORD}])(?:${body})(?![${WORD}])`, "i"), SUCCESS_CLAIM_RE, HEDGE_RE, FAILURE_SIGNAL_RE, EVIDENCE_WINDOW = 10, EVIDENCE_TEXT_CAP = 400, SNIPPET_MAX_CHARS = 160;
|
|
21839
|
+
var WORD = "A-Za-zА-Яа-яЁё0-9_", wb = (body) => new RegExp(`(?<![${WORD}])(?:${body})(?![${WORD}])`, "i"), SUCCESS_CLAIM_RE, HEDGE_RE, FAILURE_SIGNAL_RE, EVIDENCE_WINDOW = 10, EVIDENCE_TEXT_CAP = 400, SNIPPET_MAX_CHARS = 160, BOOKKEEPING_TOOLS;
|
|
21813
21840
|
var init_success_claim = __esm(() => {
|
|
21814
21841
|
SUCCESS_CLAIM_RE = wb("published|опубликован[а-яё]*|задеплоен[а-яё]*|deployed|shipped|готово|выполнено|завершен[а-яё]*|успешно|success(?:ful(?:ly)?)?|done|completed?|finished|все сделано|всё сделано");
|
|
21815
21842
|
HEDGE_RE = wb("may take|being processed|processing|staged|propagat[a-z]*|in progress|shortly|soon|will appear|несколько минут|обрабатывается|застейджен[а-яё]*|подожд[а-яё]*|проверь[а-яё]* позже|появится|пропагир[а-яё]*|кэш[а-яё]*|cach[a-z]*|обычно|typically");
|
|
21816
21843
|
FAILURE_SIGNAL_RE = new RegExp(`${wb("404|409|40[03]|50\\d|failed|failure|not found|ERR_[A-Z_]+|E(?:404|409|ACCES|PERM|CONNREFUSED|TIMED?OUT)|conflict|denied|ошибка|ошибк[а-яё]*|не найден[а-яё]*|не удалось|отклонено").source}|✗|⛔`, "i");
|
|
21844
|
+
BOOKKEEPING_TOOLS = new Set(["plan", "plan_step", "todo"]);
|
|
21817
21845
|
});
|
|
21818
21846
|
|
|
21819
21847
|
// src/modules/hallucination/artifact-claim.ts
|
|
@@ -52151,6 +52179,7 @@ function exportCmd(state, args, out) {
|
|
|
52151
52179
|
|
|
52152
52180
|
// src/modules/webui/limits.ts
|
|
52153
52181
|
var MAX_BODY_BYTES = 24 * 1024 * 1024;
|
|
52182
|
+
var MAX_QUEUE_LENGTH = 5;
|
|
52154
52183
|
|
|
52155
52184
|
// src/modules/webui/state.ts
|
|
52156
52185
|
init_env();
|
|
@@ -52170,6 +52199,8 @@ function createWebUiState() {
|
|
|
52170
52199
|
busy: false,
|
|
52171
52200
|
agentRunning: false,
|
|
52172
52201
|
turnToken: null,
|
|
52202
|
+
turnSeq: 0,
|
|
52203
|
+
pendingQueue: [],
|
|
52173
52204
|
lastPostTs: 0,
|
|
52174
52205
|
minPostIntervalMs: 500,
|
|
52175
52206
|
historyTail: 200,
|
|
@@ -52496,111 +52527,14 @@ function makeForwarder(s, sink = (ev) => broadcast(ev, s), opts = {}) {
|
|
|
52496
52527
|
s.busy = false;
|
|
52497
52528
|
s.agentRunning = false;
|
|
52498
52529
|
s.turnToken = null;
|
|
52499
|
-
|
|
52530
|
+
s.turnSeq += 1;
|
|
52531
|
+
sink({ type: "turn_end", ts: Date.now(), turnSeq: s.turnSeq, ...summary });
|
|
52532
|
+
opts.onQueueDrained?.();
|
|
52500
52533
|
},
|
|
52501
52534
|
flushNow: flush
|
|
52502
52535
|
};
|
|
52503
52536
|
}
|
|
52504
52537
|
|
|
52505
|
-
// src/modules/webui/log-forward.ts
|
|
52506
|
-
init_bus();
|
|
52507
|
-
init_context_log();
|
|
52508
|
-
var FORWARDED = ["info", "warn", "error"];
|
|
52509
|
-
function installOutputForwarding(state, bus = defaultOutputBus) {
|
|
52510
|
-
return bus.subscribe((ev) => {
|
|
52511
|
-
if (!FORWARDED.includes(ev.level))
|
|
52512
|
-
return;
|
|
52513
|
-
broadcast({
|
|
52514
|
-
type: "log",
|
|
52515
|
-
ts: ev.ts,
|
|
52516
|
-
level: ev.level,
|
|
52517
|
-
source: ev.source,
|
|
52518
|
-
text: redactContextSecrets(ev.text),
|
|
52519
|
-
diagnostic: ev.data?.diagnostic === true
|
|
52520
|
-
}, state);
|
|
52521
|
-
});
|
|
52522
|
-
}
|
|
52523
|
-
|
|
52524
|
-
// src/modules/webui/prompt-io.ts
|
|
52525
|
-
init_question();
|
|
52526
|
-
init_approve();
|
|
52527
|
-
var questionSeq = 0;
|
|
52528
|
-
function nextQuestionId() {
|
|
52529
|
-
questionSeq += 1;
|
|
52530
|
-
return `q${Date.now().toString(36)}-${questionSeq}`;
|
|
52531
|
-
}
|
|
52532
|
-
function toIndexes(value) {
|
|
52533
|
-
if (!Array.isArray(value))
|
|
52534
|
-
return [];
|
|
52535
|
-
const out = [];
|
|
52536
|
-
for (const item of value) {
|
|
52537
|
-
const n = typeof item === "number" ? item : Number(item);
|
|
52538
|
-
if (Number.isInteger(n) && n >= -1)
|
|
52539
|
-
out.push(n);
|
|
52540
|
-
}
|
|
52541
|
-
return out;
|
|
52542
|
-
}
|
|
52543
|
-
function toText(value) {
|
|
52544
|
-
if (typeof value === "string")
|
|
52545
|
-
return value;
|
|
52546
|
-
if (value == null)
|
|
52547
|
-
return "";
|
|
52548
|
-
return String(value);
|
|
52549
|
-
}
|
|
52550
|
-
function ask2(state, payload, coerce) {
|
|
52551
|
-
return new Promise((resolve27) => {
|
|
52552
|
-
const id = nextQuestionId();
|
|
52553
|
-
state.pendingQuestions.set(id, (value) => resolve27(coerce(value)));
|
|
52554
|
-
broadcast({ type: "question", ts: Date.now(), id, ...payload }, state);
|
|
52555
|
-
});
|
|
52556
|
-
}
|
|
52557
|
-
function createWebPromptIO(state) {
|
|
52558
|
-
return {
|
|
52559
|
-
askText(question) {
|
|
52560
|
-
return ask2(state, { text: question, kind: "text" }, toText);
|
|
52561
|
-
},
|
|
52562
|
-
askChoice(question, options, opts = {}) {
|
|
52563
|
-
return ask2(state, {
|
|
52564
|
-
text: question,
|
|
52565
|
-
kind: "choice",
|
|
52566
|
-
options: options.map((o) => o.label ?? String(o)),
|
|
52567
|
-
multiple: !!opts.multiple,
|
|
52568
|
-
allowCustom: !!opts.allowCustom
|
|
52569
|
-
}, toIndexes);
|
|
52570
|
-
}
|
|
52571
|
-
};
|
|
52572
|
-
}
|
|
52573
|
-
function resolveAnswer(state, id, value) {
|
|
52574
|
-
const resolver = state.pendingQuestions.get(id);
|
|
52575
|
-
if (!resolver)
|
|
52576
|
-
return false;
|
|
52577
|
-
state.pendingQuestions.delete(id);
|
|
52578
|
-
resolver(value);
|
|
52579
|
-
return true;
|
|
52580
|
-
}
|
|
52581
|
-
function cancelPendingQuestions(state) {
|
|
52582
|
-
const pending = [...state.pendingQuestions.values()];
|
|
52583
|
-
state.pendingQuestions.clear();
|
|
52584
|
-
for (const resolve27 of pending)
|
|
52585
|
-
resolve27(undefined);
|
|
52586
|
-
}
|
|
52587
|
-
function installWebPromptIO(deps) {
|
|
52588
|
-
if (deps.registry) {
|
|
52589
|
-
for (const tool of [questionTool, approveTool]) {
|
|
52590
|
-
if (!deps.registry.has(tool.name))
|
|
52591
|
-
deps.registry.register(tool);
|
|
52592
|
-
}
|
|
52593
|
-
}
|
|
52594
|
-
if (deps.toolCtx)
|
|
52595
|
-
deps.toolCtx.exitOnComplete = false;
|
|
52596
|
-
const io = createWebPromptIO(deps.state);
|
|
52597
|
-
deps.agent.setPromptIO(io);
|
|
52598
|
-
return io;
|
|
52599
|
-
}
|
|
52600
|
-
|
|
52601
|
-
// src/modules/webui/server.ts
|
|
52602
|
-
import { createServer } from "node:http";
|
|
52603
|
-
|
|
52604
52538
|
// src/modules/webui/util.ts
|
|
52605
52539
|
init_context_log();
|
|
52606
52540
|
import { exec } from "node:child_process";
|
|
@@ -52741,6 +52675,32 @@ function readBodyError(req, res, err) {
|
|
|
52741
52675
|
}
|
|
52742
52676
|
sendJson(res, 400, { error: "bad request" });
|
|
52743
52677
|
}
|
|
52678
|
+
function submitTurn(s, bridge, text, images, token = reserveBusy(s)) {
|
|
52679
|
+
broadcast({ type: "state", ts: Date.now(), busy: true, queueLen: s.pendingQueue.length }, s);
|
|
52680
|
+
const run = Promise.resolve(bridge.submit(text, { images }));
|
|
52681
|
+
run.catch((err) => {
|
|
52682
|
+
if (releaseBusy(s, token)) {
|
|
52683
|
+
broadcast({ type: "error", ts: Date.now(), error: scrubError(err) }, s);
|
|
52684
|
+
broadcast({ type: "state", ts: Date.now(), busy: false, queueLen: s.pendingQueue.length }, s);
|
|
52685
|
+
}
|
|
52686
|
+
});
|
|
52687
|
+
}
|
|
52688
|
+
function drainQueueTurn(s) {
|
|
52689
|
+
const bridge = s.bridge;
|
|
52690
|
+
if (!bridge || s.busy || bridge.isBusy())
|
|
52691
|
+
return false;
|
|
52692
|
+
const next = s.pendingQueue.shift();
|
|
52693
|
+
if (!next)
|
|
52694
|
+
return false;
|
|
52695
|
+
submitTurn(s, bridge, next.text, next.images);
|
|
52696
|
+
return true;
|
|
52697
|
+
}
|
|
52698
|
+
function enqueueMessage(s, text, images) {
|
|
52699
|
+
if (s.pendingQueue.length >= MAX_QUEUE_LENGTH)
|
|
52700
|
+
return null;
|
|
52701
|
+
s.pendingQueue.push({ text, images, ts: Date.now() });
|
|
52702
|
+
return s.pendingQueue.length;
|
|
52703
|
+
}
|
|
52744
52704
|
function handleMessagePost(req, res, s) {
|
|
52745
52705
|
if (s.setupRequired) {
|
|
52746
52706
|
sendJson(res, 409, { error: "setup_required" });
|
|
@@ -52751,7 +52711,27 @@ function handleMessagePost(req, res, s) {
|
|
|
52751
52711
|
return;
|
|
52752
52712
|
}
|
|
52753
52713
|
if (s.busy || s.bridge.isBusy()) {
|
|
52754
|
-
|
|
52714
|
+
if (s.minPostIntervalMs > 0 && Date.now() - s.lastPostTs < s.minPostIntervalMs) {
|
|
52715
|
+
sendJson(res, 429, { error: "rate_limited" });
|
|
52716
|
+
return;
|
|
52717
|
+
}
|
|
52718
|
+
readBody(req, s.maxBodyBytes).then((raw) => {
|
|
52719
|
+
const body = parseMessageBody(raw);
|
|
52720
|
+
if ("error" in body) {
|
|
52721
|
+
sendJson(res, 400, { error: body.error });
|
|
52722
|
+
return;
|
|
52723
|
+
}
|
|
52724
|
+
s.lastPostTs = Date.now();
|
|
52725
|
+
const pos = enqueueMessage(s, body.text, body.images);
|
|
52726
|
+
if (pos === null) {
|
|
52727
|
+
sendJson(res, 429, { error: "queue_full" });
|
|
52728
|
+
return;
|
|
52729
|
+
}
|
|
52730
|
+
broadcast({ type: "state", ts: Date.now(), busy: true, queueLen: s.pendingQueue.length }, s);
|
|
52731
|
+
sendJson(res, 202, { ok: true, queued: pos });
|
|
52732
|
+
}).catch((err) => {
|
|
52733
|
+
readBodyError(req, res, err);
|
|
52734
|
+
});
|
|
52755
52735
|
return;
|
|
52756
52736
|
}
|
|
52757
52737
|
if (s.minPostIntervalMs > 0 && Date.now() - s.lastPostTs < s.minPostIntervalMs) {
|
|
@@ -52760,28 +52740,22 @@ function handleMessagePost(req, res, s) {
|
|
|
52760
52740
|
}
|
|
52761
52741
|
const bridge = s.bridge;
|
|
52762
52742
|
const token = reserveBusy(s);
|
|
52763
|
-
broadcast({ type: "state", ts: Date.now(), busy: true }, s);
|
|
52743
|
+
broadcast({ type: "state", ts: Date.now(), busy: true, queueLen: s.pendingQueue.length }, s);
|
|
52764
52744
|
readBody(req, s.maxBodyBytes).then((raw) => {
|
|
52765
52745
|
const body = parseMessageBody(raw);
|
|
52766
52746
|
if ("error" in body) {
|
|
52767
52747
|
if (releaseBusy(s, token)) {
|
|
52768
|
-
broadcast({ type: "state", ts: Date.now(), busy: false }, s);
|
|
52748
|
+
broadcast({ type: "state", ts: Date.now(), busy: false, queueLen: s.pendingQueue.length }, s);
|
|
52769
52749
|
}
|
|
52770
52750
|
sendJson(res, 400, { error: body.error });
|
|
52771
52751
|
return;
|
|
52772
52752
|
}
|
|
52773
52753
|
s.lastPostTs = Date.now();
|
|
52774
|
-
|
|
52775
|
-
run.catch((err) => {
|
|
52776
|
-
if (releaseBusy(s, token)) {
|
|
52777
|
-
broadcast({ type: "error", ts: Date.now(), error: scrubError(err) }, s);
|
|
52778
|
-
broadcast({ type: "state", ts: Date.now(), busy: false }, s);
|
|
52779
|
-
}
|
|
52780
|
-
});
|
|
52754
|
+
submitTurn(s, bridge, body.text, body.images, token);
|
|
52781
52755
|
sendJson(res, 202, { ok: true });
|
|
52782
52756
|
}).catch((err) => {
|
|
52783
52757
|
if (releaseBusy(s, token)) {
|
|
52784
|
-
broadcast({ type: "state", ts: Date.now(), busy: false }, s);
|
|
52758
|
+
broadcast({ type: "state", ts: Date.now(), busy: false, queueLen: s.pendingQueue.length }, s);
|
|
52785
52759
|
}
|
|
52786
52760
|
readBodyError(req, res, err);
|
|
52787
52761
|
});
|
|
@@ -52797,6 +52771,105 @@ function tryOpenBrowser(url) {
|
|
|
52797
52771
|
} catch {}
|
|
52798
52772
|
}
|
|
52799
52773
|
|
|
52774
|
+
// src/modules/webui/log-forward.ts
|
|
52775
|
+
init_bus();
|
|
52776
|
+
init_context_log();
|
|
52777
|
+
var FORWARDED = ["info", "warn", "error"];
|
|
52778
|
+
function installOutputForwarding(state, bus = defaultOutputBus) {
|
|
52779
|
+
return bus.subscribe((ev) => {
|
|
52780
|
+
if (!FORWARDED.includes(ev.level))
|
|
52781
|
+
return;
|
|
52782
|
+
broadcast({
|
|
52783
|
+
type: "log",
|
|
52784
|
+
ts: ev.ts,
|
|
52785
|
+
level: ev.level,
|
|
52786
|
+
source: ev.source,
|
|
52787
|
+
text: redactContextSecrets(ev.text),
|
|
52788
|
+
diagnostic: ev.data?.diagnostic === true
|
|
52789
|
+
}, state);
|
|
52790
|
+
});
|
|
52791
|
+
}
|
|
52792
|
+
|
|
52793
|
+
// src/modules/webui/prompt-io.ts
|
|
52794
|
+
init_question();
|
|
52795
|
+
init_approve();
|
|
52796
|
+
var questionSeq = 0;
|
|
52797
|
+
function nextQuestionId() {
|
|
52798
|
+
questionSeq += 1;
|
|
52799
|
+
return `q${Date.now().toString(36)}-${questionSeq}`;
|
|
52800
|
+
}
|
|
52801
|
+
function toIndexes(value) {
|
|
52802
|
+
if (!Array.isArray(value))
|
|
52803
|
+
return [];
|
|
52804
|
+
const out = [];
|
|
52805
|
+
for (const item of value) {
|
|
52806
|
+
const n = typeof item === "number" ? item : Number(item);
|
|
52807
|
+
if (Number.isInteger(n) && n >= -1)
|
|
52808
|
+
out.push(n);
|
|
52809
|
+
}
|
|
52810
|
+
return out;
|
|
52811
|
+
}
|
|
52812
|
+
function toText(value) {
|
|
52813
|
+
if (typeof value === "string")
|
|
52814
|
+
return value;
|
|
52815
|
+
if (value == null)
|
|
52816
|
+
return "";
|
|
52817
|
+
return String(value);
|
|
52818
|
+
}
|
|
52819
|
+
function ask2(state, payload, coerce) {
|
|
52820
|
+
return new Promise((resolve27) => {
|
|
52821
|
+
const id = nextQuestionId();
|
|
52822
|
+
state.pendingQuestions.set(id, (value) => resolve27(coerce(value)));
|
|
52823
|
+
broadcast({ type: "question", ts: Date.now(), id, ...payload }, state);
|
|
52824
|
+
});
|
|
52825
|
+
}
|
|
52826
|
+
function createWebPromptIO(state) {
|
|
52827
|
+
return {
|
|
52828
|
+
askText(question) {
|
|
52829
|
+
return ask2(state, { text: question, kind: "text" }, toText);
|
|
52830
|
+
},
|
|
52831
|
+
askChoice(question, options, opts = {}) {
|
|
52832
|
+
return ask2(state, {
|
|
52833
|
+
text: question,
|
|
52834
|
+
kind: "choice",
|
|
52835
|
+
options: options.map((o) => o.label ?? String(o)),
|
|
52836
|
+
multiple: !!opts.multiple,
|
|
52837
|
+
allowCustom: !!opts.allowCustom
|
|
52838
|
+
}, toIndexes);
|
|
52839
|
+
}
|
|
52840
|
+
};
|
|
52841
|
+
}
|
|
52842
|
+
function resolveAnswer(state, id, value) {
|
|
52843
|
+
const resolver = state.pendingQuestions.get(id);
|
|
52844
|
+
if (!resolver)
|
|
52845
|
+
return false;
|
|
52846
|
+
state.pendingQuestions.delete(id);
|
|
52847
|
+
resolver(value);
|
|
52848
|
+
return true;
|
|
52849
|
+
}
|
|
52850
|
+
function cancelPendingQuestions(state) {
|
|
52851
|
+
const pending = [...state.pendingQuestions.values()];
|
|
52852
|
+
state.pendingQuestions.clear();
|
|
52853
|
+
for (const resolve27 of pending)
|
|
52854
|
+
resolve27(undefined);
|
|
52855
|
+
}
|
|
52856
|
+
function installWebPromptIO(deps) {
|
|
52857
|
+
if (deps.registry) {
|
|
52858
|
+
for (const tool of [questionTool, approveTool]) {
|
|
52859
|
+
if (!deps.registry.has(tool.name))
|
|
52860
|
+
deps.registry.register(tool);
|
|
52861
|
+
}
|
|
52862
|
+
}
|
|
52863
|
+
if (deps.toolCtx)
|
|
52864
|
+
deps.toolCtx.exitOnComplete = false;
|
|
52865
|
+
const io = createWebPromptIO(deps.state);
|
|
52866
|
+
deps.agent.setPromptIO(io);
|
|
52867
|
+
return io;
|
|
52868
|
+
}
|
|
52869
|
+
|
|
52870
|
+
// src/modules/webui/server.ts
|
|
52871
|
+
import { createServer } from "node:http";
|
|
52872
|
+
|
|
52800
52873
|
// src/modules/webui/body.ts
|
|
52801
52874
|
function parseJsonBody(raw) {
|
|
52802
52875
|
let parsed;
|
|
@@ -53111,6 +53184,134 @@ function handleSessionHistory(res, id, s) {
|
|
|
53111
53184
|
sendJson(res, 200, { id, history: normalizeHistory(log2, s.historyTail, messages) });
|
|
53112
53185
|
}
|
|
53113
53186
|
|
|
53187
|
+
// src/modules/webui/log-api.ts
|
|
53188
|
+
init_context_log();
|
|
53189
|
+
var TRACE_LOG_TYPES = [
|
|
53190
|
+
"assistant",
|
|
53191
|
+
"llm_usage",
|
|
53192
|
+
"compaction",
|
|
53193
|
+
"context_probe",
|
|
53194
|
+
"audit",
|
|
53195
|
+
"moe_plan",
|
|
53196
|
+
"moe_subtask",
|
|
53197
|
+
"moe_replan",
|
|
53198
|
+
"moe_verify",
|
|
53199
|
+
"output",
|
|
53200
|
+
"critic_check",
|
|
53201
|
+
"critic_verdict",
|
|
53202
|
+
"critic_escalation",
|
|
53203
|
+
"claim_retry"
|
|
53204
|
+
];
|
|
53205
|
+
var ALLOWED_TYPES = new Set([
|
|
53206
|
+
...TRACE_LOG_TYPES,
|
|
53207
|
+
"tool_call",
|
|
53208
|
+
"tool_result",
|
|
53209
|
+
"user",
|
|
53210
|
+
"error",
|
|
53211
|
+
"context"
|
|
53212
|
+
]);
|
|
53213
|
+
var DEFAULT_LIMIT2 = 100;
|
|
53214
|
+
var MAX_LIMIT = 500;
|
|
53215
|
+
function scrub2(text) {
|
|
53216
|
+
return text ? stripAnsi2(redactContextSecrets(text)) : undefined;
|
|
53217
|
+
}
|
|
53218
|
+
var STR_FIELDS = [
|
|
53219
|
+
"status",
|
|
53220
|
+
"kind",
|
|
53221
|
+
"provider",
|
|
53222
|
+
"model",
|
|
53223
|
+
"tool",
|
|
53224
|
+
"title",
|
|
53225
|
+
"subtaskId",
|
|
53226
|
+
"expertTag"
|
|
53227
|
+
];
|
|
53228
|
+
var NUM_FIELDS = [
|
|
53229
|
+
"iteration",
|
|
53230
|
+
"promptTokens",
|
|
53231
|
+
"completionTokens",
|
|
53232
|
+
"totalTokens",
|
|
53233
|
+
"duration",
|
|
53234
|
+
"cycle"
|
|
53235
|
+
];
|
|
53236
|
+
function copyStr(out, e, f) {
|
|
53237
|
+
const v = e[f];
|
|
53238
|
+
if (typeof v === "string")
|
|
53239
|
+
out[f] = v;
|
|
53240
|
+
}
|
|
53241
|
+
function copyNum(out, e, f) {
|
|
53242
|
+
const v = e[f];
|
|
53243
|
+
if (typeof v === "number")
|
|
53244
|
+
out[f] = v;
|
|
53245
|
+
}
|
|
53246
|
+
function pick(e) {
|
|
53247
|
+
const out = { ts: e.ts, type: e.type };
|
|
53248
|
+
for (const f of STR_FIELDS)
|
|
53249
|
+
copyStr(out, e, f);
|
|
53250
|
+
for (const f of NUM_FIELDS)
|
|
53251
|
+
copyNum(out, e, f);
|
|
53252
|
+
if (typeof e.success === "boolean")
|
|
53253
|
+
out.success = e.success;
|
|
53254
|
+
if (typeof e.tool_call_id === "string")
|
|
53255
|
+
out.toolCallId = e.tool_call_id;
|
|
53256
|
+
const text = scrub2(e.content ?? e.summary ?? e.message);
|
|
53257
|
+
if (text !== undefined)
|
|
53258
|
+
out.content = text;
|
|
53259
|
+
return out;
|
|
53260
|
+
}
|
|
53261
|
+
function parseQuery(url) {
|
|
53262
|
+
try {
|
|
53263
|
+
return new URL(url ?? "", "http://127.0.0.1").searchParams;
|
|
53264
|
+
} catch {
|
|
53265
|
+
return new URLSearchParams;
|
|
53266
|
+
}
|
|
53267
|
+
}
|
|
53268
|
+
function tailSessionLog(entries, opts = {}) {
|
|
53269
|
+
const types2 = opts.types && opts.types.length > 0 ? new Set(opts.types) : new Set(TRACE_LOG_TYPES);
|
|
53270
|
+
const since = opts.since ? Date.parse(opts.since) : NaN;
|
|
53271
|
+
const limit = Math.min(Math.max(opts.limit ?? DEFAULT_LIMIT2, 1), MAX_LIMIT);
|
|
53272
|
+
const out = [];
|
|
53273
|
+
for (const e of entries) {
|
|
53274
|
+
if (!ALLOWED_TYPES.has(e.type) || !types2.has(e.type))
|
|
53275
|
+
continue;
|
|
53276
|
+
if (!Number.isNaN(since)) {
|
|
53277
|
+
const ts = Date.parse(e.ts);
|
|
53278
|
+
if (Number.isNaN(ts) || ts <= since)
|
|
53279
|
+
continue;
|
|
53280
|
+
}
|
|
53281
|
+
out.push(pick(e));
|
|
53282
|
+
}
|
|
53283
|
+
return out.slice(-limit);
|
|
53284
|
+
}
|
|
53285
|
+
function handleSessionLog(req, res, id, s) {
|
|
53286
|
+
if (!s.sessionManager) {
|
|
53287
|
+
sendJson(res, 503, { error: "sessions_unavailable" });
|
|
53288
|
+
return;
|
|
53289
|
+
}
|
|
53290
|
+
const manager = s.sessionManager;
|
|
53291
|
+
if (!id || !manager.get(id)) {
|
|
53292
|
+
sendJson(res, 404, { error: "not_found" });
|
|
53293
|
+
return;
|
|
53294
|
+
}
|
|
53295
|
+
const q = parseQuery(req.url);
|
|
53296
|
+
const types2 = (q.get("types") ?? "").split(",").map((t2) => t2.trim()).filter((t2) => ALLOWED_TYPES.has(t2));
|
|
53297
|
+
const rawLimit = q.get("limit");
|
|
53298
|
+
const limit = rawLimit === null || rawLimit.trim() === "" ? undefined : Number(rawLimit);
|
|
53299
|
+
let log2 = [];
|
|
53300
|
+
try {
|
|
53301
|
+
log2 = manager.loadSessionLog?.(id) ?? [];
|
|
53302
|
+
} catch {
|
|
53303
|
+
log2 = [];
|
|
53304
|
+
}
|
|
53305
|
+
sendJson(res, 200, {
|
|
53306
|
+
id,
|
|
53307
|
+
log: tailSessionLog(log2, {
|
|
53308
|
+
types: types2,
|
|
53309
|
+
since: q.get("since") ?? undefined,
|
|
53310
|
+
limit: limit !== undefined && Number.isFinite(limit) ? limit : undefined
|
|
53311
|
+
})
|
|
53312
|
+
});
|
|
53313
|
+
}
|
|
53314
|
+
|
|
53114
53315
|
// src/modules/webui/sessions-api.ts
|
|
53115
53316
|
function handleSessionsGet(res, s) {
|
|
53116
53317
|
if (!s.sessionManager) {
|
|
@@ -53240,6 +53441,11 @@ function handleSessionsApi(req, res, reqUrl, s) {
|
|
|
53240
53441
|
handleSessionHistory(res, id, s);
|
|
53241
53442
|
return true;
|
|
53242
53443
|
}
|
|
53444
|
+
if (p.startsWith("/api/sessions/") && p.endsWith("/log") && req.method === "GET") {
|
|
53445
|
+
const id = decodeURIComponent(p.slice("/api/sessions/".length, -"/log".length));
|
|
53446
|
+
handleSessionLog(req, res, id, s);
|
|
53447
|
+
return true;
|
|
53448
|
+
}
|
|
53243
53449
|
if (p.startsWith("/api/sessions/") && req.method === "DELETE") {
|
|
53244
53450
|
const id = decodeURIComponent(p.slice("/api/sessions/".length));
|
|
53245
53451
|
handleSessionDelete(res, id, s);
|
|
@@ -54574,7 +54780,7 @@ function handleEvents(req, res, s) {
|
|
|
54574
54780
|
const greeted = res.write(`retry: 1200
|
|
54575
54781
|
|
|
54576
54782
|
`) && res.write(`event: state
|
|
54577
|
-
data: ${JSON.stringify({ busy: s.busy })}
|
|
54783
|
+
data: ${JSON.stringify({ busy: s.busy, queueLen: s.pendingQueue.length })}
|
|
54578
54784
|
|
|
54579
54785
|
`);
|
|
54580
54786
|
if (!greeted) {
|
|
@@ -54607,7 +54813,11 @@ function dropClient(res, s) {
|
|
|
54607
54813
|
}
|
|
54608
54814
|
var API_ROUTES = {
|
|
54609
54815
|
"GET /api/state": (_req, res, s) => {
|
|
54610
|
-
sendJson(res, 200, {
|
|
54816
|
+
sendJson(res, 200, {
|
|
54817
|
+
busy: s.busy,
|
|
54818
|
+
queueLen: s.pendingQueue.length,
|
|
54819
|
+
state: s.bridge?.getState?.() ?? null
|
|
54820
|
+
});
|
|
54611
54821
|
},
|
|
54612
54822
|
"POST /api/message": (req, res, s) => handleMessagePost(req, res, s),
|
|
54613
54823
|
"GET /api/config": (_req, res, s) => handleConfigGet(res, s),
|
|
@@ -54769,6 +54979,7 @@ function stopServer(s = getDefaultState()) {
|
|
|
54769
54979
|
s.busy = false;
|
|
54770
54980
|
s.agentRunning = false;
|
|
54771
54981
|
s.turnToken = null;
|
|
54982
|
+
s.pendingQueue.length = 0;
|
|
54772
54983
|
s.lastPostTs = 0;
|
|
54773
54984
|
}
|
|
54774
54985
|
|
|
@@ -54890,7 +55101,7 @@ function makeRunAgent(deps) {
|
|
|
54890
55101
|
toolCalls,
|
|
54891
55102
|
interrupted: interrupted || undefined
|
|
54892
55103
|
});
|
|
54893
|
-
broadcast({ type: "state", ts: Date.now(), busy: state.busy, usage: state.usage }, state);
|
|
55104
|
+
broadcast({ type: "state", ts: Date.now(), busy: state.busy, queueLen: state.pendingQueue.length, usage: state.usage }, state);
|
|
54894
55105
|
return result;
|
|
54895
55106
|
} catch (err) {
|
|
54896
55107
|
forwarder.onTurnEnd({ success: false, interrupted: interrupted || undefined });
|
|
@@ -54915,7 +55126,7 @@ function makeReconfigure(state, agent, after) {
|
|
|
54915
55126
|
await after();
|
|
54916
55127
|
} catch {}
|
|
54917
55128
|
}
|
|
54918
|
-
broadcast({ type: "state", ts: Date.now(), busy: state.busy, usage: state.usage }, state);
|
|
55129
|
+
broadcast({ type: "state", ts: Date.now(), busy: state.busy, queueLen: state.pendingQueue.length, usage: state.usage }, state);
|
|
54919
55130
|
};
|
|
54920
55131
|
}
|
|
54921
55132
|
async function startWebUi(deps) {
|
|
@@ -54939,8 +55150,16 @@ async function startWebUi(deps) {
|
|
|
54939
55150
|
if (deps.indexer.refresh) {
|
|
54940
55151
|
const refresh = deps.indexer.refresh.bind(deps.indexer);
|
|
54941
55152
|
state.refreshIndex = async () => {
|
|
54942
|
-
|
|
54943
|
-
|
|
55153
|
+
broadcast({ type: "index", ts: Date.now(), phase: "start" }, state);
|
|
55154
|
+
try {
|
|
55155
|
+
await refresh();
|
|
55156
|
+
} catch {
|
|
55157
|
+
broadcast({ type: "index", ts: Date.now(), phase: "error" }, state);
|
|
55158
|
+
throw new Error("index refresh failed");
|
|
55159
|
+
}
|
|
55160
|
+
const stats = state.projectMapStats?.() ?? null;
|
|
55161
|
+
broadcast({ type: "index", ts: Date.now(), phase: "done", stats }, state);
|
|
55162
|
+
broadcast({ type: "state", ts: Date.now(), busy: state.busy, queueLen: state.pendingQueue.length, usage: state.usage }, state);
|
|
54944
55163
|
};
|
|
54945
55164
|
}
|
|
54946
55165
|
}
|
|
@@ -54956,7 +55175,9 @@ async function startWebUi(deps) {
|
|
|
54956
55175
|
if (modelCache)
|
|
54957
55176
|
modelCache.refresh();
|
|
54958
55177
|
state.reconfigure = makeReconfigure(state, deps.agent, modelCache ? () => modelCache.refresh() : undefined);
|
|
54959
|
-
const forwarder = makeForwarder(state
|
|
55178
|
+
const forwarder = makeForwarder(state, undefined, {
|
|
55179
|
+
onQueueDrained: () => drainQueueTurn(state)
|
|
55180
|
+
});
|
|
54960
55181
|
const stopOutputForwarding = installOutputForwarding(state);
|
|
54961
55182
|
let probe;
|
|
54962
55183
|
if (deps.contextProbe) {
|