@xmoon76/dsh-pi-tui 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,9 @@ output.
30
30
 
31
31
  ## Prerequisites
32
32
 
33
- - A DeepSeek Harness installation with profiles support (`dsh` on your `PATH`).
33
+ - A DeepSeek Harness installation with profiles support (`dsh` on your `PATH`),
34
+ version `0.1.1-rc.1` or later on the same compat line (the TUI consumes the
35
+ split credential events and the `ctx.authorization` seam of that release).
34
36
  - Node >= 22.19 (`^22.19.0 || >=24`, same range as dsh). Running from source
35
37
  needs Node with native TypeScript support (>= 23.6) or the tsx ESM hook
36
38
  (`node --import tsx/esm`, how dsh's own source launch works).
@@ -327,19 +329,20 @@ the Advanced/Unstable roadmap).
327
329
  read. Enter switches to the selected session.
328
330
  - `/search <query>` — full-text search over persisted session logs, then
329
331
  switch to a hit.
330
- - `/title [title]` / `/rename [title]` — with an argument, set the current
331
- session's title (pins it against automatic generation; titles appear in
332
- the `/sessions` picker); **without an argument, regenerate the title from
333
- the conversation — this overwrites the current title, including one you
334
- pinned earlier**.
332
+ - `/title [title]` — with an argument, set the current session's title
333
+ (pins it against automatic generation; titles appear in the `/sessions`
334
+ picker); **without an argument, regenerate the title from the
335
+ conversation — this overwrites the current title, including one you
336
+ pinned earlier** (`/rename` is an alias).
337
+ - `/tasks` — the merged task browser: background jobs AND subagents in one
338
+ searchable list (type to filter rows by kind/label/status — `subagent`,
339
+ `bash`, `failed`…). `Enter` opens the detail (child transcript for a
340
+ subagent, status viewer for a job), `i` interrupts the selected subagent.
341
+ `/subagents` is an alias.
335
342
  - `/yolo` — switch to `danger-full-access` (alias of `/permission danger-full-access`).
336
- - `/queue` — per-item queue management: edit, delete, steer one, or insert a
337
- message into the agent's inbox (the queue pane above the editor shows
338
- pending messages; `Ctrl+S` steers them all at once, `Alt+↑` pulls them all
339
- back into the editor).
340
343
  - `/status` — show the current session's stats and identity (turn counts,
341
344
  token usage, workspace, installed dsh version).
342
- - `/preset`, `/model`, `/settings`, `/export`, `/fork`, `/subagents` — see
345
+ - `/preset`, `/model`, `/settings`, `/export`, `/fork` — see
343
346
  `dsh --profile pi-tui`'s command autocomplete (`/` + Tab).
344
347
 
345
348
  ## Keybindings (selection)
@@ -354,23 +357,30 @@ the Advanced/Unstable roadmap).
354
357
  draft, if any) into the running turn at once; otherwise sends the draft
355
358
  alone. An idle agent starts a fresh turn with everything.
356
359
  - `Alt+↑` — dequeue: pull every queued message back into the editor draft.
357
- - `Ctrl+T` — toggle the full todo list; the dock above the editor always shows
358
- the todo summary and background tasks, and queued input renders between
359
- them.
360
+ - `Ctrl+T` — toggle the full todo list (in fullscreen, clicking the panel
361
+ expands it to the full list and back); the dock above the editor always
362
+ shows the todo summary and background tasks, and queued input renders
363
+ between them.
360
364
  - `@` — file/folder mentions in the editor: `@` + Tab completes files from the
361
365
  whole workspace (fd-backed when `fd` is on PATH, with a built-in recursive
362
366
  fallback otherwise). The literal `@path` is submitted and the model reads
363
- the file itself. With background work running, an empty editor's `↓` or
364
- `Ctrl+J` opens the task browser over both surfaces:
367
+ the file itself. With background work running, an empty editor's `↓` opens
368
+ the same merged task browser as `/tasks`:
365
369
  - **subagent rows** (live continuable children) — `Enter` opens the child's
366
- transcript read-only (`Esc` returns); they never register jobs records, so
367
- this browser is their only glanceable home.
370
+ transcript read-only (`Esc` returns); `i` interrupts the selected child.
371
+ They never register jobs records, so this browser is their only glanceable
372
+ home.
368
373
  - **job rows** (bash and one-shot subagent jobs) — `Enter` shows the status
369
374
  viewer only: a bash job's output read cursor belongs to the model's
370
375
  `job_output`, and a one-shot subagent job record carries no child session
371
- id, so the transcript is reached via `/subagents` (`s` stops a job).
376
+ id, so a subagent job's transcript is reached through `/tasks` by picking
377
+ the child by its label.
372
378
  The footer badge shows `[N tasks running · M agents · ↓ view]` while any
373
- background work is live.
379
+ background work is live. The queue pane above the editor shows pending
380
+ input: user messages as `❯` rows, everything else (job notices, subagent
381
+ reports, injected instructions) as `⏳` notices that fold into a
382
+ `+N more` line beyond five — and disappear once the agent has received
383
+ them.
374
384
 
375
385
  ## Launch options
376
386
 
package/README.zh-CN.md CHANGED
@@ -28,7 +28,9 @@
28
28
 
29
29
  ## 前置要求
30
30
 
31
- - 一个支持 profile 的 DeepSeek Harness 安装(`dsh` 已在 `PATH` 中)。
31
+ - 一个支持 profile 的 DeepSeek Harness 安装(`dsh` 已在 `PATH` 中),版本
32
+ `0.1.1-rc.1` 或同一兼容线上的更新版本——TUI 消费该版本的拆分凭据事件
33
+ 与 `ctx.authorization` 服务。
32
34
  - Node >= 22.19(`^22.19.0 || >=24`,与 dsh 相同范围)。从源码运行需要
33
35
  原生支持 TypeScript 的 Node(>= 23.6)或 tsx ESM 钩子
34
36
  (`node --import tsx/esm`,dsh 自身源码启动的方式)。
@@ -292,18 +294,19 @@ SDK。原始终端访问、pre-host 输入拦截与完整输入所有权**不属
292
294
  边输入边搜索,行按工作区分组并实时显示 `filtered/total` 计数,标题在
293
295
  后台按需加载。回车切换到所选会话。
294
296
  - `/search <query>` — 在持久化的会话日志中全文搜索,然后跳转到命中项。
295
- - `/title [title]` / `/rename [title]` — 带参数时设置当前会话的标题
296
- (固定标题,防止自动生成;标题会出现在 `/sessions` 选择器中);
297
- **不带参数时根据对话重新生成标题——这会覆盖当前标题,包括你之前
298
- 固定的标题**。
297
+ - `/title [title]` — 带参数时设置当前会话的标题(固定标题,防止自动
298
+ 生成;标题会出现在 `/sessions` 选择器中);**不带参数时根据对话重新
299
+ 生成标题——这会覆盖当前标题,包括你之前固定的标题**(`/rename` 是
300
+ 别名)。
301
+ - `/tasks` — 合并任务浏览器:后台 job 与子代理在同一个可搜索列表中
302
+ (直接输入过滤行——`subagent`、`bash`、`failed`…)。`Enter` 打开详情
303
+ (子代理为子转录,job 为状态查看器),`i` 中断选中的子代理。
304
+ `/subagents` 是别名。
299
305
  - `/yolo` — 切换到 `danger-full-access`(`/permission danger-full-access`
300
306
  的别名)。
301
- - `/queue` — 逐条管理队列:编辑、删除、单条 steer,或向 agent 的 inbox
302
- 插入消息(编辑器上方的队列窗格显示待处理消息;`Ctrl+S` 一次性全部
303
- steer,`Alt+↑` 把全部消息拉回编辑器)。
304
307
  - `/status` — 显示当前会话的统计与身份信息(回合数、token 用量、
305
308
  工作区、已安装的 dsh 版本)。
306
- - `/preset`、`/model`、`/settings`、`/export`、`/fork`、`/subagents` —
309
+ - `/preset`、`/model`、`/settings`、`/export`、`/fork` —
307
310
  见 `dsh --profile pi-tui` 的命令自动补全(`/` + Tab)。
308
311
 
309
312
  ## 快捷键(节选)
@@ -317,20 +320,25 @@ SDK。原始终端访问、pre-host 输入拦截与完整输入所有权**不属
317
320
  送入正在运行的回合;否则只发送草稿。空闲的 agent 会用全部内容开启
318
321
  新回合。
319
322
  - `Alt+↑` — 出队:把所有排队消息拉回编辑器草稿。
320
- - `Ctrl+T` — 切换完整待办列表;编辑器上方的 dock 始终显示待办摘要与
321
- 后台任务,排队的输入渲染在两者之间。
323
+ - `Ctrl+T` — 切换完整待办列表(全屏时点击面板可在完整列表与折叠之间
324
+ 切换);编辑器上方的 dock 始终显示待办摘要与后台任务,排队的输入渲染
325
+ 在两者之间。
322
326
  - `@` — 编辑器中的文件/文件夹提及:`@` + Tab 从整个工作区补全文件
323
327
  (`fd` 在 PATH 上时以其为后端,否则使用内置的递归回退)。字面
324
328
  `@path` 会被提交,由模型自行读取文件。有后台工作时,空编辑器的 `↓`
325
- 或 `Ctrl+J` 会打开覆盖两种表面的任务浏览器:
329
+ 会打开与 `/tasks` 相同的合并任务浏览器:
326
330
  - **子代理行**(可继续的实时子任务)——`Enter` 以只读方式打开子代理
327
- 的转录(`Esc` 返回);它们从不注册 job 记录,所以此浏览器是它们唯一
328
- 可一览的归宿。
331
+ 的转录(`Esc` 返回);`i` 中断选中的子代理。它们从不注册 job 记录,
332
+ 所以此浏览器是它们唯一可一览的归宿。
329
333
  - **job 行**(bash 与一次性子代理 job)——`Enter` 只显示状态查看器:
330
334
  bash job 的输出读取游标属于模型的 `job_output`,而一次性子代理 job
331
- 记录不带子会话 id,因此转录需通过 `/subagents` 访问(`s` 停止 job)。
335
+ 记录不带子会话 id,因此子代理 job 的转录需通过 `/tasks` 按标签
336
+ 挑选子代理访问。
332
337
  只要有后台工作处于活动状态,页脚徽章就会显示
333
- `[N tasks running · M agents · ↓ view]`。
338
+ `[N tasks running · M agents · ↓ view]`。编辑器上方的队列窗格显示
339
+ 待处理输入:用户消息为 `❯` 行,其余(任务通知、子代理汇报、注入的
340
+ 指令)为 `⏳` 通知行,超过五条折叠为 `+N more`——并在主 agent 接收
341
+ 后消失。
334
342
 
335
343
  ## 启动选项
336
344
 
package/cordis.patch.yml CHANGED
@@ -26,10 +26,22 @@
26
26
 
27
27
  - id: tui-app
28
28
  name: '@xmoon76/dsh-pi-tui'
29
- inject: [tuiStartup, piTuiExtensions]
29
+ inject: [tuiStartup, piTuiExtensions, authorization]
30
30
  config:
31
31
  sessionId: ''
32
32
 
33
+ # The authorization seam (`ctx.authorization`): plugin-owned credential-
34
+ # obtaining flows, one attempt per key, transport-neutral notices and
35
+ # prompts. The dsh host does NOT mount it anywhere (the base layer ships
36
+ # only `credentials` + `llm-pi-ai`), so the TUI profile inserts the row
37
+ # itself — the TUI is the interactive surface that can answer its
38
+ # prompts. `AuthorizationService` injects `credentials`, so this row
39
+ # activates only after the base's credentials row; llm-pi-ai's
40
+ # provider login flows register once both services are up (it injects
41
+ # `authorization` optionally and degrades without it).
42
+ - id: authorization
43
+ name: '@deepseek-ai/dsh-authorization'
44
+
33
45
  # The agent-preset roster. The SHIPPED root is an assembly fact of this
34
46
  # bundle: the four presets (standard/code/minimal/cordis) travel with the
35
47
  # package under config/agent-presets and carry `system` trust. The
package/dist/builtins.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TUI_STARTUP_SERVICE } from "./startup.mjs";
2
- import { M as PI_TUI_EXTENSIONS_SERVICE } from "./src-C_RdKmcj.mjs";
2
+ import { A as dshVersion, F as PI_TUI_EXTENSIONS_SERVICE } from "./src-ClAKiW0z.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  //#region src/builtins.ts
@@ -27,12 +27,13 @@ function apply(ctx) {
27
27
  if (ctx.get("tuiStartup") === void 0) return;
28
28
  const service = ctx.get(PI_TUI_EXTENSIONS_SERVICE);
29
29
  if (service === void 0) return;
30
+ const installedDsh = dshVersion();
30
31
  service.register("chrome.header.badge", {
31
32
  id: "builtin-version",
32
33
  order: 1e3,
33
- description: "The bundle version (first-party builtin)."
34
+ description: "The dsh and bundle versions (first-party builtin)."
34
35
  }, {
35
- text: `v${packageVersion()}`,
36
+ text: installedDsh === void 0 ? `tui-v${packageVersion()}` : `dsh-${installedDsh} · tui-v${packageVersion()}`,
36
37
  tone: "info"
37
38
  });
38
39
  const counters = service.register("chrome.footer.status", {
@@ -1,3 +1,3 @@
1
1
  import "./startup.mjs";
2
- import { A as inject, M as PI_TUI_EXTENSIONS_SERVICE, N as API_VERSION, P as describeKey, j as name, k as apply } from "./src-C_RdKmcj.mjs";
2
+ import { F as PI_TUI_EXTENSIONS_SERVICE, I as API_VERSION, L as describeKey, M as apply, N as inject, P as name } from "./src-ClAKiW0z.mjs";
3
3
  export { API_VERSION, PI_TUI_EXTENSIONS_SERVICE, apply, describeKey, inject, name };
package/dist/index.d.mts CHANGED
@@ -125,6 +125,16 @@ interface Config {
125
125
  sessionId?: string;
126
126
  }
127
127
  declare const Config: z<Config>;
128
+ /**
129
+ * Wire the credential surface refresh to the dsh 0.1.1-rc.1 credential
130
+ * events. The rc.1 release split the credential update event into the
131
+ * reference half and the durable-record half; both change what the footer
132
+ * model row and the welcome card show (a /login /logout, an external
133
+ * .credentials.yaml edit, or an authorization flow committing a record), so
134
+ * they share one best-effort callback. The callback must not read secrets
135
+ * or mutate providers — it only re-reads describe-level state.
136
+ */
137
+ declare function registerCredentialSurfaceRefresh(ctx: Context, refresh: () => void): void;
128
138
  /**
129
139
  * Slash commands that need no session: before the first user message
130
140
  * (deferred start) they run locally without creating one. Everything else
@@ -262,7 +272,22 @@ interface QueueNoticeSource {
262
272
  readonly form?: string;
263
273
  readonly kind?: string;
264
274
  readonly summary?: string;
275
+ /** The reporting child's session id (subagent-report relays). */
276
+ readonly senderSessionId?: string;
265
277
  }
278
+ /**
279
+ * Whether an inbox message is USER-ORIGIN input — the queue pane's steerable
280
+ * `❯` rows. Everything else is injected context, not the user's own queued
281
+ * input, and must never read as one: plugin notices (background-job
282
+ * completions), `subagent-report` relays (a child's active report, e.g.
283
+ * "Background subagent X reported:"), injected skill/agent instructions,
284
+ * goal messages. The web makes the same cut (`placement: source.kind ===
285
+ * 'user' ? 'steering' : 'context'`), and this deployment's queue pane has
286
+ * the same rule: only user-origin rows are steerable. A sourceless row
287
+ * (undefined) is treated as user input — plain rows never carry a source.
288
+ * @param source - the message source projection, or undefined for a plain row.
289
+ */
290
+ declare function isUserQueueInput(source: QueueNoticeSource | undefined): boolean;
266
291
  /**
267
292
  * Whether a plain submitted draft is the quit word: exactly `exit` (trimmed,
268
293
  * lowercase). The runner intercepts this BEFORE any session creation or
@@ -483,4 +508,4 @@ declare function recomposeBlank(ctx: Context, agent: {
483
508
  */
484
509
  declare function apply(ctx: Context, config: Config): void;
485
510
  //#endregion
486
- export { AgentComposition, CompactionFold, Config, HOST_COMMAND_CATALOG, InitialCatalogResolution, LOCAL_COMMANDS, PendingSubagentCall, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
511
+ export { AgentComposition, CompactionFold, Config, HOST_COMMAND_CATALOG, InitialCatalogResolution, LOCAL_COMMANDS, PendingSubagentCall, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, registerCredentialSurfaceRefresh, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import "./startup.mjs";
2
- import { C as shouldConsumeAdvertisedMiss, D as subagentNoticeIsFailure, E as subagentJobViewHint, O as forkSeed, S as runningProfile, T as subagentJobTranscriptId, _ as name, a as SessionLockRefusedError, b as resolveInitialCatalog, c as compactingFromLog, d as foldCompactionEvent, f as foldQueueRows, g as matchPendingSubagentCall, h as isSubagentSettlementNotice, i as SESSIONLESS_COMMANDS, l as composeAgent, m as isPlainExitPrompt, n as HOST_COMMAND_CATALOG, o as apply, p as inject, r as LOCAL_COMMANDS, s as busyAfterTurnBoundary, t as Config, u as dangerCommand, v as recomposeBlank, w as shouldSteerOnEnter, x as resumeCommand, y as recordedPreset } from "./src-C_RdKmcj.mjs";
3
- export { Config, HOST_COMMAND_CATALOG, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
2
+ import { C as resumeCommand, D as subagentJobTranscriptId, E as shouldSteerOnEnter, O as subagentJobViewHint, S as resolveInitialCatalog, T as shouldConsumeAdvertisedMiss, _ as matchPendingSubagentCall, a as SessionLockRefusedError, b as recordedPreset, c as compactingFromLog, d as foldCompactionEvent, f as foldQueueRows, g as isUserQueueInput, h as isSubagentSettlementNotice, i as SESSIONLESS_COMMANDS, j as forkSeed, k as subagentNoticeIsFailure, l as composeAgent, m as isPlainExitPrompt, n as HOST_COMMAND_CATALOG, o as apply, p as inject, r as LOCAL_COMMANDS, s as busyAfterTurnBoundary, t as Config, u as dangerCommand, v as name, w as runningProfile, x as registerCredentialSurfaceRefresh, y as recomposeBlank } from "./src-ClAKiW0z.mjs";
3
+ export { Config, HOST_COMMAND_CATALOG, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, registerCredentialSurfaceRefresh, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };