omnius 1.0.663 → 1.0.665

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/index.js CHANGED
@@ -63552,6 +63552,30 @@ function pushBounded(buf, item) {
63552
63552
  if (buf.length > MAX_BUFFER)
63553
63553
  buf.splice(0, buf.length - MAX_BUFFER);
63554
63554
  }
63555
+ function captureBrowserDiagnosticCursor() {
63556
+ return {
63557
+ startedAtMs: Date.now(),
63558
+ consoleIndex: consoleBuffer.length,
63559
+ networkIndex: networkBuffer.length,
63560
+ errorIndex: errorBuffer.length,
63561
+ websocketIndex: wsBuffer.length,
63562
+ crashCount
63563
+ };
63564
+ }
63565
+ function diagnosticEntriesAfter(entries2, index, startedAtMs) {
63566
+ return index <= entries2.length ? entries2.slice(index) : entries2.filter((entry) => entry.ts >= startedAtMs);
63567
+ }
63568
+ function browserDiagnosticWindow(cursor) {
63569
+ return {
63570
+ startedAtMs: cursor.startedAtMs,
63571
+ endedAtMs: Date.now(),
63572
+ console: diagnosticEntriesAfter(consoleBuffer, cursor.consoleIndex, cursor.startedAtMs),
63573
+ network: diagnosticEntriesAfter(networkBuffer, cursor.networkIndex, cursor.startedAtMs),
63574
+ errors: diagnosticEntriesAfter(errorBuffer, cursor.errorIndex, cursor.startedAtMs),
63575
+ websockets: diagnosticEntriesAfter(wsBuffer, cursor.websocketIndex, cursor.startedAtMs),
63576
+ crashCount: Math.max(0, crashCount - cursor.crashCount)
63577
+ };
63578
+ }
63555
63579
  function which(cmd) {
63556
63580
  try {
63557
63581
  const out = execFileSync("which", [cmd], {
@@ -63663,6 +63687,11 @@ function asOptionalBoolean(value2) {
63663
63687
  function defaultPlaywrightHeadless() {
63664
63688
  return asOptionalBoolean(process.env["OMNIUS_PLAYWRIGHT_HEADLESS"] ?? process.env["OMNIUS_BROWSER_HEADLESS"]) ?? true;
63665
63689
  }
63690
+ function hasGraphicalBrowserDisplay() {
63691
+ if (process.platform !== "linux")
63692
+ return true;
63693
+ return Boolean(process.env["DISPLAY"]?.trim() || process.env["WAYLAND_DISPLAY"]?.trim());
63694
+ }
63666
63695
  async function installPlaywrightBrowser() {
63667
63696
  try {
63668
63697
  mkdirSync18(PLAYWRIGHT_RUNTIME_DIR, { recursive: true });
@@ -63927,37 +63956,6 @@ function attachDiagnosticListeners(p2) {
63927
63956
  } catch {
63928
63957
  }
63929
63958
  });
63930
- if (!initScriptInstalled) {
63931
- try {
63932
- p2.addInitScript(`(() => {
63933
- if (window.__browserEvents) return;
63934
- window.__browserEvents = [];
63935
- const push = (evt) => {
63936
- window.__browserEvents.push(evt);
63937
- if (window.__browserEvents.length > 2000) window.__browserEvents.splice(0, window.__browserEvents.length - 2000);
63938
- };
63939
- const origError = console.error;
63940
- console.error = function() {
63941
- push({ ts: Date.now(), type: "console:error", text: Array.from(arguments).map(String).join(" ").slice(0, 2000) });
63942
- return origError.apply(console, arguments);
63943
- };
63944
- const origWarn = console.warn;
63945
- console.warn = function() {
63946
- push({ ts: Date.now(), type: "console:warn", text: Array.from(arguments).map(String).join(" ").slice(0, 2000) });
63947
- return origWarn.apply(console, arguments);
63948
- };
63949
- window.onerror = function(msg, source, lineno, colno, err) {
63950
- push({ ts: Date.now(), type: "onerror", text: String(msg).slice(0, 2000), stack: err instanceof Error ? (err.stack || "").slice(0, 3000) : "" });
63951
- };
63952
- window.onunhandledrejection = function(evt) {
63953
- const r = evt.reason;
63954
- push({ ts: Date.now(), type: "unhandledRejection", text: String(r?.message ?? r ?? "unknown").slice(0, 2000), stack: typeof r?.stack === "string" ? r.stack.slice(0, 3000) : "" });
63955
- };
63956
- })()`);
63957
- initScriptInstalled = true;
63958
- } catch {
63959
- }
63960
- }
63961
63959
  }
63962
63960
  function clearDiagnosticBuffers() {
63963
63961
  consoleBuffer = [];
@@ -64145,6 +64143,9 @@ async function runBrowserLocatorAction(pageHandle, selector, index, timeout2, ac
64145
64143
  async function waitForBrowserExpectation(pageHandle, expectation, timeout2) {
64146
64144
  await pageHandle.locator(expectation).first().waitFor({ state: "visible", timeout: timeout2 });
64147
64145
  }
64146
+ async function browserExpectationIsVisible(pageHandle, expectation) {
64147
+ return Boolean(await pageHandle.locator(expectation).first().isVisible().catch(() => false));
64148
+ }
64148
64149
  async function runBrowserInteractionWithEvidence(input) {
64149
64150
  const before = await captureBrowserInteractionSnapshot(input.pageHandle);
64150
64151
  let performed = null;
@@ -64736,6 +64737,169 @@ async function findBrowserVisualCandidate(pageHandle, target, visualX, visualY,
64736
64737
  }
64737
64738
  return candidates.filter((candidate) => includeOffscreen || candidate["visible"] === true).sort((a2, b) => Number(b["score"] ?? 0) - Number(a2["score"] ?? 0))[0] ?? null;
64738
64739
  }
64740
+ async function captureBrowserObservation(input) {
64741
+ const html = await input.pageHandle.content();
64742
+ const readableText = await input.pageHandle.evaluate(`(() => {
64743
+ const clone = document.body ? document.body.cloneNode(true) : document.documentElement.cloneNode(true);
64744
+ clone.querySelectorAll("script, style, noscript, svg, path").forEach(el => el.remove());
64745
+ return clone.innerText || clone.textContent || "";
64746
+ })()`);
64747
+ const summary = downsampleDom(html);
64748
+ lastDomSummarySelectors = extractDomSummarySelectors(summary);
64749
+ const screenshotBuffer = await input.pageHandle.screenshot({
64750
+ fullPage: input.fullPage
64751
+ });
64752
+ const screenshotPath = saveBuffer(input.outputPath || defaultArtifactPath(input.action, "png"), screenshotBuffer);
64753
+ const viewport = input.pageHandle.viewportSize?.() ?? {
64754
+ width: 0,
64755
+ height: 0
64756
+ };
64757
+ const diagnostics = input.cursor ? browserDiagnosticWindow(input.cursor) : {
64758
+ startedAtMs: input.startMs,
64759
+ endedAtMs: Date.now(),
64760
+ console: [...consoleBuffer],
64761
+ network: [...networkBuffer],
64762
+ errors: [...errorBuffer],
64763
+ websockets: [...wsBuffer],
64764
+ crashCount
64765
+ };
64766
+ const consoleErrors = diagnostics.console.filter((entry) => /error/i.test(entry.type)).slice(-30);
64767
+ const consoleWarnings = diagnostics.console.filter((entry) => /warn/i.test(entry.type)).slice(-30);
64768
+ const networkFailures = diagnostics.network.filter((entry) => entry.ok === false || (entry.status ?? 0) >= 400).slice(-30);
64769
+ const pageErrors = diagnostics.errors.slice(-30);
64770
+ const gate = classifyBrowserGate({
64771
+ url: input.pageHandle.url(),
64772
+ html,
64773
+ text: String(readableText ?? ""),
64774
+ network: diagnostics.network,
64775
+ errors: diagnostics.errors
64776
+ });
64777
+ const actualObservationMs = Math.max(0, diagnostics.endedAtMs - diagnostics.startedAtMs);
64778
+ const runtimeFailureCount = consoleErrors.length + pageErrors.length + networkFailures.length + diagnostics.crashCount;
64779
+ const blocked = gate.kind === "captcha" || gate.kind === "bot_challenge";
64780
+ const contradicted = input.expectationSatisfied === false || runtimeFailureCount > 0;
64781
+ const verification = blocked ? {
64782
+ status: "blocked",
64783
+ confidence: gate.confidence,
64784
+ reason: `Browser observation was blocked by gate=${gate.kind}.`
64785
+ } : contradicted ? {
64786
+ status: "contradicted",
64787
+ confidence: 0.96,
64788
+ reason: input.expectationSatisfied === false ? `The declared postcondition was not observed: ${input.expectation}.` : `The browser emitted ${runtimeFailureCount} runtime failure signal(s) during the observation window.`,
64789
+ nextSuggestedObservation: "Fix the concrete console/page/network failure, then rerun validate_lifecycle or observe_window in the same browser mode."
64790
+ } : input.expectationSatisfied === true ? {
64791
+ status: "proven",
64792
+ confidence: 0.9,
64793
+ reason: `The declared postcondition remained visible and no browser runtime failures appeared during the ${actualObservationMs}ms observation window.`
64794
+ } : {
64795
+ status: "inconclusive",
64796
+ confidence: 0.58,
64797
+ reason: `The browser remained under observation for ${actualObservationMs}ms, but no explicit postcondition was evaluated.`,
64798
+ nextSuggestedObservation: "Repeat with expect set to a state-specific selector before making a user-visible success claim."
64799
+ };
64800
+ const mode = browserHeadless === false ? "headed" : "headless";
64801
+ const evidenceEvent = createEvidenceEvent({
64802
+ sessionId: playwrightSessionId ?? void 0,
64803
+ tool: "playwright_browser",
64804
+ action: input.action,
64805
+ surface: "browser",
64806
+ claim: input.expectation ? `Browser postcondition: ${input.expectation}` : "Timed browser runtime observation",
64807
+ after: createEvidenceObservation({
64808
+ screenshotPath,
64809
+ screenshotHash: sha256Buffer(screenshotBuffer),
64810
+ width: viewport.width,
64811
+ height: viewport.height,
64812
+ url: input.pageHandle.url(),
64813
+ title: await input.pageHandle.title(),
64814
+ domSummaryHash: sha256Text(summary),
64815
+ readableTextHash: sha256Text(String(readableText ?? "")),
64816
+ visibleTextExcerpt: clipEvidenceText(readableText, 500),
64817
+ consoleErrorCount: consoleErrors.length,
64818
+ pageErrorCount: pageErrors.length,
64819
+ networkFailureCount: networkFailures.length,
64820
+ gateKind: gate.kind,
64821
+ gateConfidence: gate.confidence,
64822
+ gateEvidence: gate.evidence,
64823
+ metadata: {
64824
+ mode,
64825
+ engine: "playwright",
64826
+ requestedObservationMs: input.requestedObservationMs,
64827
+ actualObservationMs,
64828
+ diagnosticsScope: input.cursor ? "window" : "session",
64829
+ consoleWarningCount: consoleWarnings.length,
64830
+ websocketEventCount: diagnostics.websockets.length,
64831
+ crashCount: diagnostics.crashCount,
64832
+ expectation: input.expectation,
64833
+ expectationSatisfied: input.expectationSatisfied
64834
+ }
64835
+ }),
64836
+ verification
64837
+ });
64838
+ const bundle = {
64839
+ schema: "omnius.browser-observation-window.v1",
64840
+ id: timestampSlug(),
64841
+ engine: "playwright",
64842
+ mode,
64843
+ url: input.pageHandle.url(),
64844
+ title: await input.pageHandle.title(),
64845
+ viewport,
64846
+ screenshotPath,
64847
+ observationWindow: {
64848
+ requestedMs: input.requestedObservationMs,
64849
+ actualMs: actualObservationMs,
64850
+ startedAt: new Date(diagnostics.startedAtMs).toISOString(),
64851
+ endedAt: new Date(diagnostics.endedAtMs).toISOString(),
64852
+ diagnosticsScope: input.cursor ? "window" : "session"
64853
+ },
64854
+ expectation: input.expectation ?? null,
64855
+ expectationSatisfied: input.expectationSatisfied ?? null,
64856
+ expectationError: input.expectationError ?? null,
64857
+ gate,
64858
+ consoleErrors,
64859
+ consoleWarnings,
64860
+ networkFailures,
64861
+ pageErrors,
64862
+ websocketEvents: diagnostics.websockets.slice(-30),
64863
+ crashes: diagnostics.crashCount,
64864
+ domSummary: summary.slice(0, 12e3),
64865
+ evidenceEvents: [evidenceEvent]
64866
+ };
64867
+ const failureDetails = [
64868
+ ...consoleErrors.map((entry) => `console ${entry.type}: ${entry.text}${entry.loc ? ` (${entry.loc})` : ""}`),
64869
+ ...pageErrors.map((entry) => `page error: ${entry.message}`),
64870
+ ...networkFailures.map((entry) => `network: ${entry.method} ${entry.status ?? "FAILED"} ${entry.url}${entry.failure ? ` (${entry.failure})` : ""}`)
64871
+ ].slice(0, 30);
64872
+ const output2 = [
64873
+ "Browser observation bundle",
64874
+ `URL: ${bundle.url}`,
64875
+ `Title: ${bundle.title}`,
64876
+ `Mode: ${mode}`,
64877
+ `Observation window: ${actualObservationMs}ms (${input.cursor ? "window-scoped" : "session-scoped"} diagnostics)`,
64878
+ `Viewport: ${viewport.width}x${viewport.height}`,
64879
+ `Screenshot: ${screenshotPath}`,
64880
+ `Gate: ${gate.kind} (${gate.confidence.toFixed(2)})${gate.evidence.length ? ` — ${gate.evidence.join("; ")}` : ""}`,
64881
+ `Console errors: ${consoleErrors.length}`,
64882
+ `Console warnings: ${consoleWarnings.length}`,
64883
+ `Page errors: ${pageErrors.length}`,
64884
+ `Network failures: ${networkFailures.length}`,
64885
+ `Verification: ${verification.status} — ${verification.reason}`,
64886
+ ...failureDetails.length > 0 ? ["", "--- Runtime failures ---", ...failureDetails] : [],
64887
+ "",
64888
+ "--- DOM Summary ---",
64889
+ summary.slice(0, 6e3)
64890
+ ].join("\n");
64891
+ const shouldFail = input.failOnRuntimeErrors && contradicted;
64892
+ return {
64893
+ success: !shouldFail,
64894
+ output: output2,
64895
+ ...shouldFail ? {
64896
+ error: `Browser lifecycle validation failed: ${runtimeFailureCount} runtime failure signal(s)${input.expectationSatisfied === false ? "; declared postcondition was not observed" : ""}.`
64897
+ } : {},
64898
+ llmContent: JSON.stringify(bundle, null, 2),
64899
+ evidenceEvents: [evidenceEvent],
64900
+ durationMs: Date.now() - input.startMs
64901
+ };
64902
+ }
64739
64903
  function ok(output2, start2, skipDiag = false) {
64740
64904
  const diag = skipDiag ? "" : formatDiagnosticSummary();
64741
64905
  return {
@@ -64748,7 +64912,7 @@ ${diag}` : output2,
64748
64912
  function fail(error, start2) {
64749
64913
  return { success: false, output: "", error, durationMs: Date.now() - start2 };
64750
64914
  }
64751
- var pw, browser, context, page, browserHeadless, playwrightSessionId, playwrightSessionSequence, MAX_BUFFER, consoleBuffer, networkBuffer, errorBuffer, wsBuffer, crashCount, initScriptInstalled, lastDomSummarySelectors, dynamicImport, PLAYWRIGHT_RUNTIME_DIR, PLAYWRIGHT_BROWSERS_DIR, PLAYWRIGHT_CHROMIUM_INSTALL_ARGS, PlaywrightBrowserTool;
64915
+ var pw, browser, context, page, browserHeadless, playwrightSessionId, playwrightSessionSequence, MAX_BUFFER, consoleBuffer, networkBuffer, errorBuffer, wsBuffer, crashCount, lastDomSummarySelectors, dynamicImport, PLAYWRIGHT_RUNTIME_DIR, PLAYWRIGHT_BROWSERS_DIR, PLAYWRIGHT_CHROMIUM_INSTALL_ARGS, PlaywrightBrowserTool;
64752
64916
  var init_playwright_browser = __esm({
64753
64917
  "packages/execution/dist/tools/playwright-browser.js"() {
64754
64918
  "use strict";
@@ -64769,15 +64933,18 @@ var init_playwright_browser = __esm({
64769
64933
  errorBuffer = [];
64770
64934
  wsBuffer = [];
64771
64935
  crashCount = 0;
64772
- initScriptInstalled = false;
64773
64936
  lastDomSummarySelectors = /* @__PURE__ */ new Map();
64774
- dynamicImport = new Function("mod", "return import(mod)");
64937
+ dynamicImport = (mod3) => import(
64938
+ /* @vite-ignore */
64939
+ mod3
64940
+ );
64775
64941
  PLAYWRIGHT_RUNTIME_DIR = join22(homedir7(), ".omnius", "playwright-runtime");
64776
64942
  PLAYWRIGHT_BROWSERS_DIR = join22(PLAYWRIGHT_RUNTIME_DIR, "browsers");
64777
64943
  PLAYWRIGHT_CHROMIUM_INSTALL_ARGS = ["playwright", "install", "chromium"];
64778
64944
  PlaywrightBrowserTool = class {
64779
64945
  name = "playwright_browser";
64780
- description = "Full-scope Playwright browser automation + diagnostic capture. Launches a persistent headless Chromium session by default, with optional visible/headed mode when a GUI display is available. Beyond navigation/interaction, this tool buffers everything the running app emits (console messages, network requests, JS exceptions, accessibility tree) so the agent can verify what is ACTUALLY happening — not just what the build/test reports. Auto-installs Playwright + Chromium on first use without sudo or OS package manager escalation. Diagnostic actions: observe_bundle, dom_summary, dom, console_logs, network_log, page_errors, websocket_log, a11y_snapshot, bounding_box, query_all, performance, cookies, storage, viewport, clear_diagnostics. Interaction actions: navigate, click, visual_click, fill, type, press, select, check, hover. Use fill with a selector or natural-language target for form fields; avoid raw evaluate for form filling because direct .value assignment does not fire app input/change events. For state-changing interactions, pass expect as a Playwright selector for the visible post-action state (for example text=Stop Simulation). A dispatched click without an asserted postcondition is recorded as inconclusive, not completion proof. Selectors are strict by default: when a selector matches more than one element, use a more specific selector or index (zero-based). Detached/re-rendered elements are re-queried once before failing. This is a separate browser/runtime from browser_action; once you start a workflow here, continue here unless you intentionally navigate browser_action to the same URL. Every result carries a typed runtime-session receipt; use hardware_prerequisite before declaring a WebSerial, WebUSB, camera, or microphone interaction blocked by a real device/permission boundary. Capture actions: screenshot, pdf, content, innerText, innerHTML, getAttribute, evaluate. Loopback URLs (localhost, 127.0.0.1, ::1) are allowed for local development servers; private LAN and metadata URLs remain blocked. Workflow for user-facing work: start/serve the system with the stack-native tool, navigate to the real URL, then inspect page_errors, console_logs, network_log, DOM/accessibility, and screenshot evidence before completion. Build/typecheck/test output is only one layer; runtime browser evidence is required when the delivered artifact is a page, app, dashboard, game, form, visualization, or other UI. Repeat navigate/act/observe until the actual user flow is clean.";
64946
+ modelFacingSummary = "Validate a live UI in one persistent Playwright session. For GUI, GPU, WebGL, WebGPU, canvas, or animation claims, use validate_lifecycle with headless=false, force_new=true, an explicit expect, and a finite observation_ms. Use observe_window after interactions. Inspect delayed console, page, network, DOM, and screenshot evidence. A clean headless or immediate load is not headed lifecycle proof.";
64947
+ description = "Persistent Playwright browser lifecycle validation with timed console, page-error, network, DOM, and screenshot capture. Launches a persistent headless Chromium session by default, with optional visible/headed mode when a GUI display is available. Beyond navigation/interaction, this tool buffers everything the running app emits (console messages, network requests, JS exceptions, accessibility tree) so the agent can verify what is ACTUALLY happening — not just what the build/test reports. Auto-installs Playwright + Chromium on first use without sudo or OS package manager escalation. Diagnostic actions: validate_lifecycle, observe_window, observe_bundle, dom_summary, dom, console_logs, network_log, page_errors, websocket_log, a11y_snapshot, bounding_box, query_all, performance, cookies, storage, viewport, clear_diagnostics. Interaction actions: navigate, click, visual_click, fill, type, press, select, check, hover. Use fill with a selector or natural-language target for form fields; avoid raw evaluate for form filling because direct .value assignment does not fire app input/change events. For state-changing interactions, pass expect as a Playwright selector for the visible post-action state (for example text=Stop Simulation). A dispatched click without an asserted postcondition is recorded as inconclusive, not completion proof. Selectors are strict by default: when a selector matches more than one element, use a more specific selector or index (zero-based). Detached/re-rendered elements are re-queried once before failing. This is a separate browser/runtime from browser_action; once you start a workflow here, continue here unless you intentionally navigate browser_action to the same URL. Every result carries a typed runtime-session receipt; use hardware_prerequisite before declaring a WebSerial, WebUSB, camera, or microphone interaction blocked by a real device/permission boundary. Capture actions: screenshot, pdf, content, innerText, innerHTML, getAttribute, evaluate. Loopback URLs (localhost, 127.0.0.1, ::1) are allowed for local development servers; private LAN and metadata URLs remain blocked. Workflow for user-facing work: start/serve the system with the stack-native tool, then run validate_lifecycle against the real URL with an explicit expect and observation_ms. For GPU, WebGL, WebGPU, canvas, animation, or failures seen in a normal GUI browser, use headless=false and force_new=true; a clean headless run does not verify headed GPU behavior. Use observe_window after interactions to keep the same browser alive while delayed failures surface, then inspect page_errors, console_logs, network_log, DOM/accessibility, and screenshot evidence before completion. Build/typecheck/test output is only one layer; runtime browser evidence is required when the delivered artifact is a page, app, dashboard, game, form, visualization, or other UI. Repeat navigate/act/observe until the actual user flow is clean.";
64781
64948
  parameters = {
64782
64949
  type: "object",
64783
64950
  properties: {
@@ -64824,12 +64991,14 @@ var init_playwright_browser = __esm({
64824
64991
  "storage",
64825
64992
  "viewport",
64826
64993
  "observe_bundle",
64994
+ "observe_window",
64995
+ "validate_lifecycle",
64827
64996
  "visual_click",
64828
64997
  "hardware_prerequisite",
64829
64998
  "clear_diagnostics",
64830
64999
  "close"
64831
65000
  ],
64832
- description: "Action to perform:\n- navigate: go to a URL\n- click: click element by selector\n- fill: clear input and type text by selector, or by natural-language target when selector is absent\n- type: type text character by character into a selector, or into the currently focused element after visual_click\n- press: press a key (Enter, Tab, Escape, etc.)\n- screenshot: capture the headless browser page, not the desktop; use value to choose the output file path\n- observe_bundle: capture URL/title/viewport, DOM summary, a11y, diagnostics, screenshot, and gate assessment\n- visual_click: browser screenshot -> Moondream point -> elementFromPoint -> human-like Playwright mouse click -> post-action screenshot\n- hardware_prerequisite: record whether a WebSerial/WebUSB/media flow has the browser capability, secure context, and required physical/user-gesture prerequisite; it never pretends hardware was exercised\n- evaluate: run JavaScript in page context\n- content: get page text content (readable, stripped)\n- dom: get raw page HTML (truncated)\n- dom_summary: compact interactive DOM summary with selectors\n- innerText: get innerText of a specific element\n- select: select dropdown option by value\n- check/uncheck: toggle checkbox\n- hover: hover over element\n- wait: wait for a selector to appear, or sleep for timeout ms when no selector is provided\n- waitForNavigation: wait for page navigation to complete\n- waitForSelector: wait for element matching selector\n- title: get page title\n- url: get current URL\n- getAttribute: get element attribute value\n- innerHTML: get element's innerHTML\n- textContent: get element's textContent\n- goBack/goForward/reload: browser navigation\n- pdf: save page as PDF\n- close: close browser session"
65001
+ description: "Action to perform:\n- navigate: go to a URL\n- click: click element by selector\n- fill: clear input and type text by selector, or by natural-language target when selector is absent\n- type: type text character by character into a selector, or into the currently focused element after visual_click\n- press: press a key (Enter, Tab, Escape, etc.)\n- screenshot: capture the headless browser page, not the desktop; use value to choose the output file path\n- observe_bundle: capture URL/title/viewport, DOM summary, a11y, diagnostics, screenshot, and gate assessment\n- observe_window: keep the current browser/page alive for observation_ms, then return only diagnostics emitted during that time window plus DOM/screenshot evidence\n- validate_lifecycle: start a fresh headed browser by default, clear diagnostics, navigate to url, hold it for observation_ms, and fail on delayed console/page/network errors; use this for GUI/GPU/WebGL validation\n- visual_click: browser screenshot -> Moondream point -> elementFromPoint -> human-like Playwright mouse click -> post-action screenshot\n- hardware_prerequisite: record whether a WebSerial/WebUSB/media flow has the browser capability, secure context, and required physical/user-gesture prerequisite; it never pretends hardware was exercised\n- evaluate: run JavaScript in page context\n- content: get page text content (readable, stripped)\n- dom: get raw page HTML (truncated)\n- dom_summary: compact interactive DOM summary with selectors\n- innerText: get innerText of a specific element\n- select: select dropdown option by value\n- check/uncheck: toggle checkbox\n- hover: hover over element\n- wait: wait for a selector to appear, or sleep for timeout ms when no selector is provided\n- waitForNavigation: wait for page navigation to complete\n- waitForSelector: wait for element matching selector\n- title: get page title\n- url: get current URL\n- getAttribute: get element attribute value\n- innerHTML: get element's innerHTML\n- textContent: get element's textContent\n- goBack/goForward/reload: browser navigation\n- pdf: save page as PDF\n- close: close browser session"
64833
65002
  },
64834
65003
  url: {
64835
65004
  type: "string",
@@ -64895,6 +65064,14 @@ var init_playwright_browser = __esm({
64895
65064
  type: "number",
64896
65065
  description: "Milliseconds to wait for expect after the action (defaults to timeout)."
64897
65066
  },
65067
+ observation_ms: {
65068
+ type: "number",
65069
+ description: "Milliseconds to keep the instantiated page alive while collecting delayed diagnostics. Defaults to 8000 for validate_lifecycle and 5000 for observe_window; maximum 120000."
65070
+ },
65071
+ start_delay_ms: {
65072
+ type: "number",
65073
+ description: "Optional delay before opening an observe_window diagnostic window. Use this only when the requested lifecycle has an explicit warm-up phase; maximum 60000."
65074
+ },
64898
65075
  hardware_capability: {
64899
65076
  type: "string",
64900
65077
  enum: ["web_serial", "web_usb", "camera", "microphone"],
@@ -64906,7 +65083,7 @@ var init_playwright_browser = __esm({
64906
65083
  },
64907
65084
  headless: {
64908
65085
  type: "boolean",
64909
- description: "Launch mode for a new browser session. Defaults to true. Set false to use a visible GUI Chromium when DISPLAY/desktop access is available; changing this restarts the browser session."
65086
+ description: "Launch mode for a new browser session. Normal actions default to true; validate_lifecycle defaults to false. For GUI/GPU/WebGL claims, set false explicitly. A headless result cannot verify headed graphics behavior."
64910
65087
  },
64911
65088
  force_new: {
64912
65089
  type: "boolean",
@@ -64948,8 +65125,12 @@ var init_playwright_browser = __esm({
64948
65125
  const expectationTimeout = typeof args.expect_timeout === "number" ? Math.max(1, Math.min(12e4, Math.round(args.expect_timeout))) : timeout2;
64949
65126
  const width = typeof args.width === "number" ? Math.max(320, Math.min(3840, Math.round(args.width))) : void 0;
64950
65127
  const height = typeof args.height === "number" ? Math.max(240, Math.min(2160, Math.round(args.height))) : void 0;
64951
- const headless = asOptionalBoolean(args.headless);
64952
- const forceNew = asOptionalBoolean(args.force_new) === true;
65128
+ const requestedHeadless = asOptionalBoolean(args.headless);
65129
+ const validatesFullLifecycle = action === "validate_lifecycle";
65130
+ const headless = requestedHeadless ?? (validatesFullLifecycle ? false : void 0);
65131
+ const forceNew = asOptionalBoolean(args.force_new) === true || validatesFullLifecycle;
65132
+ const observationMs = typeof args.observation_ms === "number" ? Math.max(0, Math.min(12e4, Math.round(args.observation_ms))) : validatesFullLifecycle ? 8e3 : 5e3;
65133
+ const startDelayMs = typeof args.start_delay_ms === "number" ? Math.max(0, Math.min(6e4, Math.round(args.start_delay_ms))) : 0;
64953
65134
  const visualTarget = typeof args.target === "string" && args.target.trim() ? args.target.trim() : typeof args.text === "string" && args.text.trim() ? args.text.trim() : "";
64954
65135
  const preferredVisionModel = normalizeVisionModelName(args.vision_model);
64955
65136
  const delayMs = typeof args.delay_ms === "number" ? Math.max(0, Math.min(3e4, Math.round(args.delay_ms))) : 700;
@@ -64964,6 +65145,9 @@ var init_playwright_browser = __esm({
64964
65145
  durationMs: Date.now() - start2
64965
65146
  };
64966
65147
  }
65148
+ if (headless === false && !hasGraphicalBrowserDisplay()) {
65149
+ return fail("Headed Chromium was required, but no graphical DISPLAY or WAYLAND_DISPLAY is available. Report headed GUI/GPU validation as blocked; do not substitute a clean headless run.", start2);
65150
+ }
64967
65151
  const err = await ensureBrowser({ headless, forceNew });
64968
65152
  if (err)
64969
65153
  return {
@@ -65689,7 +65873,14 @@ ${JSON.stringify(data, null, 2)}`, start2);
65689
65873
  }
65690
65874
  return fail(`viewport: provide "WxH" (e.g. "375x667") or a Playwright device name (e.g. "iPhone 13")`, start2);
65691
65875
  }
65692
- case "observe_bundle": {
65876
+ case "validate_lifecycle": {
65877
+ if (!url)
65878
+ return fail("url is required", start2);
65879
+ try {
65880
+ await validateNetworkEgressUrl(url, { allowLoopback: true });
65881
+ } catch (err2) {
65882
+ return fail(networkEgressErrorMessage(err2), start2);
65883
+ }
65693
65884
  if (width || height) {
65694
65885
  const current = page.viewportSize?.() ?? {
65695
65886
  width: 1280,
@@ -65700,87 +65891,107 @@ ${JSON.stringify(data, null, 2)}`, start2);
65700
65891
  height: height ?? current.height ?? 720
65701
65892
  });
65702
65893
  }
65703
- const html = await page.content();
65704
- const readableText = await page.evaluate(`(() => {
65705
- const clone = document.body ? document.body.cloneNode(true) : document.documentElement.cloneNode(true);
65706
- clone.querySelectorAll("script, style, noscript, svg, path").forEach(el => el.remove());
65707
- return clone.innerText || clone.textContent || "";
65708
- })()`);
65709
- const summary = downsampleDom(html);
65710
- lastDomSummarySelectors = extractDomSummarySelectors(summary);
65711
- const screenshotPath = saveBuffer(outputPath3 || defaultArtifactPath("observe", "png"), await page.screenshot({ fullPage: args.full_page === true }));
65712
- const viewport = page.viewportSize?.() ?? { width: 0, height: 0 };
65713
- const gate = classifyBrowserGate({
65714
- url: page.url(),
65715
- html,
65716
- text: String(readableText ?? ""),
65717
- network: networkBuffer,
65718
- errors: errorBuffer
65894
+ clearDiagnosticBuffers();
65895
+ const cursor = captureBrowserDiagnosticCursor();
65896
+ await page.goto(url, {
65897
+ waitUntil: "domcontentloaded",
65898
+ timeout: timeout2
65719
65899
  });
65720
- const errors = errorBuffer.slice(-20).map((entry) => entry.message);
65721
- const networkFailures = networkBuffer.filter((entry) => entry.ok === false || (entry.status ?? 0) >= 400).slice(-30).map((entry) => `${entry.method} ${entry.status ?? "FAILED"} ${entry.url}${entry.failure ? ` (${entry.failure})` : ""}`);
65722
- const bundle = {
65723
- id: timestampSlug(),
65724
- engine: "playwright",
65725
- mode: browserHeadless === false ? "headed" : "headless",
65726
- url: page.url(),
65727
- title: await page.title(),
65728
- viewport,
65729
- screenshotPath,
65730
- gate,
65731
- consoleErrors: consoleBuffer.filter((entry) => /error|warning/i.test(entry.type)).slice(-20),
65732
- networkFailures,
65733
- pageErrors: errors,
65734
- domSummary: summary.slice(0, 12e3)
65735
- };
65736
- const evidenceEvent = createEvidenceEvent({
65737
- tool: "playwright_browser",
65900
+ let expectationSatisfied;
65901
+ let expectationError;
65902
+ const expectationProbe = expectation ? waitForBrowserExpectation(page, expectation, Math.min(expectationTimeout, Math.max(1, observationMs))).then(() => {
65903
+ expectationSatisfied = true;
65904
+ }).catch((err2) => {
65905
+ expectationSatisfied = false;
65906
+ expectationError = err2 instanceof Error ? err2.message : String(err2);
65907
+ }) : Promise.resolve();
65908
+ await Promise.all([
65909
+ page.waitForTimeout(observationMs),
65910
+ expectationProbe
65911
+ ]);
65912
+ if (expectation && expectationSatisfied === true && !await browserExpectationIsVisible(page, expectation)) {
65913
+ expectationSatisfied = false;
65914
+ expectationError = "The declared postcondition was visible during the window but was not visible at the end of it.";
65915
+ }
65916
+ return captureBrowserObservation({
65917
+ pageHandle: page,
65918
+ action: "validate_lifecycle",
65919
+ startMs: start2,
65920
+ outputPath: outputPath3,
65921
+ fullPage: args.full_page === true,
65922
+ cursor,
65923
+ requestedObservationMs: observationMs,
65924
+ expectation,
65925
+ expectationSatisfied,
65926
+ expectationError,
65927
+ failOnRuntimeErrors: true
65928
+ });
65929
+ }
65930
+ case "observe_window": {
65931
+ if (startDelayMs > 0)
65932
+ await page.waitForTimeout(startDelayMs);
65933
+ if (width || height) {
65934
+ const current = page.viewportSize?.() ?? {
65935
+ width: 1280,
65936
+ height: 720
65937
+ };
65938
+ await page.setViewportSize({
65939
+ width: width ?? current.width ?? 1280,
65940
+ height: height ?? current.height ?? 720
65941
+ });
65942
+ }
65943
+ const cursor = captureBrowserDiagnosticCursor();
65944
+ let expectationSatisfied;
65945
+ let expectationError;
65946
+ const expectationProbe = expectation ? waitForBrowserExpectation(page, expectation, Math.min(expectationTimeout, Math.max(1, observationMs))).then(() => {
65947
+ expectationSatisfied = true;
65948
+ }).catch((err2) => {
65949
+ expectationSatisfied = false;
65950
+ expectationError = err2 instanceof Error ? err2.message : String(err2);
65951
+ }) : Promise.resolve();
65952
+ await Promise.all([
65953
+ page.waitForTimeout(observationMs),
65954
+ expectationProbe
65955
+ ]);
65956
+ if (expectation && expectationSatisfied === true && !await browserExpectationIsVisible(page, expectation)) {
65957
+ expectationSatisfied = false;
65958
+ expectationError = "The declared postcondition was visible during the window but was not visible at the end of it.";
65959
+ }
65960
+ return captureBrowserObservation({
65961
+ pageHandle: page,
65962
+ action: "observe_window",
65963
+ startMs: start2,
65964
+ outputPath: outputPath3,
65965
+ fullPage: args.full_page === true,
65966
+ cursor,
65967
+ requestedObservationMs: observationMs,
65968
+ expectation,
65969
+ expectationSatisfied,
65970
+ expectationError,
65971
+ failOnRuntimeErrors: true
65972
+ });
65973
+ }
65974
+ case "observe_bundle": {
65975
+ if (width || height) {
65976
+ const current = page.viewportSize?.() ?? {
65977
+ width: 1280,
65978
+ height: 720
65979
+ };
65980
+ await page.setViewportSize({
65981
+ width: width ?? current.width ?? 1280,
65982
+ height: height ?? current.height ?? 720
65983
+ });
65984
+ }
65985
+ return captureBrowserObservation({
65986
+ pageHandle: page,
65738
65987
  action: "observe_bundle",
65739
- surface: "browser",
65740
- after: createEvidenceObservation({
65741
- screenshotPath,
65742
- width: viewport.width,
65743
- height: viewport.height,
65744
- url: bundle.url,
65745
- title: bundle.title,
65746
- domSummaryHash: sha256Text(summary),
65747
- readableTextHash: sha256Text(String(readableText ?? "")),
65748
- visibleTextExcerpt: clipEvidenceText(readableText, 500),
65749
- consoleErrorCount: bundle.consoleErrors.length,
65750
- pageErrorCount: errors.length,
65751
- networkFailureCount: networkFailures.length,
65752
- gateKind: gate.kind,
65753
- gateConfidence: gate.confidence,
65754
- gateEvidence: gate.evidence,
65755
- metadata: { mode: bundle.mode, engine: "playwright" }
65756
- }),
65757
- verification: {
65758
- status: gate.kind === "captcha" || gate.kind === "bot_challenge" ? "blocked" : "inconclusive",
65759
- confidence: gate.kind === "captcha" || gate.kind === "bot_challenge" ? gate.confidence : 0.5,
65760
- reason: gate.kind === "none" ? "Fresh browser observation captured; no specific completion claim was evaluated." : `Fresh browser observation captured with gate=${gate.kind}.`
65761
- }
65988
+ startMs: start2,
65989
+ outputPath: outputPath3,
65990
+ fullPage: args.full_page === true,
65991
+ requestedObservationMs: 0,
65992
+ expectation,
65993
+ failOnRuntimeErrors: false
65762
65994
  });
65763
- const modelBundle = { ...bundle, evidenceEvents: [evidenceEvent] };
65764
- return {
65765
- success: true,
65766
- output: [
65767
- "Browser observation bundle",
65768
- `URL: ${bundle.url}`,
65769
- `Title: ${bundle.title}`,
65770
- `Mode: ${bundle.mode}`,
65771
- `Viewport: ${viewport.width}x${viewport.height}`,
65772
- `Screenshot: ${screenshotPath}`,
65773
- `Gate: ${gate.kind} (${gate.confidence.toFixed(2)})${gate.evidence.length ? ` — ${gate.evidence.join("; ")}` : ""}`,
65774
- `Console warnings/errors: ${bundle.consoleErrors.length}`,
65775
- `Network failures: ${networkFailures.length}`,
65776
- "",
65777
- "--- DOM Summary ---",
65778
- summary.slice(0, 6e3)
65779
- ].join("\n"),
65780
- llmContent: JSON.stringify(modelBundle, null, 2),
65781
- evidenceEvents: [evidenceEvent],
65782
- durationMs: Date.now() - start2
65783
- };
65784
65995
  }
65785
65996
  case "visual_click": {
65786
65997
  if (!visualTarget)
@@ -66068,7 +66279,9 @@ ${JSON.stringify(data, null, 2)}`, start2);
66068
66279
  ].filter(Boolean).join("\n"),
66069
66280
  llmContent: JSON.stringify(bundle, null, 2),
66070
66281
  evidenceEvents: [evidenceEvent],
66071
- ...expectationSatisfied === false ? { error: `Postcondition not observed: ${expectation}. ${expectationError?.slice(0, 500) ?? ""}`.trim() } : {},
66282
+ ...expectationSatisfied === false ? {
66283
+ error: `Postcondition not observed: ${expectation}. ${expectationError?.slice(0, 500) ?? ""}`.trim()
66284
+ } : {},
66072
66285
  durationMs: Date.now() - start2
66073
66286
  };
66074
66287
  }
@@ -659396,13 +659609,13 @@ ${context2 ?? ""}`;
659396
659609
  critique2 = this._buildVisualAdversaryCritique({
659397
659610
  evidence: `${inconclusiveEvent.tool}/${inconclusiveEvent.action} produced only inconclusive proof: ${inconclusiveEvent.verification?.reason ?? "no reason"}`,
659398
659611
  hypothesis: "The browser action may have worked, but the current evidence is not strong enough to prove the user-visible event.",
659399
- correctiveAction: 'Run playwright_browser({action:"observe_bundle"}) after the action and verify URL, visible text, DOM, console, network, and gate state.'
659612
+ correctiveAction: 'Run playwright_browser({action:"observe_window", observation_ms:5000, expect:"..."}) after the action. Verify the expected state plus window-scoped console, page, network, DOM, and screenshot evidence.'
659400
659613
  });
659401
659614
  } else if (events.length === 0) {
659402
659615
  critique2 = this._buildVisualAdversaryCritique({
659403
659616
  evidence: `${toolName} changed ${surface} state but did not return a structured evidence event.`,
659404
659617
  hypothesis: "The agent may be acting from stale visual state after a click/type/submit/navigation action.",
659405
- correctiveAction: surface === "browser" ? 'Run playwright_browser({action:"observe_bundle"}) or a post-action screenshot/DOM check before claiming success.' : 'Run vision_action_loop({action:"observe", goal:"..."}) or desktop_describe before claiming success.'
659618
+ correctiveAction: surface === "browser" ? 'Run playwright_browser({action:"observe_window", observation_ms:5000, expect:"..."}) before claiming success. Use a fresh headed validate_lifecycle run when the claim concerns GUI, GPU, WebGL, WebGPU, canvas, or animation behavior.' : 'Run vision_action_loop({action:"observe", goal:"..."}) or desktop_describe before claiming success.'
659406
659619
  });
659407
659620
  }
659408
659621
  if (critique2) {
@@ -678995,6 +679208,9 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
678995
679208
  "file_explore",
678996
679209
  "web_search",
678997
679210
  "web_fetch",
679211
+ // Browser evidence must be available without a lexical relevance guess.
679212
+ // Surfaces that do not register this tool do not expose it.
679213
+ "playwright_browser",
678998
679214
  "memory_read",
678999
679215
  "memory_write",
679000
679216
  "working_notes",
@@ -679112,11 +679328,12 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
679112
679328
  const compressDesc = true;
679113
679329
  const defs = dedupedInline.map((tool) => {
679114
679330
  const desc = getDesc(tool);
679331
+ const modelFacingDescription = tool.modelFacingSummary?.trim();
679115
679332
  return {
679116
679333
  type: "function",
679117
679334
  function: {
679118
679335
  name: tool.name,
679119
- description: compressDesc ? (desc.split(/\.\s/)[0]?.slice(0, 120) ?? desc.slice(0, 120)) + "." : desc,
679336
+ description: modelFacingDescription ? modelFacingDescription.slice(0, 420) : compressDesc ? (desc.split(/\.\s/)[0]?.slice(0, 120) ?? desc.slice(0, 120)) + "." : desc,
679120
679337
  parameters: this._publicToolParameters(tool.parameters)
679121
679338
  }
679122
679339
  };
@@ -686617,11 +686834,11 @@ var init_workerSkillStandard = __esm({
686617
686834
  {
686618
686835
  stepNumber: 3,
686619
686836
  title: "Validate every requested browser flow in one Playwright session",
686620
- description: "Use playwright_browser navigate -> observe_bundle -> action -> state-based postcondition -> observe_bundle. Prefer data-testid or role/name locators. A click receipt or screenshot alone is not a pass. Re-query after a detached-element error; record each requested interaction as passed, failed, blocked, not_applicable, or inconclusive with the concrete state and diagnostics.",
686837
+ description: "Use playwright_browser validate_lifecycle -> action -> state-based postcondition -> observe_window. Set a finite observation_ms so delayed errors can surface. For GUI, GPU, WebGL, WebGPU, canvas, or animation claims, use a fresh headed browser. A clean headless load is not headed rendering evidence. Prefer data-testid or role/name locators. A click receipt or screenshot alone is not a pass. Re-query after a detached-element error; record each requested interaction as passed, failed, blocked, not_applicable, or inconclusive with the concrete state and diagnostics.",
686621
686838
  tools: ["playwright_browser"],
686622
686839
  skills: ["browser-interaction-validation"],
686623
686840
  output: "One claim-scoped browser evidence entry per requested interaction, separate from static test evidence",
686624
- verification: "Every interaction has an observed postcondition or a precise failed/blocked/not-applicable outcome; page errors, console errors, and failed network requests are reviewed"
686841
+ verification: "Every interaction has an observed postcondition or a precise failed/blocked/not-applicable outcome; delayed page errors, console errors, failed network requests, DOM state, and screenshots are reviewed from the relevant observation window"
686625
686842
  },
686626
686843
  {
686627
686844
  stepNumber: 4,
@@ -692429,10 +692646,12 @@ FOR SCREEN / DESKTOP UI (what is on this computer display, clicking, navigating)
692429
692646
  desktop_click({ target: "element" }) - Click a UI element by description.
692430
692647
  vision_action_loop({ goal: "task", action: "observe" }) - Persistent screenshot/OCR/point/action loop with previewable screenshots.
692431
692648
 
692432
- FOR BROWSER PAGE / WEB GUI RENDERS (headless, deterministic viewport):
692649
+ FOR BROWSER PAGE / WEB GUI RENDERS:
692433
692650
  browser_action({ action: "screenshot", width: 1280, height: 720, output_path: ".omnius/browser/page.png" }) - Render the current headless browser page and return an image input.
692434
692651
  playwright_browser({ action: "viewport", text: "1280x720" }) then playwright_browser({ action: "screenshot", value: ".omnius/browser/page.png" }) - Playwright diagnostic browser capture.
692435
- playwright_browser({ action: "observe_bundle" }) - Capture screenshot + DOM summary + diagnostics + gate assessment in one browser observation.
692652
+ playwright_browser({ action: "validate_lifecycle", url: "http://127.0.0.1:3000", headless: false, force_new: true, observation_ms: 8000, expect: "text=Ready" }) - Start diagnostics before navigation and hold a fresh GUI browser open for delayed rendering failures.
692653
+ playwright_browser({ action: "observe_window", observation_ms: 5000, expect: "text=Updated" }) - Keep the current page alive and return diagnostics emitted during the post-action window.
692654
+ playwright_browser({ action: "observe_bundle" }) - Capture current session diagnostics and page state when a timed window is not required.
692436
692655
  playwright_browser({ action: "visual_click", target: "button or field", vision_model: "moondream" }) - Browser screenshot -> Moondream point -> DOM candidate -> click -> post-action screenshot.
692437
692656
 
692438
692657
  RULES:
@@ -692441,6 +692660,7 @@ RULES:
692441
692660
  - For seeing the screen, use screenshot, desktop_describe, or vision_action_loop, not camera_capture.
692442
692661
  - For image files, use image_read or vision, not desktop_describe.
692443
692662
  - For browser GUI action when selectors are unknown, use playwright_browser visual_click before desktop-level clicking.
692663
+ - Match the browser mode to the claim. A clean immediate or headless run does not verify delayed headed GPU/WebGL behavior.
692444
692664
  - Do not claim you cannot see visual content until the relevant visual tool has failed.
692445
692665
  </visual-and-sensor-tools>`;
692446
692666
  }
@@ -701137,6 +701357,7 @@ function adaptExecutionTool(tool, options2 = {}) {
701137
701357
  name: tool.name,
701138
701358
  aliases: tool.aliases,
701139
701359
  description: tool.description,
701360
+ modelFacingSummary: tool.modelFacingSummary,
701140
701361
  parameters: tool.parameters,
701141
701362
  inputSchema: tool.inputSchema,
701142
701363
  maxResultSizeChars: tool.maxResultSizeChars,
@@ -773624,6 +773845,157 @@ function codeBlock(code8, language = "") {
773624
773845
  const inner = escapeHtml2(String(code8 ?? "").replace(/\s+$/, ""));
773625
773846
  return language ? `<pre><code class="language-${escapeHtml2(language)}">${inner}</code></pre>` : `<pre>${inner}</pre>`;
773626
773847
  }
773848
+ function telegramRichText(text3) {
773849
+ return escapeHtml2(text3).replace(/\r?\n/g, "<br>");
773850
+ }
773851
+ function renderTelegramPanelPage(input) {
773852
+ const metadata = (input.metadata ?? []).map((item) => ({
773853
+ label: String(item.label ?? "").trim(),
773854
+ value: String(item.value ?? "").trim()
773855
+ })).filter((item) => item.label && item.value);
773856
+ const sections = input.sections.map((section) => ({
773857
+ title: section.title?.trim() || "",
773858
+ packets: section.packets.map((packet) => ({
773859
+ title: packet.title.trim() || "Details",
773860
+ body: packet.body.trim(),
773861
+ open: packet.open === true
773862
+ })).filter((packet) => packet.body)
773863
+ })).filter((section) => section.packets.length > 0);
773864
+ const emptyText = input.emptyText?.trim() || "No data is available yet.";
773865
+ const fallback = [`<b>${escapeHtml2(input.title)}</b>`];
773866
+ if (input.subtitle?.trim()) {
773867
+ fallback.push(`<i>${escapeHtml2(input.subtitle.trim())}</i>`);
773868
+ }
773869
+ if (metadata.length > 0) {
773870
+ fallback.push(
773871
+ "<b>Details</b>",
773872
+ `<blockquote>${escapeHtml2(metadata.map((item) => `${item.label}: ${item.value}`).join("\n"))}</blockquote>`
773873
+ );
773874
+ }
773875
+ if (sections.length === 0) {
773876
+ fallback.push(`<i>${escapeHtml2(emptyText)}</i>`);
773877
+ } else {
773878
+ for (const section of sections) {
773879
+ if (section.title) fallback.push(`<b>${escapeHtml2(section.title)}</b>`);
773880
+ for (const packet of section.packets) {
773881
+ const expandable = !packet.open && (packet.body.length > 180 || packet.body.split(/\r?\n/).length > 3);
773882
+ fallback.push(
773883
+ `<b>${escapeHtml2(packet.title)}</b>`,
773884
+ `<blockquote${expandable ? " expandable" : ""}>${escapeHtml2(packet.body)}</blockquote>`
773885
+ );
773886
+ }
773887
+ }
773888
+ }
773889
+ if (input.footer?.trim()) {
773890
+ fallback.push(`<i>${escapeHtml2(input.footer.trim())}</i>`);
773891
+ }
773892
+ const rich = [`<h3>${escapeHtml2(input.title)}</h3>`];
773893
+ if (input.subtitle?.trim()) {
773894
+ rich.push(`<p><i>${telegramRichText(input.subtitle.trim())}</i></p>`);
773895
+ }
773896
+ if (metadata.length > 0) {
773897
+ rich.push(
773898
+ [
773899
+ "<table bordered striped compact>",
773900
+ "<tr><th>Field</th><th>Value</th></tr>",
773901
+ ...metadata.map(
773902
+ (item) => `<tr><td>${telegramRichText(item.label)}</td><td>${telegramRichText(item.value)}</td></tr>`
773903
+ ),
773904
+ "</table>"
773905
+ ].join("")
773906
+ );
773907
+ }
773908
+ if (sections.length === 0) {
773909
+ rich.push(`<p><i>${telegramRichText(emptyText)}</i></p>`);
773910
+ } else {
773911
+ for (const section of sections) {
773912
+ if (section.title) rich.push(`<h4>${escapeHtml2(section.title)}</h4>`);
773913
+ for (const packet of section.packets) {
773914
+ rich.push(
773915
+ `<details${packet.open ? " open" : ""}><summary>${escapeHtml2(packet.title)}</summary><p>${telegramRichText(packet.body)}</p></details>`
773916
+ );
773917
+ }
773918
+ }
773919
+ }
773920
+ if (input.footer?.trim()) {
773921
+ rich.push(`<footer>${telegramRichText(input.footer.trim())}</footer>`);
773922
+ }
773923
+ return { html: fallback.join("\n\n"), richHtml: rich.join("\n") };
773924
+ }
773925
+ function telegramTitledTextPackets(text3, fallbackTitle = "Details", maxPackets = 16) {
773926
+ const source = String(text3 ?? "").trim();
773927
+ if (!source) return [];
773928
+ const packets = [];
773929
+ let currentTitle = "";
773930
+ let currentLines = [];
773931
+ const flush3 = () => {
773932
+ const body = currentLines.join("\n").trim();
773933
+ if (body) {
773934
+ packets.push({
773935
+ title: currentTitle || fallbackTitle,
773936
+ body
773937
+ });
773938
+ }
773939
+ currentTitle = "";
773940
+ currentLines = [];
773941
+ };
773942
+ for (const line of source.split(/\r?\n/)) {
773943
+ const heading = line.match(/^([^:\n]{1,100}):\s*$/);
773944
+ if (heading) {
773945
+ flush3();
773946
+ currentTitle = heading[1].trim();
773947
+ } else {
773948
+ currentLines.push(line);
773949
+ }
773950
+ }
773951
+ flush3();
773952
+ if (packets.length <= 1 && !/^([^:\n]{1,100}):\s*$/m.test(source)) {
773953
+ const paragraphs = source.split(/\r?\n\s*\r?\n+/).map((paragraph) => paragraph.trim()).filter(Boolean);
773954
+ packets.length = 0;
773955
+ packets.push(
773956
+ ...paragraphs.map((paragraph, index) => ({
773957
+ title: paragraphs.length === 1 ? fallbackTitle : `${fallbackTitle} ${index + 1}`,
773958
+ body: paragraph
773959
+ }))
773960
+ );
773961
+ }
773962
+ if (packets.length <= maxPackets) return packets;
773963
+ const retained = packets.slice(0, Math.max(1, maxPackets - 1));
773964
+ retained.push({
773965
+ title: `${fallbackTitle} ${maxPackets}`,
773966
+ body: packets.slice(Math.max(1, maxPackets - 1)).map((packet) => `${packet.title}
773967
+ ${packet.body}`).join("\n\n")
773968
+ });
773969
+ return retained;
773970
+ }
773971
+ function splitTelegramPlainText(text3, maxLength = 2500) {
773972
+ const chunks = [];
773973
+ let remaining = String(text3 ?? "").trim();
773974
+ const limit2 = Math.max(200, Math.floor(maxLength));
773975
+ while (remaining.length > limit2) {
773976
+ let cut = remaining.lastIndexOf("\n\n", limit2);
773977
+ if (cut < limit2 * 0.4) cut = remaining.lastIndexOf("\n", limit2);
773978
+ if (cut < limit2 * 0.4) cut = remaining.lastIndexOf(". ", limit2);
773979
+ if (cut < limit2 * 0.4) cut = remaining.lastIndexOf(" ", limit2);
773980
+ if (cut < limit2 * 0.4) cut = limit2;
773981
+ const chunk = remaining.slice(0, cut).trim();
773982
+ if (chunk) chunks.push(chunk);
773983
+ remaining = remaining.slice(cut).trim();
773984
+ }
773985
+ if (remaining || chunks.length === 0) chunks.push(remaining);
773986
+ return chunks;
773987
+ }
773988
+ function telegramHtmlToPlainText(html) {
773989
+ return String(html ?? "").replace(/<br\s*\/?>/gi, "\n").replace(
773990
+ /<\/(?:p|h[1-6]|blockquote|details|summary|table|tr|ul|ol|li|footer)>/gi,
773991
+ "\n"
773992
+ ).replace(/<hr\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&#(x[0-9a-f]+|\d+);/gi, (_match, value2) => {
773993
+ const radix = value2[0]?.toLowerCase() === "x" ? 16 : 10;
773994
+ const raw = radix === 16 ? value2.slice(1) : value2;
773995
+ const codePoint = Number.parseInt(raw, radix);
773996
+ return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : "";
773997
+ }).replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/\n[ \t]+/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
773998
+ }
773627
773999
  function safeJson(v, pretty) {
773628
774000
  try {
773629
774001
  return pretty ? JSON.stringify(v, null, 2) : JSON.stringify(v);
@@ -773637,11 +774009,14 @@ function tokenizeTelegramHtml(html) {
773637
774009
  let last2 = 0;
773638
774010
  let m2;
773639
774011
  while ((m2 = re.exec(html)) !== null) {
773640
- if (m2.index > last2) tokens3.push({ type: "text", raw: html.slice(last2, m2.index) });
774012
+ if (m2.index > last2)
774013
+ tokens3.push({ type: "text", raw: html.slice(last2, m2.index) });
773641
774014
  const closing = m2[1] === "/";
773642
774015
  const tag = (m2[2] || "").toLowerCase();
773643
774016
  if (SPAN_TAGS.has(tag)) {
773644
- tokens3.push(closing ? { type: "close", tag, raw: m2[0] } : { type: "open", tag, raw: m2[0] });
774017
+ tokens3.push(
774018
+ closing ? { type: "close", tag, raw: m2[0] } : { type: "open", tag, raw: m2[0] }
774019
+ );
773645
774020
  } else {
773646
774021
  tokens3.push({ type: "text", raw: m2[0] });
773647
774022
  }
@@ -781284,6 +781659,30 @@ function parseTelegramSilentReflectionNotes(text3) {
781284
781659
  }
781285
781660
  return null;
781286
781661
  }
781662
+ function telegramInteractionDecisionResponseFormatForPacket(packet) {
781663
+ const format3 = structuredClone(
781664
+ TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
781665
+ );
781666
+ const properties = format3.json_schema.schema.properties;
781667
+ properties.input_id = {
781668
+ type: "string",
781669
+ enum: [packet.inputId]
781670
+ };
781671
+ properties.addressed_actor_keys = {
781672
+ type: "array",
781673
+ items: { type: "string", enum: packet.actorKeys },
781674
+ maxItems: 12
781675
+ };
781676
+ properties.evidence_ids = {
781677
+ type: "array",
781678
+ items: {
781679
+ type: "string",
781680
+ enum: packet.evidence.map((entry) => entry.id)
781681
+ },
781682
+ maxItems: 16
781683
+ };
781684
+ return format3;
781685
+ }
781287
781686
  function extractPartialTelegramReplyJson(buffer2) {
781288
781687
  const stripped = stripTelegramHiddenThinking(buffer2).trimStart();
781289
781688
  if (!stripped.startsWith("{")) {
@@ -781818,10 +782217,7 @@ function formatTelegramSteeringReconciliationHTML(values) {
781818
782217
  if (values.length === 0) return "";
781819
782218
  const sections = values.slice(-4).map((value2) => {
781820
782219
  const status = value2.status.replaceAll("_", " ");
781821
- const constraints = value2.changedConstraints.length ? [
781822
- "Changes:",
781823
- ...value2.changedConstraints.map((item) => `• ${item}`)
781824
- ] : ["Changes: none"];
782220
+ const constraints = value2.changedConstraints.length ? ["Changes:", ...value2.changedConstraints.map((item) => `• ${item}`)] : ["Changes: none"];
781825
782221
  return [
781826
782222
  `Status: ${status}`,
781827
782223
  ...constraints,
@@ -782395,11 +782791,78 @@ function telegramAdminLivePanelPageLabel(page2) {
782395
782791
  if (page2 === "evidence") return "Evidence";
782396
782792
  return "Context";
782397
782793
  }
782794
+ function telegramPanelHtmlFallback(html) {
782795
+ const chunks = segmentTelegramHtml(html, 3600);
782796
+ if (chunks.length <= 1) return chunks[0] ?? "";
782797
+ return `${chunks[0]}
782798
+
782799
+ <i>... (additional packet detail requires Rich Message support)</i>`;
782800
+ }
782801
+ function telegramAdminLinePackets(lines, fallbackTitle) {
782802
+ return lines.map((line, index) => {
782803
+ const structured = line.match(
782804
+ /^([→✓✗])\s+([^\s:]+)(?::\s*|\s+)?([\s\S]*)$/
782805
+ );
782806
+ if (!structured) {
782807
+ return { title: `${fallbackTitle} ${index + 1}`, body: line };
782808
+ }
782809
+ const marker = structured[1];
782810
+ const toolName = structured[2] || "tool";
782811
+ const detail = structured[3]?.trim();
782812
+ const phase = marker === "→" ? "Call" : marker === "✓" ? "Result" : "Failure";
782813
+ return {
782814
+ title: `${phase} · ${toolName}`,
782815
+ body: detail || (marker === "→" ? "Request admitted." : marker === "✓" ? "Completed." : "The tool reported a failure.")
782816
+ };
782817
+ });
782818
+ }
782819
+ function telegramContextMetadata(page2, pageIndex, pageCount) {
782820
+ return [
782821
+ { label: "Packet", value: `${pageIndex + 1} / ${pageCount}` },
782822
+ { label: "Source", value: page2.source },
782823
+ ...page2.metadata.map((line, index) => {
782824
+ const equalsAt = line.indexOf("=");
782825
+ return equalsAt > 0 ? {
782826
+ label: line.slice(0, equalsAt).trim(),
782827
+ value: line.slice(equalsAt + 1).trim()
782828
+ } : { label: `Detail ${index + 1}`, value: line };
782829
+ })
782830
+ ];
782831
+ }
782832
+ function renderTelegramContextExplorerPagePayload(page2, pageIndex, pageCount, title = "Context used for this reply") {
782833
+ if (!page2) {
782834
+ const empty2 = renderTelegramPanelPage({
782835
+ title,
782836
+ sections: [],
782837
+ emptyText: "No context packets are available."
782838
+ });
782839
+ return { text: empty2.html, rich_html: empty2.richHtml };
782840
+ }
782841
+ const rendered = renderTelegramPanelPage({
782842
+ title,
782843
+ subtitle: page2.title,
782844
+ metadata: telegramContextMetadata(page2, pageIndex, pageCount),
782845
+ sections: [
782846
+ {
782847
+ packets: telegramTitledTextPackets(page2.body, "Content")
782848
+ }
782849
+ ],
782850
+ emptyText: "This context packet is empty.",
782851
+ footer: `Context packet ${pageIndex + 1} of ${pageCount}`
782852
+ });
782853
+ return {
782854
+ text: telegramPanelHtmlFallback(rendered.html),
782855
+ rich_html: rendered.richHtml
782856
+ };
782857
+ }
782398
782858
  function trimTelegramAdminPanelLines(lines, max = 80) {
782399
782859
  if (lines.length > max) lines.splice(0, lines.length - max);
782400
782860
  }
782401
782861
  function pushTelegramAdminPanelLine(state5, page2, line) {
782402
- const clean7 = stripTelegramHiddenThinking(line).replace(/\s+/g, " ").trim();
782862
+ const clean7 = sanitizeTelegramProgressText(
782863
+ stripTelegramHiddenThinking(line).replace(/\s+/g, " ").trim(),
782864
+ 1e3
782865
+ );
782403
782866
  if (!clean7) return;
782404
782867
  const target = page2 === "tools" ? state5.toolLines : page2 === "logs" ? state5.logLines : state5.mutationLines;
782405
782868
  if (target[target.length - 1] !== clean7) target.push(clean7);
@@ -782408,7 +782871,10 @@ function pushTelegramAdminPanelLine(state5, page2, line) {
782408
782871
  if (state5.activePage !== page2) state5.dirtyPages[page2] = true;
782409
782872
  }
782410
782873
  function pushTelegramAdminPanelMutationLine(state5, line) {
782411
- const clean7 = stripTelegramHiddenThinking(line).replace(/\s+/g, " ").trim();
782874
+ const clean7 = sanitizeTelegramProgressText(
782875
+ stripTelegramHiddenThinking(line).replace(/\s+/g, " ").trim(),
782876
+ 1e3
782877
+ );
782412
782878
  if (!clean7) return;
782413
782879
  if (state5.mutationLines[state5.mutationLines.length - 1] !== clean7)
782414
782880
  state5.mutationLines.push(clean7);
@@ -782428,6 +782894,37 @@ function telegramAdminLivePanelIntakeText(requestPreview) {
782428
782894
  void requestPreview;
782429
782895
  return "";
782430
782896
  }
782897
+ function telegramAdminEventTurn(event) {
782898
+ const turn = event.turn;
782899
+ return typeof turn === "number" && Number.isFinite(turn) ? turn : void 0;
782900
+ }
782901
+ function syncTelegramAdminResponseText(state5) {
782902
+ state5.responseText = state5.responseSegments.map(
782903
+ (segment) => cleanTelegramVisibleReply(segment.rawText, {
782904
+ suppressPotentialNoReplyPrefix: true
782905
+ })
782906
+ ).filter(Boolean).join("\n\n");
782907
+ }
782908
+ function upsertTelegramAdminResponseSegment(state5, key, turn, rawText, complete) {
782909
+ const existing = state5.responseSegments.find(
782910
+ (segment) => segment.key === key
782911
+ );
782912
+ if (existing) {
782913
+ existing.rawText = rawText;
782914
+ existing.complete = complete;
782915
+ } else {
782916
+ state5.responseSegments.push({
782917
+ key,
782918
+ ...turn !== void 0 ? { turn } : {},
782919
+ rawText,
782920
+ complete
782921
+ });
782922
+ }
782923
+ if (state5.responseSegments.length > 8) {
782924
+ state5.responseSegments.splice(0, state5.responseSegments.length - 8);
782925
+ }
782926
+ syncTelegramAdminResponseText(state5);
782927
+ }
782431
782928
  function renderTelegramHalftoneProgressBar(elapsedSec, width = 12) {
782432
782929
  const safeWidth = Math.max(1, Math.floor(width));
782433
782930
  const phase = Math.max(0, elapsedSec) % 60 / 60 * safeWidth;
@@ -782460,6 +782957,8 @@ function createTelegramAdminLivePanelState(args) {
782460
782957
  requestPreview,
782461
782958
  intakeText: telegramAdminLivePanelIntakeText(requestPreview),
782462
782959
  responseText: "",
782960
+ responseSegments: [],
782961
+ responseSegmentSequence: 0,
782463
782962
  steeringReconciliations: [],
782464
782963
  evidenceText: "",
782465
782964
  toolLines: [],
@@ -782470,24 +782969,40 @@ function createTelegramAdminLivePanelState(args) {
782470
782969
  };
782471
782970
  }
782472
782971
  function applyTelegramAdminLivePanelEvent(state5, event) {
782972
+ if (event.type === "stream_start") {
782973
+ const turn = telegramAdminEventTurn(event);
782974
+ const key = turn !== void 0 ? `turn:${turn}` : `stream:${++state5.responseSegmentSequence}`;
782975
+ upsertTelegramAdminResponseSegment(state5, key, turn, "", false);
782976
+ state5.activeResponseSegmentKey = key;
782977
+ return;
782978
+ }
782473
782979
  if (event.type === "stream_token" && event.streamKind === "content" && event.content) {
782474
- const next = cleanTelegramVisibleReply(
782475
- `${state5.responseText}${event.content}`,
782476
- { suppressPotentialNoReplyPrefix: true }
782980
+ const turn = telegramAdminEventTurn(event);
782981
+ const key = state5.activeResponseSegmentKey ?? (turn !== void 0 ? `turn:${turn}` : `stream:${++state5.responseSegmentSequence}`);
782982
+ const existing = state5.responseSegments.find(
782983
+ (segment) => segment.key === key
782984
+ );
782985
+ upsertTelegramAdminResponseSegment(
782986
+ state5,
782987
+ key,
782988
+ turn,
782989
+ `${existing?.rawText ?? ""}${event.content}`,
782990
+ false
782477
782991
  );
782478
- if (next) {
782479
- state5.responseText = next;
782992
+ if (state5.responseText) {
782480
782993
  state5.updatedAt = Date.now();
782481
782994
  if (state5.activePage !== "response") state5.dirtyPages.response = true;
782482
782995
  }
782483
782996
  return;
782484
782997
  }
782485
782998
  if ((event.type === "assistant_text" || event.type === "stream_end") && event.content) {
782486
- const next = cleanTelegramVisibleReply(event.content, {
782487
- suppressPotentialNoReplyPrefix: true
782488
- });
782489
- if (next) {
782490
- state5.responseText = next;
782999
+ const turn = telegramAdminEventTurn(event);
783000
+ const key = turn !== void 0 ? `turn:${turn}` : state5.activeResponseSegmentKey ?? `event:${++state5.responseSegmentSequence}`;
783001
+ upsertTelegramAdminResponseSegment(state5, key, turn, event.content, true);
783002
+ if (state5.activeResponseSegmentKey === key) {
783003
+ state5.activeResponseSegmentKey = void 0;
783004
+ }
783005
+ if (state5.responseText) {
782491
783006
  state5.updatedAt = Date.now();
782492
783007
  if (state5.activePage !== "response") state5.dirtyPages.response = true;
782493
783008
  }
@@ -782530,12 +783045,12 @@ function applyTelegramAdminLivePanelEvent(state5, event) {
782530
783045
  }
782531
783046
  }
782532
783047
  function renderTelegramAdminLivePanelPayload(state5) {
782533
- state5.dirtyPages[state5.activePage] = false;
782534
- const header = [
782535
- `<b>Admin run:</b> <code>${escapeTelegramHTML(state5.status)}</code>`,
782536
- state5.requestPreview ? `<i>${escapeTelegramHTML(state5.requestPreview)}</i>` : ""
782537
- ].filter(Boolean).join("\n");
783048
+ const metadata = [
783049
+ { label: "Run status", value: state5.status },
783050
+ ...state5.requestPreview ? [{ label: "Request", value: state5.requestPreview }] : []
783051
+ ];
782538
783052
  let body;
783053
+ let richHtml;
782539
783054
  if (state5.activePage === "response") {
782540
783055
  const response = state5.responseText.trim();
782541
783056
  const clipped = response.length > 2600 ? `${response.slice(0, 2597).trimEnd()}...` : response;
@@ -782548,15 +783063,53 @@ function renderTelegramAdminLivePanelPayload(state5) {
782548
783063
  ].filter(Boolean).join("\n\n");
782549
783064
  } else if (state5.activePage === "tools") {
782550
783065
  const lines = state5.toolLines.slice(-12);
782551
- body = lines.length > 0 ? `<blockquote>${escapeTelegramHTML(lines.join("\n"))}</blockquote>` : "<i>No tool calls yet.</i>";
783066
+ const rendered = renderTelegramPanelPage({
783067
+ title: "Tools",
783068
+ subtitle: `${lines.length} recent tool event${lines.length === 1 ? "" : "s"}. Newest is last.`,
783069
+ metadata,
783070
+ sections: [{ packets: telegramAdminLinePackets(lines, "Tool event") }],
783071
+ emptyText: "No tool calls yet.",
783072
+ footer: "Page 2 of 5 · Tools"
783073
+ });
783074
+ body = telegramPanelHtmlFallback(rendered.html);
783075
+ richHtml = rendered.richHtml;
782552
783076
  } else if (state5.activePage === "logs") {
782553
- const lines = [
782554
- ...state5.mutationLines.slice(-8),
782555
- ...state5.logLines.slice(-10)
782556
- ];
782557
- body = lines.length > 0 ? `<blockquote expandable>${escapeTelegramHTML(lines.join("\n"))}</blockquote>` : "<i>No mutations or logs yet.</i>";
783077
+ const mutations = state5.mutationLines.slice(-8);
783078
+ const logs = state5.logLines.slice(-10);
783079
+ const rendered = renderTelegramPanelPage({
783080
+ title: "Activity",
783081
+ subtitle: `${mutations.length} workspace change event${mutations.length === 1 ? "" : "s"}; ${logs.length} runner event${logs.length === 1 ? "" : "s"}.`,
783082
+ metadata,
783083
+ sections: [
783084
+ {
783085
+ title: "Workspace changes",
783086
+ packets: telegramAdminLinePackets(mutations, "Change")
783087
+ },
783088
+ {
783089
+ title: "Run activity",
783090
+ packets: telegramAdminLinePackets(logs, "Event")
783091
+ }
783092
+ ],
783093
+ emptyText: "No workspace changes or runner activity yet.",
783094
+ footer: "Page 3 of 5 · Activity"
783095
+ });
783096
+ body = telegramPanelHtmlFallback(rendered.html);
783097
+ richHtml = rendered.richHtml;
782558
783098
  } else if (state5.activePage === "evidence") {
782559
- body = state5.evidenceText.trim() ? `<blockquote expandable>${escapeTelegramHTML(state5.evidenceText.trim())}</blockquote>` : "<i>No completion evidence yet.</i>";
783099
+ const packets = telegramTitledTextPackets(
783100
+ state5.evidenceText,
783101
+ "Completion evidence"
783102
+ );
783103
+ const rendered = renderTelegramPanelPage({
783104
+ title: "Evidence",
783105
+ subtitle: `${packets.length} completion evidence packet${packets.length === 1 ? "" : "s"}.`,
783106
+ metadata,
783107
+ sections: [{ packets }],
783108
+ emptyText: "No completion evidence yet.",
783109
+ footer: "Page 4 of 5 · Evidence"
783110
+ });
783111
+ body = telegramPanelHtmlFallback(rendered.html);
783112
+ richHtml = rendered.richHtml;
782560
783113
  } else {
782561
783114
  const pageCount = state5.contextPages.length;
782562
783115
  const pageIndex = Math.min(
@@ -782565,36 +783118,20 @@ function renderTelegramAdminLivePanelPayload(state5) {
782565
783118
  );
782566
783119
  state5.contextPageIndex = pageIndex;
782567
783120
  const page2 = state5.contextPages[pageIndex];
782568
- if (page2) {
782569
- const meta = page2.metadata.map((line) => `<code>${escapeTelegramHTML(line)}</code>`).join("\n");
782570
- body = [
782571
- `<b>${escapeTelegramHTML(page2.title)}</b>`,
782572
- `Source: <code>${escapeTelegramHTML(page2.source)}</code>`,
782573
- meta,
782574
- "",
782575
- page2.body.trim() ? `<blockquote expandable>${escapeTelegramHTML(page2.body)}</blockquote>` : "<i>Section was empty.</i>",
782576
- "",
782577
- `<i>Context ${pageIndex + 1}/${pageCount}</i>`
782578
- ].filter(Boolean).join("\n");
782579
- } else {
782580
- body = "<i>Full context has not been captured yet.</i>";
782581
- }
783121
+ const rendered = renderTelegramContextExplorerPagePayload(
783122
+ page2,
783123
+ pageIndex,
783124
+ pageCount,
783125
+ "Context"
783126
+ );
783127
+ body = rendered.text;
783128
+ richHtml = rendered.rich_html;
782582
783129
  }
782583
- const panelText = state5.activePage === "response" ? body : [
782584
- header,
782585
- "",
782586
- body,
782587
- "",
782588
- `<i>Page ${TELEGRAM_ADMIN_LIVE_PANEL_PAGES.indexOf(state5.activePage) + 1}/${TELEGRAM_ADMIN_LIVE_PANEL_PAGES.length}: ${telegramAdminLivePanelPageLabel(state5.activePage)}</i>`
782589
- ].join("\n");
782590
- const text3 = truncateTelegramUrlSafe(
782591
- panelText,
782592
- 3900,
782593
- "\n\n<i>... (panel clipped)</i>"
782594
- );
783130
+ const text3 = body;
782595
783131
  const pageButtons = TELEGRAM_ADMIN_LIVE_PANEL_PAGES.map((page2) => ({
782596
783132
  text: `${page2 === state5.activePage ? "●" : "○"} ${telegramAdminLivePanelPageLabel(page2)}${state5.dirtyPages[page2] ? " *" : ""}`,
782597
- callback_data: `omni:v1:tgadmin:${state5.nonce}:page:${page2}`
783133
+ callback_data: `omni:v1:tgadmin:${state5.nonce}:page:${page2}`,
783134
+ ...page2 === state5.activePage ? { style: "primary" } : {}
782598
783135
  }));
782599
783136
  const keyboard = [
782600
783137
  pageButtons.slice(0, 3),
@@ -782606,19 +783143,28 @@ function renderTelegramAdminLivePanelPayload(state5) {
782606
783143
  keyboard.push([
782607
783144
  {
782608
783145
  text: "Prev",
782609
- callback_data: `omni:v1:tgadmin:${state5.nonce}:ctx:${Math.max(0, pageIndex - 1)}`
783146
+ ...pageIndex > 0 ? {
783147
+ callback_data: `omni:v1:tgadmin:${state5.nonce}:ctx:${pageIndex - 1}`
783148
+ } : { disabled: {} }
782610
783149
  },
782611
783150
  {
782612
783151
  text: `${pageIndex + 1}/${lastPage + 1}`,
782613
- callback_data: `omni:v1:tgadmin:${state5.nonce}:ctx:${pageIndex}`
783152
+ disabled: {},
783153
+ style: "primary"
782614
783154
  },
782615
783155
  {
782616
783156
  text: "Next",
782617
- callback_data: `omni:v1:tgadmin:${state5.nonce}:ctx:${Math.min(lastPage, pageIndex + 1)}`
783157
+ ...pageIndex < lastPage ? {
783158
+ callback_data: `omni:v1:tgadmin:${state5.nonce}:ctx:${pageIndex + 1}`
783159
+ } : { disabled: {} }
782618
783160
  }
782619
783161
  ]);
782620
783162
  }
782621
- return { text: text3, reply_markup: { inline_keyboard: keyboard } };
783163
+ return {
783164
+ text: text3,
783165
+ ...richHtml ? { rich_html: richHtml } : {},
783166
+ reply_markup: { inline_keyboard: keyboard }
783167
+ };
782622
783168
  }
782623
783169
  function splitTelegramReminderDue(raw) {
782624
783170
  const text3 = raw.trim().replace(/^set\s+/i, "").trim();
@@ -785865,8 +786411,8 @@ ${context2}` : context2;
785865
786411
  }
785866
786412
  await this.transitionTelegramIntake(
785867
786413
  intakeRecord,
785868
- "consumed",
785869
- "attention router retained active-runner input as context without reply authority",
786414
+ decision2.source === "inference-unavailable" ? "failed" : "consumed",
786415
+ decision2.source === "inference-unavailable" ? "attention router could not produce a valid input-bound decision" : "attention router retained active-runner input as context without reply authority",
785870
786416
  { runId: sameRunner ? currentSubAgent.runId : expectedSubAgent.runId }
785871
786417
  );
785872
786418
  return;
@@ -788945,6 +789491,8 @@ ${antecedents.join("\n")}` : "Immediate antecedents: none.",
788945
789491
  scenarioConfidence: decision2.scenarioConfidence,
788946
789492
  scenarioObjective: decision2.scenarioObjective,
788947
789493
  scenarioStateLoop: decision2.scenarioStateLoop,
789494
+ diagnosticNote: decision2.diagnosticNote,
789495
+ validationIssueCodes: decision2.validationIssueCodes,
788948
789496
  salienceSignals,
788949
789497
  self: this.telegramSelfSocialActorInput(),
788950
789498
  daydreamOpportunities
@@ -791345,10 +791893,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
791345
791893
  id2,
791346
791894
  `broker queue admission deadline expired for ${deadline.request_id}${queueSummary ? ` (${queueSummary})` : ""}; re-entering after ${(delayMs / 1e3).toFixed(1)}s, attempt ${brokerQueueRetryAttempt}`
791347
791895
  );
791348
- await this.waitForTelegramBrokerQueueRetry(
791349
- delayMs,
791350
- lifecycleSignal
791351
- );
791896
+ await this.waitForTelegramBrokerQueueRetry(delayMs, lifecycleSignal);
791352
791897
  }
791353
791898
  }
791354
791899
  const usage = result.usage;
@@ -791731,6 +792276,15 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791731
792276
  getTelegramThinkingVisible() {
791732
792277
  return this.telegramThinkingVisible;
791733
792278
  }
792279
+ telegramMentionActorIsSelf(actor) {
792280
+ const selfUserId = this.currentTelegramBotUserId();
792281
+ if (selfUserId !== void 0 && actor.userId !== void 0 && actor.userId === selfUserId) {
792282
+ return true;
792283
+ }
792284
+ const selfUsername = this.state.botUsername.trim().replace(/^@/, "").toLowerCase();
792285
+ const actorUsername = actor.username?.trim().replace(/^@/, "").toLowerCase();
792286
+ return Boolean(selfUsername && actorUsername === selfUsername);
792287
+ }
791734
792288
  buildTelegramInteractionEvidencePacket(msg) {
791735
792289
  const sessionKey = this.sessionKeyForMessage(msg);
791736
792290
  const selfActorKey = telegramSocialActorKey(
@@ -791756,7 +792310,7 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791756
792310
  ];
791757
792311
  const mentionedActors = msg.mentionedActors ?? (msg.mentionedUsernames ?? []).map((username) => ({ username }));
791758
792312
  mentionedActors.forEach((actor, index) => {
791759
- const actorKey = telegramSocialActorKey(actor);
792313
+ const actorKey = this.telegramMentionActorIsSelf(actor) ? selfActorKey : telegramSocialActorKey(actor);
791760
792314
  if (actorKey === "unknown") return;
791761
792315
  actorKeys.add(actorKey);
791762
792316
  evidence.push({
@@ -791796,33 +792350,103 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791796
792350
  evidence
791797
792351
  };
791798
792352
  }
791799
- validateTelegramInteractionDecision(decision2, packet) {
791800
- if (decision2.inputId !== packet.inputId) return null;
791801
- if (!decision2.selfRole || !decision2.replyBasis) return null;
792353
+ validateTelegramInteractionDecisionDetailed(decision2, packet) {
792354
+ const issues = [];
792355
+ if (!decision2.inputId) {
792356
+ issues.push({
792357
+ code: "missing_input_id",
792358
+ message: `input_id must be ${JSON.stringify(packet.inputId)}.`
792359
+ });
792360
+ } else if (decision2.inputId !== packet.inputId) {
792361
+ issues.push({
792362
+ code: "input_id_mismatch",
792363
+ message: `input_id must be ${JSON.stringify(packet.inputId)}.`
792364
+ });
792365
+ }
792366
+ if (!decision2.selfRole) {
792367
+ issues.push({
792368
+ code: "missing_self_role",
792369
+ message: "self_role is required."
792370
+ });
792371
+ }
792372
+ if (!decision2.replyBasis) {
792373
+ issues.push({
792374
+ code: "missing_reply_basis",
792375
+ message: "reply_basis is required."
792376
+ });
792377
+ }
791802
792378
  const addressed = decision2.addressedActorKeys ?? [];
791803
792379
  const evidenceIds = decision2.evidenceIds ?? [];
791804
792380
  const allowedActors = new Set(packet.actorKeys);
791805
792381
  const allowedEvidence = new Set(packet.evidence.map((entry) => entry.id));
791806
- if (addressed.some((key) => !allowedActors.has(key))) return null;
791807
- if (evidenceIds.some((id2) => !allowedEvidence.has(id2))) return null;
792382
+ const unknownActors = addressed.filter((key) => !allowedActors.has(key));
792383
+ if (unknownActors.length > 0) {
792384
+ issues.push({
792385
+ code: "unknown_addressed_actor",
792386
+ message: `addressed_actor_keys contains unknown values ${JSON.stringify(unknownActors)}. Allowed values: ${JSON.stringify(packet.actorKeys)}.`
792387
+ });
792388
+ }
792389
+ const unknownEvidence = evidenceIds.filter(
792390
+ (id2) => !allowedEvidence.has(id2)
792391
+ );
792392
+ if (unknownEvidence.length > 0) {
792393
+ issues.push({
792394
+ code: "unknown_evidence_id",
792395
+ message: `evidence_ids contains unknown values ${JSON.stringify(unknownEvidence)}. Allowed values: ${JSON.stringify([...allowedEvidence])}.`
792396
+ });
792397
+ }
791808
792398
  if (decision2.selfRole === "addressee") {
791809
- if (!addressed.includes(packet.selfActorKey)) return null;
792399
+ if (!addressed.includes(packet.selfActorKey)) {
792400
+ issues.push({
792401
+ code: "addressee_missing_self_actor",
792402
+ message: `self_role=addressee requires addressed_actor_keys to include ${JSON.stringify(packet.selfActorKey)}.`
792403
+ });
792404
+ }
791810
792405
  } else if (decision2.selfRole === "observer") {
791811
- if (addressed.includes(packet.selfActorKey)) return null;
792406
+ if (addressed.includes(packet.selfActorKey)) {
792407
+ issues.push({
792408
+ code: "observer_addresses_self",
792409
+ message: `self_role=observer cannot address ${JSON.stringify(packet.selfActorKey)}.`
792410
+ });
792411
+ }
791812
792412
  }
791813
792413
  if (decision2.shouldReply) {
791814
- if (decision2.replyBasis === "none" || decision2.replyBasis === "uncertain" || evidenceIds.length === 0) {
791815
- return null;
792414
+ if (decision2.replyBasis === "none" || decision2.replyBasis === "uncertain") {
792415
+ issues.push({
792416
+ code: "reply_basis_not_authorized",
792417
+ message: "should_reply=true requires reply_basis direct_turn, ongoing_self_task, or social_intervention."
792418
+ });
791816
792419
  }
791817
- if (decision2.replyBasis === "direct_turn" && (decision2.selfRole !== "addressee" || !addressed.includes(packet.selfActorKey))) {
791818
- return null;
792420
+ if (evidenceIds.length === 0) {
792421
+ issues.push({
792422
+ code: "reply_missing_evidence",
792423
+ message: "should_reply=true requires at least one evidence_id."
792424
+ });
792425
+ }
792426
+ if (decision2.replyBasis === "direct_turn") {
792427
+ if (decision2.selfRole !== "addressee") {
792428
+ issues.push({
792429
+ code: "direct_turn_self_not_addressee",
792430
+ message: "reply_basis=direct_turn requires self_role=addressee."
792431
+ });
792432
+ }
792433
+ if (!addressed.includes(packet.selfActorKey)) {
792434
+ issues.push({
792435
+ code: "direct_turn_missing_self_actor",
792436
+ message: `reply_basis=direct_turn requires addressed_actor_keys to include ${JSON.stringify(packet.selfActorKey)}.`
792437
+ });
792438
+ }
791819
792439
  }
791820
792440
  if (decision2.replyBasis === "ongoing_self_task" && !packet.evidence.some(
791821
792441
  (entry) => entry.kind === "active_task" && evidenceIds.includes(entry.id)
791822
792442
  )) {
791823
- return null;
792443
+ issues.push({
792444
+ code: "ongoing_task_missing_active_task_evidence",
792445
+ message: "reply_basis=ongoing_self_task requires the active_task evidence ID."
792446
+ });
791824
792447
  }
791825
792448
  }
792449
+ if (issues.length > 0) return { decision: null, issues };
791826
792450
  const decisionId = `tgdec-${createHash74("sha256").update(
791827
792451
  JSON.stringify({
791828
792452
  inputId: packet.inputId,
@@ -791834,7 +792458,10 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791834
792458
  evidenceIds
791835
792459
  })
791836
792460
  ).digest("hex").slice(0, 20)}`;
791837
- return { ...decision2, decisionId };
792461
+ return { decision: { ...decision2, decisionId }, issues: [] };
792462
+ }
792463
+ validateTelegramInteractionDecision(decision2, packet) {
792464
+ return this.validateTelegramInteractionDecisionDetailed(decision2, packet).decision;
791838
792465
  }
791839
792466
  bindTrustedTelegramDecision(decision2, packet) {
791840
792467
  const bound = {
@@ -791847,13 +792474,20 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791847
792474
  };
791848
792475
  return this.validateTelegramInteractionDecision(bound, packet) ?? bound;
791849
792476
  }
791850
- async retryTelegramInteractionDecisionStrict(backend, userPrompt, forcedRoute, timeoutMs, diagnostics, sessionKey = "__router__", evidencePacket) {
792477
+ async retryTelegramInteractionDecisionStrict(backend, userPrompt, forcedRoute, timeoutMs, diagnostics, sessionKey = "__router__", evidencePacket, validationIssues = []) {
791851
792478
  const routeInstruction = forcedRoute ? `The operator selected Telegram mode "${forcedRoute}". The route field must be "${forcedRoute}", but should_reply must still be inferred from context.` : `Infer route live from context.`;
791852
792479
  const retryPrompt = [
791853
792480
  `A prior attempt failed the typed Telegram attention-decision contract and was discarded.`,
791854
792481
  `Make a fresh model-derived attention decision from the complete clean context below.`,
791855
792482
  `Return exactly one JSON object and no prose. No <think> tags. No commentary.`,
791856
792483
  routeInstruction,
792484
+ ...validationIssues.length > 0 ? [
792485
+ `The host rejected the prior semantic contract for these exact reasons:`,
792486
+ ...validationIssues.map(
792487
+ (issue2) => `- ${issue2.code}: ${issue2.message}`
792488
+ ),
792489
+ `Correct every listed issue. Do not repeat a rejected identifier.`
792490
+ ] : [],
791857
792491
  ``,
791858
792492
  `Required schema: ${TELEGRAM_INTERACTION_DECISION_MINIMAL_SCHEMA}`,
791859
792493
  ``,
@@ -791876,7 +792510,7 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791876
792510
  maxTokens: 700,
791877
792511
  timeoutMs: telegramRouterTimeoutMs(timeoutMs, 8e3, 15e3),
791878
792512
  think: false,
791879
- responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
792513
+ responseFormat: evidencePacket ? telegramInteractionDecisionResponseFormatForPacket(evidencePacket) : TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
791880
792514
  },
791881
792515
  diagnostics,
791882
792516
  "router-strict-retry",
@@ -791892,11 +792526,19 @@ arguments=` : ""}` + (chunk.toolCallArgs ?? "");
791892
792526
  defaultShouldReply: false,
791893
792527
  requireShouldReply: true
791894
792528
  });
791895
- const validated = parsed && evidencePacket ? this.validateTelegramInteractionDecision(parsed, evidencePacket) : parsed;
792529
+ if (parsed && evidencePacket) parsed.inputId = evidencePacket.inputId;
792530
+ const validation = parsed && evidencePacket ? this.validateTelegramInteractionDecisionDetailed(
792531
+ parsed,
792532
+ evidencePacket
792533
+ ) : { decision: parsed, issues: [] };
792534
+ const validated = validation.decision;
791896
792535
  if (!validated) {
791897
792536
  if (diagnostics) {
791898
792537
  const cleaned = stripTelegramHiddenThinking(retryText).trim();
791899
- diagnostics.strictRetryStatus = cleaned ? "unparseable" : "empty";
792538
+ diagnostics.strictRetryStatus = parsed && validation.issues.length > 0 ? "semantic-invalid" : cleaned ? "unparseable" : "empty";
792539
+ diagnostics.strictRetryValidationIssues = validation.issues.map(
792540
+ (issue2) => issue2.code
792541
+ );
791900
792542
  }
791901
792543
  return null;
791902
792544
  }
@@ -792449,7 +793091,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792449
793091
  maxTokens: 360,
792450
793092
  timeoutMs: telegramRouterTimeoutMs(config.timeoutMs, 8e3, 3e4),
792451
793093
  think: false,
792452
- responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
793094
+ responseFormat: telegramInteractionDecisionResponseFormatForPacket(evidencePacket)
792453
793095
  },
792454
793096
  diagnostics,
792455
793097
  "router",
@@ -792479,7 +793121,20 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792479
793121
  defaultShouldReply: false,
792480
793122
  requireShouldReply: true
792481
793123
  });
792482
- const validated = parsed ? this.validateTelegramInteractionDecision(parsed, evidencePacket) : null;
793124
+ if (parsed) parsed.inputId = evidencePacket.inputId;
793125
+ const initialValidation = parsed ? this.validateTelegramInteractionDecisionDetailed(
793126
+ parsed,
793127
+ evidencePacket
793128
+ ) : {
793129
+ decision: null,
793130
+ issues: []
793131
+ };
793132
+ if (diagnostics) {
793133
+ diagnostics.validationIssues = initialValidation.issues.map(
793134
+ (issue2) => issue2.code
793135
+ );
793136
+ }
793137
+ const validated = initialValidation.decision;
792483
793138
  if (validated) {
792484
793139
  return withRouterTelemetry(
792485
793140
  this.applyTelegramSilentReflectionNotes(validated, reflectionNotes)
@@ -792511,7 +793166,8 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792511
793166
  config.timeoutMs ?? 3e4,
792512
793167
  diagnostics,
792513
793168
  sessionKey,
792514
- evidencePacket
793169
+ evidencePacket,
793170
+ initialValidation.issues
792515
793171
  );
792516
793172
  if (strictRetry) {
792517
793173
  return withRouterTelemetry(
@@ -792525,20 +793181,26 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792525
793181
  ) || telegramRouterErrorLooksLikeBackendLiveness(
792526
793182
  diagnostics.strictRetryError ?? ""
792527
793183
  );
792528
- const fallback = this.applyTelegramSilentReflectionNotes(
792529
- this.buildTelegramRouterUnavailableDecision(msg, toolContext, {
792530
- reason: backendLivenessFailure ? "router recovery hit a backend liveness failure; no model-derived reply decision" : dualEmptyVisible ? "router returned no visible decision content in JSON or plain mode; no model-derived reply decision" : "router output failed the typed decision contract and the fresh retry; no model-derived reply decision",
792531
- silentDisposition: reflectionNotes.silentDisposition,
792532
- diagnosticNote: this.composeTelegramRouterDiagnosticNote(
792533
- invalidRouterPreview,
792534
- failureNarrative,
792535
- backendLivenessFailure ? "router backend failed during attention-decision recovery; no usable router decision was available" : dualEmptyVisible ? "router returned no visible decision content in JSON or plain mode; fresh typed retry did not recover it" : invalidRouterPreview ? "router produced an invalid attention decision payload; the quarantined fresh retry did not recover it" : "router produced an empty attention decision payload; the fresh typed retry did not recover it",
792536
- diagnostics
792537
- ),
792538
- raw: text3
792539
- }),
792540
- reflectionNotes
792541
- );
793184
+ const fallback = {
793185
+ ...this.applyTelegramSilentReflectionNotes(
793186
+ this.buildTelegramRouterUnavailableDecision(msg, toolContext, {
793187
+ reason: backendLivenessFailure ? "router recovery hit a backend liveness failure; no model-derived reply decision" : dualEmptyVisible ? "router returned no visible decision content in JSON or plain mode; no model-derived reply decision" : "router output failed the typed decision contract and the fresh retry; no model-derived reply decision",
793188
+ silentDisposition: reflectionNotes.silentDisposition,
793189
+ diagnosticNote: this.composeTelegramRouterDiagnosticNote(
793190
+ invalidRouterPreview,
793191
+ failureNarrative,
793192
+ backendLivenessFailure ? "router backend failed during attention-decision recovery; no usable router decision was available" : dualEmptyVisible ? "router returned no visible decision content in JSON or plain mode; fresh typed retry did not recover it" : invalidRouterPreview ? "router produced an invalid attention decision payload; the quarantined fresh retry did not recover it" : "router produced an empty attention decision payload; the fresh typed retry did not recover it",
793193
+ diagnostics
793194
+ ),
793195
+ raw: text3
793196
+ }),
793197
+ reflectionNotes
793198
+ ),
793199
+ validationIssueCodes: [
793200
+ ...diagnostics.validationIssues ?? [],
793201
+ ...diagnostics.strictRetryValidationIssues ?? []
793202
+ ].filter((code8, index, all2) => all2.indexOf(code8) === index)
793203
+ };
792542
793204
  return withRouterTelemetry(fallback);
792543
793205
  } catch (err) {
792544
793206
  if (err instanceof TelegramBridgeStoppedError) throw err;
@@ -792617,7 +793279,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792617
793279
  `empty visible content after <think> strip during ${affected}`
792618
793280
  );
792619
793281
  }
792620
- if (visibleAttempts.length > 0 && diag.strictRetryStatus !== "recovered") {
793282
+ if (visibleAttempts.length > 0 && (diag.validationIssues ?? []).length === 0 && diag.strictRetryStatus !== "recovered") {
792621
793283
  parts.push(`visible router text remained unparseable`);
792622
793284
  }
792623
793285
  if (skippedAttempts.length > 0) {
@@ -792668,6 +793330,10 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792668
793330
  detailParts.push(
792669
793331
  `strict-retry: unparseable (preview="${diag.strictRetryPreview ?? ""}")`
792670
793332
  );
793333
+ } else if (diag.strictRetryStatus === "semantic-invalid") {
793334
+ detailParts.push(
793335
+ `strict-retry: semantic-invalid (${(diag.strictRetryValidationIssues ?? []).join(", ") || "no issue codes"})`
793336
+ );
792671
793337
  } else if (diag.strictRetryStatus === "threw") {
792672
793338
  detailParts.push(
792673
793339
  `strict-retry: threw (${diag.strictRetryError ?? "no detail"})`
@@ -792679,6 +793345,19 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
792679
793345
  } else if (diag.strictRetryStatus === "recovered") {
792680
793346
  detailParts.push(`strict-retry: recovered`);
792681
793347
  }
793348
+ if ((diag.validationIssues ?? []).length > 0) {
793349
+ parts.push(
793350
+ `router semantic contract rejected (${diag.validationIssues.join(", ")})`
793351
+ );
793352
+ detailParts.push(
793353
+ `router-validation: ${diag.validationIssues.join(", ")}`
793354
+ );
793355
+ }
793356
+ if ((diag.strictRetryValidationIssues ?? []).length > 0) {
793357
+ detailParts.push(
793358
+ `strict-retry-validation: ${diag.strictRetryValidationIssues.join(", ")}`
793359
+ );
793360
+ }
792682
793361
  let operatorHint;
792683
793362
  if (networkErrorSeen) {
792684
793363
  operatorHint = this.telegramRouterBackendLivenessHint(timeoutSeen);
@@ -793540,10 +794219,14 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793540
794219
  * Send a placeholder message that will be progressively edited with
793541
794220
  * streamed content. Returns the message_id for subsequent edits.
793542
794221
  */
793543
- async sendLiveMessage(chatId, initialText, replyToMessageId, replyMarkup) {
794222
+ async sendLiveMessage(chatId, initialText, replyToMessageId, replyMarkup, richHtml) {
793544
794223
  const normalizedInitialText = normalizeTelegramOutboundLinks(initialText);
794224
+ const normalizedRichHtml = richHtml ? normalizeTelegramOutboundLinks(richHtml) : "";
793545
794225
  try {
793546
- const body = {
794226
+ const body = normalizedRichHtml ? {
794227
+ chat_id: chatId,
794228
+ rich_message: { html: normalizedRichHtml }
794229
+ } : {
793547
794230
  chat_id: chatId,
793548
794231
  text: normalizedInitialText,
793549
794232
  parse_mode: "HTML"
@@ -793551,15 +794234,16 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793551
794234
  const replyParameters = telegramReplyParameters(replyToMessageId);
793552
794235
  if (replyParameters) body["reply_parameters"] = replyParameters;
793553
794236
  if (replyMarkup) body["reply_markup"] = replyMarkup;
793554
- const result = await this.apiCall("sendMessage", body);
794237
+ const method = normalizedRichHtml ? "sendRichMessage" : "sendMessage";
794238
+ const result = await this.apiCall(method, body);
793555
794239
  if (result.ok === false) {
793556
794240
  throw new Error(
793557
- String(result.description || "Telegram sendMessage failed")
794241
+ String(result.description || `Telegram ${method} failed`)
793558
794242
  );
793559
794243
  }
793560
794244
  const messageId = result.result?.message_id;
793561
794245
  if (!Number.isInteger(messageId)) {
793562
- throw new Error("Telegram sendMessage returned no valid message_id");
794246
+ throw new Error(`Telegram ${method} returned no valid message_id`);
793563
794247
  }
793564
794248
  this.state.messagesSent++;
793565
794249
  this.updateTelegramTextDeliveryCapability(chatId, {
@@ -793569,10 +794253,43 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793569
794253
  });
793570
794254
  return messageId;
793571
794255
  } catch {
794256
+ if (normalizedRichHtml) {
794257
+ try {
794258
+ const body = {
794259
+ chat_id: chatId,
794260
+ text: normalizedInitialText,
794261
+ parse_mode: "HTML"
794262
+ };
794263
+ const replyParameters = telegramReplyParameters(replyToMessageId);
794264
+ if (replyParameters) body["reply_parameters"] = replyParameters;
794265
+ if (replyMarkup) body["reply_markup"] = replyMarkup;
794266
+ const result = await this.apiCall("sendMessage", body);
794267
+ if (result.ok === false) {
794268
+ throw new Error(
794269
+ String(result.description || "Telegram sendMessage failed")
794270
+ );
794271
+ }
794272
+ const messageId = result.result?.message_id;
794273
+ if (!Number.isInteger(messageId)) {
794274
+ throw new Error(
794275
+ "Telegram sendMessage returned no valid message_id"
794276
+ );
794277
+ }
794278
+ this.state.messagesSent++;
794279
+ this.updateTelegramTextDeliveryCapability(chatId, {
794280
+ status: "allowed",
794281
+ canSendText: true,
794282
+ source: "sendLiveMessage.html_fallback"
794283
+ });
794284
+ return messageId;
794285
+ } catch {
794286
+ }
794287
+ }
793572
794288
  try {
794289
+ const readablePlain = telegramHtmlToPlainText(normalizedInitialText);
793573
794290
  const body = {
793574
794291
  chat_id: chatId,
793575
- text: normalizedInitialText
794292
+ text: readablePlain || "No displayable content."
793576
794293
  };
793577
794294
  const replyParameters = telegramReplyParameters(replyToMessageId);
793578
794295
  if (replyParameters) body["reply_parameters"] = replyParameters;
@@ -793620,19 +794337,18 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793620
794337
  * Edit a live message with updated content. Used for progressive streaming.
793621
794338
  * Handles rate limits and Telegram's "message is not modified" error silently.
793622
794339
  */
793623
- async editLiveMessage(chatId, messageId, html, replyMarkup) {
794340
+ async editLiveMessage(chatId, messageId, html, replyMarkup, richHtml) {
793624
794341
  const normalizedHtml = normalizeTelegramOutboundLinks(html);
793625
- const truncated = normalizedHtml.length > 4e3 ? truncateTelegramUrlSafe(
793626
- normalizedHtml,
793627
- 4e3,
793628
- "\n\n<i>... (streaming)</i>"
793629
- ) : normalizedHtml;
794342
+ const normalizedRichHtml = richHtml ? normalizeTelegramOutboundLinks(richHtml) : "";
794343
+ const htmlSegments = segmentTelegramHtml(normalizedHtml, 3600);
794344
+ const truncated = htmlSegments.length > 1 ? `${htmlSegments[0]}
794345
+
794346
+ <i>... (additional detail clipped)</i>` : htmlSegments[0] ?? normalizedHtml;
793630
794347
  try {
793631
794348
  const result = await this.apiCall("editMessageText", {
793632
794349
  chat_id: chatId,
793633
794350
  message_id: messageId,
793634
- text: truncated,
793635
- parse_mode: "HTML",
794351
+ ...normalizedRichHtml ? { rich_message: { html: normalizedRichHtml } } : { text: truncated, parse_mode: "HTML" },
793636
794352
  ...replyMarkup ? { reply_markup: replyMarkup } : {}
793637
794353
  });
793638
794354
  if (result.error_code === 400 && result.description?.includes("not modified"))
@@ -793644,8 +794360,24 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793644
794360
  );
793645
794361
  return true;
793646
794362
  } catch {
794363
+ if (normalizedRichHtml) {
794364
+ try {
794365
+ const result = await this.apiCall("editMessageText", {
794366
+ chat_id: chatId,
794367
+ message_id: messageId,
794368
+ text: truncated,
794369
+ parse_mode: "HTML",
794370
+ ...replyMarkup ? { reply_markup: replyMarkup } : {}
794371
+ });
794372
+ if (result.error_code === 400 && result.description?.includes("not modified"))
794373
+ return true;
794374
+ if (result.error_code === 429) return false;
794375
+ if (result.ok !== false) return true;
794376
+ } catch {
794377
+ }
794378
+ }
793647
794379
  try {
793648
- const plain = normalizedHtml.replace(/<[^>]+>/g, "");
794380
+ const plain = telegramHtmlToPlainText(normalizedHtml);
793649
794381
  const truncPlain = plain.length > 4e3 ? truncateTelegramUrlSafe(plain, 4e3, "\n\n... (streaming)") : plain;
793650
794382
  const result = await this.apiCall("editMessageText", {
793651
794383
  chat_id: chatId,
@@ -793760,20 +794492,25 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793760
794492
  async renderTelegramAdminLivePanel(state5, replyToMessageId) {
793761
794493
  const payload = renderTelegramAdminLivePanelPayload(state5);
793762
794494
  if (state5.messageId !== void 0) {
793763
- return this.editLiveMessage(
794495
+ const delivered = await this.editLiveMessage(
793764
794496
  state5.chatId,
793765
794497
  state5.messageId,
793766
794498
  payload.text,
793767
- payload.reply_markup
794499
+ payload.reply_markup,
794500
+ payload.rich_html
793768
794501
  );
794502
+ if (delivered) state5.dirtyPages[state5.activePage] = false;
794503
+ return delivered;
793769
794504
  }
793770
794505
  const messageId = await this.sendLiveMessage(
793771
794506
  state5.chatId,
793772
794507
  payload.text,
793773
794508
  replyToMessageId,
793774
- payload.reply_markup
794509
+ payload.reply_markup,
794510
+ payload.rich_html
793775
794511
  );
793776
794512
  state5.messageId = messageId;
794513
+ state5.dirtyPages[state5.activePage] = false;
793777
794514
  return true;
793778
794515
  }
793779
794516
  updateTelegramAdminLivePanelFromEvent(subAgent, msg, event) {
@@ -793808,12 +794545,14 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
793808
794545
  ...subAgent.steeringReconciliations.values()
793809
794546
  ];
793810
794547
  state5.responseText = replyText || (status === "failed" ? "I couldn't complete that request." : "Done.");
794548
+ state5.responseSegments = [];
794549
+ state5.activeResponseSegmentKey = void 0;
793811
794550
  if (evidenceText.trim()) {
793812
794551
  state5.evidenceText = evidenceText.trim();
793813
794552
  state5.dirtyPages.evidence = true;
793814
794553
  }
793815
- state5.activePage = "response";
793816
- state5.dirtyPages.response = false;
794554
+ if (state5.activePage === "response") state5.dirtyPages.response = false;
794555
+ else state5.dirtyPages.response = true;
793817
794556
  state5.updatedAt = Date.now();
793818
794557
  this.refreshTelegramAdminLivePanelContextPages(state5, subAgent, msg);
793819
794558
  if (subAgent.liveMessagePromise)
@@ -793948,6 +794687,7 @@ ${signals || summary || "Canonical runner status reported completed."}`
793948
794687
  [
793949
794688
  {
793950
794689
  text: "Admin context",
794690
+ style: "primary",
793951
794691
  callback_data: this.telegramContextExplorerCallbackData(
793952
794692
  id2,
793953
794693
  "open",
@@ -793966,35 +794706,36 @@ ${signals || summary || "Canonical runner status reported completed."}`
793966
794706
  [
793967
794707
  {
793968
794708
  text: "Prev",
793969
- callback_data: this.telegramContextExplorerCallbackData(
793970
- state5.id,
793971
- "page",
793972
- Math.max(0, page2 - 1)
793973
- )
794709
+ ...page2 > 0 ? {
794710
+ callback_data: this.telegramContextExplorerCallbackData(
794711
+ state5.id,
794712
+ "page",
794713
+ page2 - 1
794714
+ )
794715
+ } : { disabled: {} }
793974
794716
  },
793975
794717
  {
793976
794718
  text: `${page2 + 1}/${lastPage + 1}`,
793977
- callback_data: this.telegramContextExplorerCallbackData(
793978
- state5.id,
793979
- "noop",
793980
- page2
793981
- )
794719
+ disabled: {},
794720
+ style: "primary"
793982
794721
  },
793983
794722
  {
793984
794723
  text: "Next",
793985
- callback_data: this.telegramContextExplorerCallbackData(
793986
- state5.id,
793987
- "page",
793988
- Math.min(lastPage, page2 + 1)
793989
- )
794724
+ ...page2 < lastPage ? {
794725
+ callback_data: this.telegramContextExplorerCallbackData(
794726
+ state5.id,
794727
+ "page",
794728
+ page2 + 1
794729
+ )
794730
+ } : { disabled: {} }
793990
794731
  }
793991
794732
  ],
793992
794733
  [
793993
794734
  {
793994
- text: "Reply",
794735
+ text: "Close context",
793995
794736
  callback_data: this.telegramContextExplorerCallbackData(
793996
794737
  state5.id,
793997
- "reply",
794738
+ "close",
793998
794739
  page2
793999
794740
  )
794000
794741
  }
@@ -794005,7 +794746,7 @@ ${signals || summary || "Canonical runner status reported completed."}`
794005
794746
  splitTelegramContextBody(text3, maxLength = 2500) {
794006
794747
  const cleaned = stripTelegramHiddenThinking(text3 || "").trim();
794007
794748
  if (!cleaned) return [""];
794008
- return splitTelegramMessageText(cleaned, maxLength);
794749
+ return splitTelegramPlainText(cleaned, maxLength);
794009
794750
  }
794010
794751
  addTelegramContextExplorerPages(pages, title, source, body, metadata) {
794011
794752
  const chunks = this.splitTelegramContextBody(body);
@@ -794045,7 +794786,7 @@ ${signals || summary || "Canonical runner status reported completed."}`
794045
794786
  "",
794046
794787
  "Visible reply preview:",
794047
794788
  cleanTelegramVisibleReply(finalText).slice(0, 900) || "(empty)"
794048
- ].filter((line) => line !== "").join("\n");
794789
+ ].join("\n").replace(/\n{3,}/g, "\n\n").trim();
794049
794790
  pages.push({
794050
794791
  title: "Overview",
794051
794792
  source: "telegram.final_reply",
@@ -794056,6 +794797,23 @@ ${signals || summary || "Canonical runner status reported completed."}`
794056
794797
  `chat_id=${String(msg.chatId)}`
794057
794798
  ]
794058
794799
  });
794800
+ const reconciliations = [...subAgent.steeringReconciliations.values()];
794801
+ if (reconciliations.length > 0) {
794802
+ this.addTelegramContextExplorerPages(
794803
+ pages,
794804
+ "Plan updates",
794805
+ "runner.steering_reconciliation",
794806
+ reconciliations.map(
794807
+ (value2, index) => [
794808
+ `Update ${index + 1}:`,
794809
+ `Status: ${value2.status.replaceAll("_", " ")}`,
794810
+ ...value2.changedConstraints.length > 0 ? value2.changedConstraints.map((item) => `Change: ${item}`) : ["Change: none"],
794811
+ `Next: ${value2.revisedNextAction}`
794812
+ ].join("\n")
794813
+ ).join("\n\n"),
794814
+ [`count=${reconciliations.length}`, "visibility=admin-only"]
794815
+ );
794816
+ }
794059
794817
  if (frame2) {
794060
794818
  for (const section of frame2.sections) {
794061
794819
  this.addTelegramContextExplorerPages(
@@ -794146,23 +794904,43 @@ ${signals || summary || "Canonical runner status reported completed."}`
794146
794904
  [`count=${prompt.toolNames.length}`]
794147
794905
  );
794148
794906
  }
794149
- return pages;
794907
+ const redactor = getSecretRedactor();
794908
+ const redactedPages = pages.map((page2) => ({
794909
+ title: redactor.redactText(page2.title),
794910
+ source: redactor.redactText(page2.source),
794911
+ body: redactor.redactText(page2.body),
794912
+ metadata: page2.metadata.map((line) => redactor.redactText(line))
794913
+ }));
794914
+ const maxPages = 64;
794915
+ if (redactedPages.length <= maxPages) return redactedPages;
794916
+ const omitted = redactedPages.length - (maxPages - 1);
794917
+ return [
794918
+ ...redactedPages.slice(0, maxPages - 1),
794919
+ {
794920
+ title: "Omitted context packets",
794921
+ source: "telegram.context_budget",
794922
+ body: `${omitted} additional packet${omitted === 1 ? " was" : "s were"} omitted from this interactive view.`,
794923
+ metadata: [
794924
+ `retained=${maxPages - 1}`,
794925
+ `omitted=${omitted}`,
794926
+ `total=${redactedPages.length}`
794927
+ ]
794928
+ }
794929
+ ];
794150
794930
  }
794151
- buildTelegramContextExplorerState(msg, subAgent, finalHtml, finalText) {
794931
+ buildTelegramContextExplorerState(msg, subAgent, finalText) {
794152
794932
  const pages = this.buildTelegramContextExplorerPages(
794153
794933
  msg,
794154
794934
  subAgent,
794155
794935
  finalText
794156
794936
  );
794157
794937
  if (pages.length === 0) return null;
794158
- const finalHtmlFirstChunk = splitTelegramMessageText(finalHtml, 3900)[0] ?? finalHtml;
794159
794938
  const id2 = randomBytes29(6).toString("hex");
794160
794939
  const now2 = Date.now();
794161
794940
  return {
794162
794941
  id: id2,
794163
- chatId: msg.chatId,
794164
- messageId: null,
794165
- finalHtml: finalHtmlFirstChunk,
794942
+ sourceChatId: msg.chatId,
794943
+ sourceMessageId: null,
794166
794944
  activePage: 0,
794167
794945
  pages,
794168
794946
  createdAtMs: now2,
@@ -794175,20 +794953,10 @@ ${signals || summary || "Canonical runner status reported completed."}`
794175
794953
  Math.max(0, state5.pages.length - 1)
794176
794954
  );
794177
794955
  const page2 = state5.pages[pageIndex];
794178
- if (!page2) return "<b>Context frame</b>\n<i>No pages available.</i>";
794179
- const meta = page2.metadata.map((line) => `<code>${escapeTelegramHTML(line)}</code>`).join("\n");
794180
- const body = page2.body.trim() ? `<blockquote expandable>${escapeTelegramHTML(page2.body)}</blockquote>` : "<i>Section was empty.</i>";
794181
- return truncateTelegramUrlSafe(
794182
- [
794183
- `<b>Context frame</b>`,
794184
- `<i>Page ${pageIndex + 1}/${state5.pages.length}: ${escapeTelegramHTML(page2.title)}</i>`,
794185
- `Source: <code>${escapeTelegramHTML(page2.source)}</code>`,
794186
- meta,
794187
- "",
794188
- body
794189
- ].filter(Boolean).join("\n"),
794190
- 3900,
794191
- "\n\n<i>... (context page clipped)</i>"
794956
+ return renderTelegramContextExplorerPagePayload(
794957
+ page2,
794958
+ pageIndex,
794959
+ state5.pages.length
794192
794960
  );
794193
794961
  }
794194
794962
  async handleTelegramContextExplorerCallback(callback) {
@@ -794215,36 +794983,94 @@ ${signals || summary || "Canonical runner status reported completed."}`
794215
794983
  }
794216
794984
  const action = parts[4];
794217
794985
  const requestedPage = Number.parseInt(parts[5] ?? "0", 10);
794218
- const messageId = state5.messageId ?? callback.messageId;
794219
- if (messageId === void 0) {
794220
- answerText = "Cannot identify context frame message.";
794986
+ if (action === "open") {
794987
+ if (callback.chatId !== state5.sourceChatId || callback.messageId !== state5.sourceMessageId) {
794988
+ answerText = "This context button is not bound to this message.";
794989
+ alert = true;
794990
+ return;
794991
+ }
794992
+ const maxPage2 = Math.max(0, state5.pages.length - 1);
794993
+ const nextPage2 = Number.isFinite(requestedPage) ? Math.min(maxPage2, Math.max(0, requestedPage)) : 0;
794994
+ const previousPage2 = state5.activePage;
794995
+ state5.activePage = nextPage2;
794996
+ const payload2 = this.renderTelegramContextExplorerPage(state5);
794997
+ const markup = this.telegramContextExplorerPageMarkup(state5);
794998
+ try {
794999
+ if (state5.panelMessageId !== void 0 && state5.panelChatId === callback.fromUserId) {
795000
+ const edited2 = await this.editLiveMessage(
795001
+ callback.fromUserId,
795002
+ state5.panelMessageId,
795003
+ payload2.text,
795004
+ markup,
795005
+ payload2.rich_html
795006
+ );
795007
+ if (!edited2) throw new Error("Telegram did not apply the DM edit");
795008
+ } else {
795009
+ state5.panelMessageId = await this.sendLiveMessage(
795010
+ callback.fromUserId,
795011
+ payload2.text,
795012
+ void 0,
795013
+ markup,
795014
+ payload2.rich_html
795015
+ );
795016
+ }
795017
+ } catch {
795018
+ state5.activePage = previousPage2;
795019
+ answerText = `Open a private chat with @${this.state.botUsername || "omnius_agent_bot"}, press Start, then try again.`;
795020
+ alert = true;
795021
+ return;
795022
+ }
795023
+ state5.viewerUserId = callback.fromUserId;
795024
+ state5.panelChatId = callback.fromUserId;
795025
+ answerText = "Opened the context packets in your private chat.";
795026
+ return;
795027
+ }
795028
+ const panelChatId = state5.panelChatId;
795029
+ const panelMessageId = state5.panelMessageId;
795030
+ if (state5.viewerUserId !== callback.fromUserId || panelChatId === void 0 || panelMessageId === void 0 || callback.chatId !== panelChatId || callback.messageId !== panelMessageId) {
795031
+ answerText = "This private context panel is not bound to this callback.";
794221
795032
  alert = true;
794222
795033
  return;
794223
795034
  }
794224
- state5.messageId = messageId;
794225
795035
  if (action === "noop") return;
794226
- if (action === "reply") {
794227
- await this.editLiveMessage(
794228
- state5.chatId,
794229
- messageId,
794230
- state5.finalHtml,
794231
- this.telegramContextExplorerReplyMarkup(state5.id)
795036
+ if (action === "close") {
795037
+ const closed = await this.editLiveMessage(
795038
+ panelChatId,
795039
+ panelMessageId,
795040
+ "<b>Context panel closed.</b>"
794232
795041
  );
795042
+ if (!closed) {
795043
+ answerText = "Telegram did not close this context panel. Try again.";
795044
+ alert = true;
795045
+ return;
795046
+ }
795047
+ state5.panelMessageId = void 0;
795048
+ state5.panelChatId = void 0;
795049
+ state5.viewerUserId = void 0;
794233
795050
  return;
794234
795051
  }
794235
- if (action !== "open" && action !== "page") {
795052
+ if (action !== "page") {
794236
795053
  answerText = "Unknown context frame action.";
794237
795054
  alert = true;
794238
795055
  return;
794239
795056
  }
794240
795057
  const maxPage = Math.max(0, state5.pages.length - 1);
794241
- state5.activePage = Number.isFinite(requestedPage) ? Math.min(maxPage, Math.max(0, requestedPage)) : 0;
794242
- await this.editLiveMessage(
794243
- state5.chatId,
794244
- messageId,
794245
- this.renderTelegramContextExplorerPage(state5),
794246
- this.telegramContextExplorerPageMarkup(state5)
795058
+ const nextPage = Number.isFinite(requestedPage) ? Math.min(maxPage, Math.max(0, requestedPage)) : 0;
795059
+ const previousPage = state5.activePage;
795060
+ state5.activePage = nextPage;
795061
+ const payload = this.renderTelegramContextExplorerPage(state5);
795062
+ const edited = await this.editLiveMessage(
795063
+ panelChatId,
795064
+ panelMessageId,
795065
+ payload.text,
795066
+ this.telegramContextExplorerPageMarkup(state5),
795067
+ payload.rich_html
794247
795068
  );
795069
+ if (!edited) {
795070
+ state5.activePage = previousPage;
795071
+ answerText = "Telegram did not apply this context page. Try again.";
795072
+ alert = true;
795073
+ }
794248
795074
  } catch (err) {
794249
795075
  answerText = err instanceof Error ? err.message : String(err);
794250
795076
  alert = true;
@@ -794697,8 +795523,8 @@ Join: ${newUrl}`
794697
795523
  work.intakeRecords.map(
794698
795524
  (intake) => this.transitionTelegramIntake(
794699
795525
  intake,
794700
- "consumed",
794701
- "Telegram attention router retained intake without visible reply"
795526
+ decision2.source === "inference-unavailable" ? "failed" : "consumed",
795527
+ decision2.source === "inference-unavailable" ? "Telegram attention router could not produce a valid input-bound decision" : "Telegram attention router retained intake without visible reply"
794702
795528
  )
794703
795529
  )
794704
795530
  );
@@ -795005,14 +795831,12 @@ Join: ${newUrl}`
795005
795831
  }
795006
795832
  const deliveredFinalText = finalText;
795007
795833
  const adminEvidenceText = isAdminDM ? this.telegramAdminCompletionRunText(subAgent, finalText) : "";
795008
- const finalHtml = composeTelegramReplyWithSteeringHTML(
795009
- deliveredFinalText,
795010
- [...subAgent.steeringReconciliations.values()]
795011
- );
795834
+ const finalHtml = isAdminDM ? composeTelegramReplyWithSteeringHTML(deliveredFinalText, [
795835
+ ...subAgent.steeringReconciliations.values()
795836
+ ]) : convertMarkdownToTelegramHTML(deliveredFinalText);
795012
795837
  const contextExplorerState = !isAdminDM && msg.chatType !== "private" && !msg.guestQueryId ? this.buildTelegramContextExplorerState(
795013
795838
  msg,
795014
795839
  subAgent,
795015
- finalHtml,
795016
795840
  deliveredFinalText
795017
795841
  ) : null;
795018
795842
  const contextExplorerReplyMarkup = contextExplorerState ? this.telegramContextExplorerReplyMarkup(contextExplorerState.id) : void 0;
@@ -795030,7 +795854,7 @@ Join: ${newUrl}`
795030
795854
  );
795031
795855
  if (contextExplorerState && sentMessageId !== null) {
795032
795856
  this.pruneTelegramContextExplorers();
795033
- contextExplorerState.messageId = sentMessageId;
795857
+ contextExplorerState.sourceMessageId = sentMessageId;
795034
795858
  this.telegramContextExplorerStates.set(
795035
795859
  contextExplorerState.id,
795036
795860
  contextExplorerState
@@ -796086,8 +796910,7 @@ ${creativeWorkspace}` : ""
796086
796910
  }
796087
796911
  const html = renderTelegramLiveProgressHTML(
796088
796912
  subAgent.intermediateLines,
796089
- subAgent.visibleReplyText || subAgent.accumulated,
796090
- [...subAgent.steeringReconciliations.values()]
796913
+ subAgent.visibleReplyText || subAgent.accumulated
796091
796914
  );
796092
796915
  if (!html.trim()) return;
796093
796916
  const now2 = Date.now();
@@ -799922,8 +800745,14 @@ ${this.quoteTelegramContextBlock(msg.text, 800)}`,
799922
800745
  return;
799923
800746
  }
799924
800747
  state5.messageId = messageId;
799925
- await this.renderTelegramAdminLivePanel(state5);
799926
- answerText2 = "";
800748
+ const delivered = await this.renderTelegramAdminLivePanel(state5);
800749
+ if (!delivered) {
800750
+ state5.dirtyPages[state5.activePage] = true;
800751
+ answerText2 = "Telegram did not apply this panel update. Try again.";
800752
+ alert2 = true;
800753
+ } else {
800754
+ answerText2 = "";
800755
+ }
799927
800756
  } catch (err) {
799928
800757
  answerText2 = err instanceof Error ? err.message : String(err);
799929
800758
  alert2 = true;