Skip to content
This repository was archived by the owner on Aug 17, 2026. It is now read-only.
This repository was archived by the owner on Aug 17, 2026. It is now read-only.

PromptBus TUI originals recaptured as wrappers after jiti reload → infinite onRequest cycle #136

Description

@joeshull

Summary

Intermittent RangeError: Maximum call stack size exceeded on the OMP TUI. Stack is a true cycle (same frames repeating), not the finite session_start wrapper chain fixed in #115.

[Unhandled Rejection] RangeError: Maximum call stack size exceeded.
    at onRequest (.../packages/extension/src/bridge.ts:2347:49)
    at <anonymous> (.../packages/extension/src/prompt-bus.ts:165:33)
    at new Promise (native:1:11)
    at request (.../packages/extension/src/prompt-bus.ts:141:16)
    at <anonymous> (.../packages/extension/src/bridge.ts:2401:12)
    at <anonymous> (.../packages/extension/src/bridge.ts:2230:42)
    at onRequest (.../packages/extension/src/bridge.ts:2347:49)
    ...

Installed path: ~/.omp-dashboard/packages/extension/src/. #115's getOrCreatePristineOriginals is present in that tree. This is a leftover hole, not a missing deploy.

Cycle

Line map on the installed file:

Stack frame What it is
bridge.ts:2230 TUI adapter batch arm (prompt.metadata.questions)
bridge.ts:2401 inputWrapperbus.request(...)
prompt-bus.ts:141/165 PromptBus.request distributes to adapters, calls onRequest synchronously
bridge.ts:2347 TUI adapter onRequest / present() (line may be onResponse after drift; cycle is the same)

present() does await originals.input(...). If originals.input is the PromptBus inputWrapper, evaluating that call synchronously re-enters bus.requesttui.onRequestoriginals.input forever. No await tick; stack blows immediately. Batch is just the prompt that hit it — any originals.select/input/confirm/editor would recurse the same way.

Why #115 does not cover this

#115 (ctx-ui-originals.ts) caches pristine ctx.ui.* in a module-level WeakMap. That is correct across repeated session_start in the same module instance.

It is not correct across:

  1. jiti module cache invalidation / extension reload (/__dashboard_reload, npm run reload, dashboard reload). New module = empty WeakMap. ctx.ui is the same object, still patched. First capture after reload reads the wrappers.
  2. Isolated extension vm contexts. bridge.ts already parks BridgeState on process.__pi_dashboard_bridge__ because module state does not survive those. The originals map does not.

On a cache miss, already-wrapped methods only console.warn and are still stored:

// ctx-ui-originals.ts
if (typeof ui[key] === 'function' && (ui[key] as any).__isPromptBusWrapper) {
  console.warn('[bridge] getOrCreatePristineOriginals: captured an already-wrapped ui method');
  break;
}
// then unconditionally:
captured.input = ui.input.bind(ui);  // bind() drops __isPromptBusWrapper
pristineUiMap.set(ui, captured);

After that, every prompt is a true cycle. Matches "from time to time": next interactive prompt after a reload / new vm context.

#115's finite chain grew with session_start count. This overflows on the first prompt after a bad capture.

Related

Suggested fix

  1. Persist pristine originals on process (same BRIDGE_KEY / sibling key) so they survive jiti invalidation and vm sandboxes. Key by ctx.ui identity.
  2. On capture, refuse to store a method with __isPromptBusWrapper. Walk/unwrap or fail loud. Never cache a wrapper as pristine.
  3. Regression: patch ctx.ui, drop the module WeakMap (simulate reload), recapture, invoke select/input/batch — must call the native once, never RangeError.

Notes

  • Observed on a long-lived TUI session; batch/ask 2+ questions is the recorded trigger.
  • Unrelated to omp-tool-ui-collapse.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions