@xmoon76/dsh-pi-tui 0.1.6 → 0.1.7

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.d.mts CHANGED
@@ -177,6 +177,24 @@ declare function shouldSteerOnEnter(parsed: {
177
177
  declare function shouldConsumeAdvertisedMiss(execution: {
178
178
  readonly result: unknown;
179
179
  } | undefined, wasAdvertised: boolean): boolean;
180
+ /** One unsettled subagent delegation, in tool/call order. */
181
+ interface PendingSubagentCall {
182
+ readonly callId: string;
183
+ readonly description: string;
184
+ }
185
+ /**
186
+ * Match the child a user is about to view against the unsettled subagent
187
+ * calls (pure, exported for the headless suite). The child's durable label
188
+ * is the delegation's `description`; duplicate descriptions take the MOST
189
+ * RECENT call (the one the user is most likely watching), an empty/absent
190
+ * label falls back to a LONE pending call, and no match disables the
191
+ * auto-pop (the user exits the viewer with Esc as before — never a wrong
192
+ * pop). Mutates `pending` by removing the matched call.
193
+ * @param pending - the unsettled calls, in order (oldest first).
194
+ * @param label - the child's durable label; '' or undefined = no description.
195
+ * @returns the matched call, or undefined when nothing matches.
196
+ */
197
+ declare function matchPendingSubagentCall(pending: PendingSubagentCall[], label: string | undefined): PendingSubagentCall | undefined;
180
198
  /**
181
199
  * The pre-mount surface catalog resolution:
182
200
  * - an explicit `--session` start PREFETCHES the resumed agent's effective
@@ -405,4 +423,4 @@ declare function recomposeBlank(ctx: Context, agent: {
405
423
  */
406
424
  declare function apply(ctx: Context, config: Config): void;
407
425
  //#endregion
408
- export { AgentComposition, Config, InitialCatalogResolution, LOCAL_COMMANDS, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, composeAgent, dangerCommand, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
426
+ export { AgentComposition, Config, InitialCatalogResolution, LOCAL_COMMANDS, PendingSubagentCall, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, composeAgent, dangerCommand, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
package/dist/index.mjs CHANGED
@@ -15329,6 +15329,17 @@ var TuiApp = class TuiApp {
15329
15329
  this.requestRender();
15330
15330
  }
15331
15331
  /**
15332
+ * Anchor the transcript view to its END (the latest content). Fullscreen
15333
+ * scrolls its ScrollView; the regular surface has no scroll view (it draws
15334
+ * into the terminal scrollback), so a forced full repaint re-renders from
15335
+ * row 0 and the main screen's viewport tracking lands on the bottom — the
15336
+ * same mechanism the fullscreen-toggle path uses to redraw cleanly.
15337
+ */
15338
+ scrollToBottom() {
15339
+ if (this.fullscreenScroll !== void 0) this.fullscreenScroll.scrollToEnd();
15340
+ else this.tui.requestRender(true);
15341
+ }
15342
+ /**
15332
15343
  * Set the live session's auto-generated title (from the session/title
15333
15344
  * log) for the header; undefined clears it.
15334
15345
  */
@@ -17718,7 +17729,7 @@ function registerTuiCommands(runner, initial) {
17718
17729
  text: `skill "${name}" returned a malformed definition`
17719
17730
  };
17720
17731
  const body = typeof skill.content === "string" && skill.content !== "" ? skill.content : skill.description;
17721
- agent.inject(createUserMessage({
17732
+ const message = createUserMessage({
17722
17733
  content: [{
17723
17734
  type: "text",
17724
17735
  text: "Skill loaded by the user: **" + skill.name + "**\n\n" + body
@@ -17727,7 +17738,9 @@ function registerTuiCommands(runner, initial) {
17727
17738
  kind: "plugin",
17728
17739
  plugin: "tui-skill"
17729
17740
  }
17730
- }));
17741
+ });
17742
+ if (agent.status === "running") agent.steer(message);
17743
+ else agent.followup(message);
17731
17744
  return {
17732
17745
  kind: "success",
17733
17746
  text: "skill " + name + " loaded"
@@ -20443,6 +20456,26 @@ function shouldSteerOnEnter(parsed, running, busyEnter, forceQueue) {
20443
20456
  function shouldConsumeAdvertisedMiss(execution, wasAdvertised) {
20444
20457
  return execution === void 0 && wasAdvertised;
20445
20458
  }
20459
+ /**
20460
+ * Match the child a user is about to view against the unsettled subagent
20461
+ * calls (pure, exported for the headless suite). The child's durable label
20462
+ * is the delegation's `description`; duplicate descriptions take the MOST
20463
+ * RECENT call (the one the user is most likely watching), an empty/absent
20464
+ * label falls back to a LONE pending call, and no match disables the
20465
+ * auto-pop (the user exits the viewer with Esc as before — never a wrong
20466
+ * pop). Mutates `pending` by removing the matched call.
20467
+ * @param pending - the unsettled calls, in order (oldest first).
20468
+ * @param label - the child's durable label; '' or undefined = no description.
20469
+ * @returns the matched call, or undefined when nothing matches.
20470
+ */
20471
+ function matchPendingSubagentCall(pending, label) {
20472
+ if (label !== void 0 && label !== "") {
20473
+ for (let index = pending.length - 1; index >= 0; index -= 1) if (pending[index].description === label) return pending.splice(index, 1)[0];
20474
+ if (pending.length === 1) return pending.splice(0, 1)[0];
20475
+ return;
20476
+ }
20477
+ if (pending.length === 1) return pending.splice(0, 1)[0];
20478
+ }
20446
20479
  async function resolveInitialCatalog(options) {
20447
20480
  const { liveAgent, presetId, signal, ctx, diag } = options;
20448
20481
  if (liveAgent !== void 0) try {
@@ -21564,6 +21597,8 @@ function apply(ctx, config) {
21564
21597
  };
21565
21598
  let repaintTimer;
21566
21599
  let viewing;
21600
+ const pendingSubagentCalls = [];
21601
+ const viewCallToChild = /* @__PURE__ */ new Map();
21567
21602
  const activeFolder = () => viewing?.folder ?? folder;
21568
21603
  const paintNow = () => {
21569
21604
  if (repaintTimer !== void 0) {
@@ -21586,6 +21621,8 @@ function apply(ctx, config) {
21586
21621
  const bumpSessionGeneration = () => {
21587
21622
  sessionGeneration += 1;
21588
21623
  callArgs.clear();
21624
+ pendingSubagentCalls.length = 0;
21625
+ viewCallToChild.clear();
21589
21626
  searchMatches = [];
21590
21627
  searchCurrent = -1;
21591
21628
  app.setSearchResult(0, 0);
@@ -21613,6 +21650,8 @@ function apply(ctx, config) {
21613
21650
  childFolder.apply((await persistence.inspect(childId)).events);
21614
21651
  } catch {}
21615
21652
  }
21653
+ const matched = matchPendingSubagentCall(pendingSubagentCalls, label);
21654
+ if (matched !== void 0) viewCallToChild.set(matched.callId, childId);
21616
21655
  viewing = {
21617
21656
  id: childId,
21618
21657
  folder: childFolder
@@ -21631,6 +21670,7 @@ function apply(ctx, config) {
21631
21670
  app.clearNotify();
21632
21671
  app.setViewerMode(void 0);
21633
21672
  repaint(app, folder);
21673
+ app.scrollToBottom();
21634
21674
  refreshStatus();
21635
21675
  return true;
21636
21676
  };
@@ -22518,17 +22558,42 @@ function apply(ctx, config) {
22518
22558
  ctx.on("session/event", (session, event) => {
22519
22559
  if (liveAgent === void 0) return;
22520
22560
  if (viewing !== void 0) {
22521
- if (session.id !== viewing.id) return;
22522
- viewing.folder.apply([event]);
22523
- schedulePaint();
22524
- if (event.type === "turn/end") paintNow();
22525
- return;
22561
+ if (session.id === viewing.id) {
22562
+ viewing.folder.apply([event]);
22563
+ schedulePaint();
22564
+ if (event.type === "turn/end") paintNow();
22565
+ return;
22566
+ }
22526
22567
  }
22527
22568
  if (session.id !== liveAgent.session.id) return;
22528
22569
  if (event.type === "tool/call") {
22529
22570
  callArgs.set(event.data.callId, typeof event.data.arguments === "string" ? event.data.arguments : JSON.stringify(event.data.arguments));
22530
- if (typeof event.data.name === "string" && event.data.name.startsWith("subagent")) refreshAgents();
22531
- } else if (event.type === "tool/result") callArgs.delete(event.data.message.content[0]?.toolCallId ?? "");
22571
+ if (typeof event.data.name === "string" && event.data.name.startsWith("subagent")) {
22572
+ refreshAgents();
22573
+ let description = "";
22574
+ try {
22575
+ const parsed = JSON.parse(event.data.arguments);
22576
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.description === "string") description = parsed.description;
22577
+ } catch {}
22578
+ pendingSubagentCalls.push({
22579
+ callId: event.data.callId,
22580
+ description
22581
+ });
22582
+ }
22583
+ } else if (event.type === "tool/result") {
22584
+ const callId = event.data.message.content[0]?.toolCallId;
22585
+ callArgs.delete(callId ?? "");
22586
+ const callIndex = pendingSubagentCalls.findIndex((call) => call.callId === callId);
22587
+ if (callIndex !== -1) pendingSubagentCalls.splice(callIndex, 1);
22588
+ const childId = callId === void 0 ? void 0 : viewCallToChild.get(callId);
22589
+ if (childId !== void 0) viewCallToChild.delete(callId);
22590
+ if (childId !== void 0 && viewing !== void 0 && viewing.id === childId) {
22591
+ folder.apply([event]);
22592
+ statsFolder.apply([event]);
22593
+ exitView();
22594
+ return;
22595
+ }
22596
+ }
22532
22597
  folder.apply([event]);
22533
22598
  statsFolder.apply([event]);
22534
22599
  if (event.type === "goal/change") goalText = foldGoal([event]);
@@ -22620,4 +22685,4 @@ function apply(ctx, config) {
22620
22685
  });
22621
22686
  }
22622
22687
  //#endregion
22623
- export { Config, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, composeAgent, dangerCommand, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
22688
+ export { Config, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, composeAgent, dangerCommand, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmoon76/dsh-pi-tui",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "The dsh-pi-tui bundle: a TUI surface for DeepSeek Harness profiles (dsh --profile pi-tui)",
5
5
  "license": "MIT",
6
6
  "author": "XMoon <xmoonox@gmail.com>",