omdsh-plugins/omdsh-tui--packages-tui0

@omdsh-plugins/omdsh-tui

Interactive pi-tui terminal front door for DeepSeek Harness agents

包名
@omdsh-plugins/omdsh-tui
版本
0.1.0-rc.6
许可证
MIT
最近更新
2026年8月17日

安装

此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 README ↗

@omdsh-plugins/omdsh-tui

English | 中文

The interactive terminal front door for DeepSeek Harness agents, built on @deepseek-ai/tui — this repository's vendored pi-tui, whose editor carries the prompt-prefix and frame modifications this front door renders with. It requires stdin and stdout TTYs; scripts and Loader pipes should use the one-shot @deepseek-ai/dsh-headless app instead.

The implemented TUI feature Agent Note owns the front-door decision; the file-reference autocomplete Agent Note owns path-only @file behavior; the terminal-state snapshot Agent Note owns its verification strategy.

Interactive terminals on macOS, Linux, and Windows are supported. Windows uses pi-tui's native console VT-input handling, and the Windows support Agent Note owns the platform decision and ConPTY process verification.

This package owns interactive terminal presentation and input only. It injects agents, commands, llm, systemPrompt, tokenMeter, tools, and userQuestions, optionally reads a skills service (present only when one is mounted), then drives an agent created or resumed by app or developer code. Agent lifecycle, persistence, and the model-facing ask_user_question tool remain separate composition entries.

After terminal startup succeeds, the package provides the terminal-local ctx.tui extension service. A plugin that injects it can call openOverlay() with a component factory and constrained layout options; the host exposes the viewport, semantic theme (including terminal-safe DeepSeek brand treatment), display-text escaping, redraw, close, and a lifetime signal, but not the pi-tui tree, terminal, focus controller, or overlay handle. Plugin overlays, the model selector, and user questions share one FIFO modal queue. Each request is an effect of the calling plugin fiber, so unload removes queued work or closes visible work before cleanup settles; terminal shutdown unloads dependents before stopping pi-tui. Overlay state is not logged or replayed. Component code is trusted and may render ANSI styling, but must pass untrusted text through host.display(). The interactive-extension Agent Note owns the boundary and rejected alternatives.

The TUI rebuilds resumed history from the append-origin session events, renders Markdown responses and reasoning, applies each tool's presentCall / presentResult intent to terminal, diff, or generic cards, keeps the standing todo/write plan in the dock above the input (cleared on the next turn/start), leaves injected context out of the transcript unless showContext asks for it, and presents ctx.userQuestions questions inline between the transcript/status area and the editor. The question panel shows progress, numbered options, wrapped labels, and separately indented descriptions; it obeys both maxQuestionOptions and questionDialogMaxHeight, marks hidden options with ↑ N more / ↓ N more, and uses Page Up / Page Down to page long question/detail content before an individually oversized selected block while keeping the editor visible. A configured welcome holds the header subtitle for the life of the process, so the frame the reader opened on does not change under them once the session is titled; with no greeting configured, the latest logged session title takes that line. The terminal window title becomes either way, which is where a session's name is a label rather than a greeting. The name is on screen as well: it rides the right end of the input's own top rule, which is the one edge already carrying nothing, so a name there costs no row and sits beside what the reader is looking at. It gives way rather than squeezing the frame — a name longer than half the row is clipped with an ellipsis, and a row too narrow to keep eight columns of rule beside it keeps the plain rule instead. A prompt renders above the step it opened: agent-loop appends step/start before the messages that step claims, so log order alone would head the answer above the question, and the surface places the prompt at the still-empty step instead of after it. A step that has already said something keeps its place, so mid-run steering stays below the text it followed. A durable llm/retry event retracts the failed step's live chunks and renders the scheduled retry count, delay, and failure in the transcript; success, exhaustion, and cancellation then settle through ordinary session events. Token accounting totals each logged model step's usage once, including failed attempts, while treating committed-message usage as a fallback for logs without a usage chunk. The context prompt value compares token-meter pressure with ctx.llm.resolveModelInfo() context for the current route and reads context unknown when the adapter has no capacity metadata; model carries the current route with the reasoning effort the next step will run at, which is the selected one where a selection exists and the route's adapter-advertised default otherwise — the same lookup answers both, so the effort costs no round-trip of its own. While the agent runs, an elapsed working indicator and esc interrupt replace the status row's content. A surface replacement never rewrites the rendered transcript: the conversation it shadows stays readable, and a landed compaction checkpoint adds one dim … earlier context was compacted … marker at its log position, so the terminal reports where the model stopped seeing that history instead of erasing it. Model-only replacement copies — a pruned tool result, a regenerated assistant message — render nothing.

An embedding may provide TuiRuntime.formatCwd when its logical workspace label differs from the session's host directory. The override changes only the banner's directory line; tools continue to use the session cwd.

Before model output, session events, tool presenters, questions, configuration, or diagnostics reach pi-tui's ANSI-aware renderers or the terminal title, the TUI renders C0 and C1 controls other than line feeds as visible \xNN text. Those sources cannot add terminal control sequences; the TUI and pi-tui retain ownership of terminal rendering and styling.

Typing @ at a token boundary searches files and directories under the session working directory. A bare fuzzy query uses a reusable bounded workspace index; a query containing / lists that directory directly, and selecting a folder keeps completion open for descent. Whitespace-bearing paths are inserted as @"path with spaces". Selecting a file inserts only its path and a trailing space: the TUI does not read it, attach hidden context, or replace it with a reference object. When a model-facing read tool is registered, the TUI adds one fixed system-prompt instruction telling the model to read an explicit path when its contents are needed.

When optional ctx.sessionReferenceResolver is mounted, the same @ menu also offers metadata-only session candidates, inserts @[label](dsh-session: ), and prepares the selected snapshots before dispatch. Session references remain structured because the model has no filesystem-like tool for retrieving session snapshots later. Preparation disables duplicate submission and restores the editor input on failure. The TUI chooses agent.steer() or agent.followup() from the status after that asynchronous preparation, so idle follow-ups still dispatch agent/pre-step while in-turn steering joins at a checkpoint without that hook.

While the agent is running, ordinary editor submissions call agent.steer(); otherwise they call agent.followup(). A slash at the start of the submitted line enters ctx.commands instead: known commands execute directly, unknown commands produce a warning, and neither path automatically reaches the model. A command producer may explicitly schedule agent work; dsh-plan-mode uses that contract for /plan [message]. The TUI registers /help, /model, /effort, /mode, /rename, /clear, /details, /palette, /reload, /resume, /status, and /exit as agent-scoped definitions; every other effective command joins autocomplete and /help dynamically, as do user-invocable skills, which the menu offers as /. A status line above the editor reports the turn phase the TUI derives from session events — waiting for the first token, thinking, responding, or executing tools — with the elapsed time in that phase and the running step total, refreshed each second, and ends with the Enter sends steering, Esc cancels hint; while steering messages wait to reach the model it inserts a N queued · badge before the hint that clears as each drains. While a turn runs, a Deep diving... row sits above the prompt beside that one, with one highlight sweeping the words every 1.8 seconds and the turn's age joining them once it passes fifteen seconds. Both the words and the sweep are the Web surface's own — the same literal, the same two brand stops, the same period, modelled rather than approximated — because a person moving between the two front doors of one harness should see the same thing happening. Truecolor terminals interpolate per glyph; a color terminal without it bolds the glyphs under the highlight and a monochrome one keeps the words. The sweep rides the status timer already ticking for the caret glyph, so it costs no clock of its own, and it is anchored to the logged turn/start, so a resumed turn reports its real age. During a live standalone compaction bracket, a fixed Context being compacted row appears above the prompt, the idle prompt caret becomes a one-cell throbbing , and terminal progress stays active until close; the row and glyph share the bracket's one refresh timer. This live state is never reconstructed from the log; a failed close adds Compaction failed: to the transcript, while a resumed orphaned start never activates the indicator (decision). Ctrl+C or Escape cancels a running turn. Tool and injected-context cards collapse long bodies into a configurable head/tail preview; Ctrl+O cycles tool cards through collapsed preview, full output, and hidden — the hidden phase drops tool cards from the transcript entirely while context cards, where showContext mounts them at all, stay at their preview, since injected instructions are not tool traffic. The hidden phase also folds each turn's assistant steps into one message: the first step with visible text or reasoning keeps the turn's single · marker, later steps render as unmarked continuations under the same indent, and a step without a visible body renders nothing; leaving the hidden phase restores the per-step markers. An injected-context card — off by default, and rendered only where showContext asks for it — renders its message as prose with the producer's outer reminder frame stripped, so neither the fold nor the frame stripping depends on the payload's syntax. What a plugin injects is addressed to the model rather than to the reader, and it lands under every prompt; a session reference is the exception the surface keeps, because the human typed the @ that put it there. Ctrl+R toggles reasoning, Ctrl+L redraws, and Ctrl+D exits while idle. Every dimension /details names ships off — reasoning hidden, the timing row absent, tool cards hidden, which is that dimension's own off — so a session opens as the conversation and nothing else and each detail is asked for rather than dismissed. toolCards, showReasoning, and showTiming set where a composition opens instead. /details names the same state those two shortcuts cycle, plus one they do not: bare it opens a centered keyboard toggle with one entry per dimension — Tool cards, Reasoning, and Step timing — showing the live values, where Tab cycles the highlighted entry and applies the change immediately (the transcript behind the dialog is the preview), and Enter, Esc, or Ctrl+C closes; /details collapsed|expanded|hidden jumps tool cards to that phase directly, /details reasoning [on|off] sets — or bare reasoning toggles — reasoning-block display, and /details timing [on|off] does the same for the per-step timing row. Hiding that row drops it rather than blanking it, since the space between a reply and the next prompt is the point; each row is a sibling of the step it measures, so the toggle walks the live steps and lets each hide its own instead of rebuilding the transcript, and a step that opens later inherits the current state. Arguments combine in one invocation, an unknown argument fails with the usage line, and a combined invocation applies reasoning first so its transcript rebuild never drops the other notices.

/model opens the advisory ctx.llm catalog as a keyboard selector: a filter box above the list narrows rows by a case-insensitive substring over each row's provider/model label, model name, and description, keeping the highlighted row selected when it survives the filter; Up/Down moves, Shift+Tab cycles the focused model's adapter-advertised reasoning efforts in display order, Enter selects the model and effort, and Escape clears a non-empty filter before a second Escape closes it. When an adapter does not advertise a default effort, the cycle also includes Default, which clears an explicit selection and preserves the provider default; models without selectable effort metadata ignore Shift+Tab. The selector renders the exact advertised effort list—including off when present—and does not synthesize, clamp, or transfer an effort between models. /model still selects an unambiguous model id directly, while /model / selects an exact target and uses its adapter default when one exists. The configured target or latest logged request header initializes the selector, and an unlisted current model remains visible because catalogs are advisory. Selection is local to this TUI session. Prompt assembly snapshots the target for one step, replaces {{provider}} and {{model}}, and applies the same provider/model/reasoning-effort target through agent/request; a switch during assembly therefore starts with a later step. The request header durably records targets that reach the model, while an unused selection remains process-local. /effort changes the reasoning effort of the model already selected, without walking a whole model catalog to reach it. Bare, it opens a keyboard list of that model's adapter-advertised efforts in display order — plus Default when the adapter configures none of its own, the same rule the model selector's Shift+Tab cycle follows — with the current effort preselected; Enter applies it, Escape or Ctrl+C closes without changing anything.