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/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
- sink({ type: "turn_end", ts: Date.now(), ...summary });
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
- sendJson(res, 409, { error: "busy" });
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
- const run = Promise.resolve(bridge.submit(body.text, { images: body.images }));
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, { busy: s.busy, state: s.bridge?.getState?.() ?? null });
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
- await refresh();
54943
- broadcast({ type: "state", ts: Date.now(), busy: state.busy, usage: state.usage }, state);
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) {