@norman-else/dsh-claude 0.1.15 → 0.1.16

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/lib/index.d.mts CHANGED
@@ -174,6 +174,9 @@ type ClaudeTurnStreamEvent = {
174
174
  } | {
175
175
  type: 'usage';
176
176
  usage: ClaudeUsage;
177
+ } | {
178
+ type: 'segment-complete';
179
+ text: string;
177
180
  } | {
178
181
  type: 'complete';
179
182
  text: string;
package/lib/index.mjs CHANGED
@@ -1007,6 +1007,11 @@ function sdkUserMessage(prompt, uuid) {
1007
1007
  uuid
1008
1008
  };
1009
1009
  }
1010
+ const BACKGROUND_TASK_REPORT_PROMPT = [
1011
+ "The background tasks launched by your preceding response have now all settled.",
1012
+ "Report their final completed or failed outcomes concisely to the user.",
1013
+ "Do not start new tools or tasks, and do not repeat the earlier progress update."
1014
+ ].join(" ");
1010
1015
  /** CLI ≥ 2.1.235 emits system/init only after its first stdin input, while
1011
1016
  * the supervisor must see init before submitting any real turn. A local
1012
1017
  * slash command nudges startup without costing a model call; its lifecycle
@@ -1125,6 +1130,7 @@ var ClaudeSupervisor = class {
1125
1130
  cursor,
1126
1131
  output: new AsyncQueue(),
1127
1132
  promptUuid,
1133
+ phase: "primary",
1128
1134
  sawActivity: false,
1129
1135
  sawTextDelta: false,
1130
1136
  text: "",
@@ -1369,7 +1375,7 @@ var ClaudeSupervisor = class {
1369
1375
  }
1370
1376
  const taskId = message.kind === "subagent" ? message.taskId : void 0;
1371
1377
  if (message.kind === "subagent" && taskId !== void 0) await this.#trackTask(entry, message, taskId, entry.active?.cursor.turn);
1372
- else if (message.kind === "background-tasks") this.#trackBackgroundLevel(entry, message.tasks, entry.active?.cursor.turn);
1378
+ else if (message.kind === "background-tasks") await this.#trackBackgroundLevel(entry, message.tasks, entry.active?.cursor.turn);
1373
1379
  if (entry.handshakePending) {
1374
1380
  if (message.kind === "protocol-error") throw new ClaudeProtocolError(`${message.title}: ${JSON.stringify(message.detail).slice(0, 1e3)}`);
1375
1381
  if (message.kind === "result") entry.handshakePending = false;
@@ -1515,10 +1521,11 @@ var ClaudeSupervisor = class {
1515
1521
  entry.tasks.set(taskId, next);
1516
1522
  const settled = next.status !== "running";
1517
1523
  await this.#scheduleTasksSnapshot(entry, settled);
1524
+ if (settled) await this.#continueAfterTasks(entry);
1518
1525
  }
1519
1526
  /** Fold the background-task level signal into the board (REPLACE semantics
1520
1527
  * for the backgrounded flag: only the listed tasks are detached). */
1521
- #trackBackgroundLevel(entry, tasks, originTurn) {
1528
+ async #trackBackgroundLevel(entry, tasks, originTurn) {
1522
1529
  const live = new Set(tasks.map((task) => task.taskId));
1523
1530
  let changed = false;
1524
1531
  for (const task of tasks) {
@@ -1549,7 +1556,28 @@ var ClaudeSupervisor = class {
1549
1556
  });
1550
1557
  changed = true;
1551
1558
  }
1552
- if (changed) this.#scheduleTasksSnapshot(entry, true);
1559
+ if (changed) {
1560
+ await this.#scheduleTasksSnapshot(entry, true);
1561
+ await this.#continueAfterTasks(entry);
1562
+ }
1563
+ }
1564
+ #hasRunningTasks(entry, active) {
1565
+ return [...entry.tasks.values()].some((task) => task.originTurn === active.cursor.turn && task.backgrounded === true && task.status === "running");
1566
+ }
1567
+ async #continueAfterTasks(entry) {
1568
+ const active = entry.active;
1569
+ if (active === void 0 || active.phase !== "waiting-tasks" || this.#hasRunningTasks(entry, active)) return;
1570
+ active.phase = "follow-up";
1571
+ active.promptUuid = randomUUID();
1572
+ active.sawTextDelta = false;
1573
+ active.text = "";
1574
+ active.thinking = "";
1575
+ await this.#appendSafely(active, {
1576
+ kind: "status",
1577
+ phase: "updated",
1578
+ title: "Claude Code is reporting background task results"
1579
+ });
1580
+ entry.input.push(sdkUserMessage(BACKGROUND_TASK_REPORT_PROMPT, active.promptUuid));
1553
1581
  }
1554
1582
  /** Persist the task board. Settled transitions flush immediately; progress
1555
1583
  * ticks throttle to one snapshot per second to bound log volume. */
@@ -1578,7 +1606,6 @@ var ClaudeSupervisor = class {
1578
1606
  }
1579
1607
  async #completeTurn(entry, active, result) {
1580
1608
  if (entry.active !== active) return;
1581
- if (active.signal !== void 0 && active.abortListener !== void 0) active.signal.removeEventListener("abort", active.abortListener);
1582
1609
  if (active.aborted) {
1583
1610
  await this.#appendSafely(active, {
1584
1611
  kind: "status",
@@ -1629,6 +1656,19 @@ var ClaudeSupervisor = class {
1629
1656
  text: result.text
1630
1657
  });
1631
1658
  }
1659
+ if (active.phase === "primary" && this.#hasRunningTasks(entry, active)) {
1660
+ active.phase = "waiting-tasks";
1661
+ await this.#appendSafely(active, {
1662
+ kind: "status",
1663
+ phase: "updated",
1664
+ title: "Claude Code is waiting for background tasks"
1665
+ });
1666
+ active.output.push({
1667
+ type: "segment-complete",
1668
+ text: active.text
1669
+ });
1670
+ return;
1671
+ }
1632
1672
  await this.#appendSafely(active, {
1633
1673
  kind: "status",
1634
1674
  phase: "completed",
@@ -1640,6 +1680,7 @@ var ClaudeSupervisor = class {
1640
1680
  });
1641
1681
  active.output.close();
1642
1682
  }
1683
+ if (active.signal !== void 0 && active.abortListener !== void 0) active.signal.removeEventListener("abort", active.abortListener);
1643
1684
  entry.active = void 0;
1644
1685
  entry.state = "idle";
1645
1686
  entry.lastUsedAt = Date.now();
@@ -2041,31 +2082,35 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
2041
2082
  let text = "";
2042
2083
  let pendingUsage;
2043
2084
  let completed = false;
2085
+ let blockIndex = 0;
2044
2086
  try {
2045
2087
  for await (const event of events) if (event.type === "text-delta") text += event.text;
2046
2088
  else if (event.type === "usage") pendingUsage = tokenUsage(event.usage);
2047
2089
  else {
2048
- completed = true;
2049
2090
  if (text.length > 0) {
2050
2091
  yield {
2051
2092
  type: "block-start",
2052
- index: 0,
2093
+ index: blockIndex,
2053
2094
  blockType: "text"
2054
2095
  };
2055
2096
  yield {
2056
2097
  type: "text-delta",
2057
- index: 0,
2098
+ index: blockIndex,
2058
2099
  text
2059
2100
  };
2060
2101
  yield {
2061
2102
  type: "block-end",
2062
- index: 0,
2103
+ index: blockIndex,
2063
2104
  block: {
2064
2105
  type: "text",
2065
2106
  text
2066
2107
  }
2067
2108
  };
2109
+ blockIndex += 1;
2068
2110
  }
2111
+ text = "";
2112
+ if (event.type === "segment-complete") continue;
2113
+ completed = true;
2069
2114
  if (pendingUsage !== void 0) yield {
2070
2115
  type: "usage",
2071
2116
  usage: pendingUsage
@@ -2081,17 +2126,17 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
2081
2126
  if (text.length > 0) {
2082
2127
  yield {
2083
2128
  type: "block-start",
2084
- index: 0,
2129
+ index: blockIndex,
2085
2130
  blockType: "text"
2086
2131
  };
2087
2132
  yield {
2088
2133
  type: "text-delta",
2089
- index: 0,
2134
+ index: blockIndex,
2090
2135
  text
2091
2136
  };
2092
2137
  yield {
2093
2138
  type: "block-end",
2094
- index: 0,
2139
+ index: blockIndex,
2095
2140
  block: {
2096
2141
  type: "text",
2097
2142
  text