@sideboard-ai/core 0.1.244 → 0.1.246

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.
@@ -46,7 +46,7 @@ import {
46
46
  resolveLoginCommand,
47
47
  resolveQuotaFallbackAgent,
48
48
  whichOnPath
49
- } from "./chunk-CYYQ6577.js";
49
+ } from "./chunk-RLNEKXXT.js";
50
50
  import "./chunk-GFRC6JQN.js";
51
51
  import "./chunk-E6C5NR46.js";
52
52
  import "./chunk-6K64FJDD.js";
@@ -55,7 +55,7 @@ import {
55
55
  resolveQuotaFallbackAgent,
56
56
  turnCostUsdFromCursorUsage,
57
57
  whichOnPath
58
- } from "./chunk-2QRU4IFI.js";
58
+ } from "./chunk-HRWCW6LC.js";
59
59
  import "./chunk-576S3HJ7.js";
60
60
  import "./chunk-J5GON4SU.js";
61
61
  import "./chunk-P2BSMNHB.js";
@@ -1697,23 +1697,38 @@ function looksLikeComposerSkillExpansion(text) {
1697
1697
  const t = text.trim();
1698
1698
  return /^## Skill: \//m.test(t) && /Follow this skill for the request above:/i.test(t);
1699
1699
  }
1700
+ var SKILL_FRONTMATTER_NAME = /^\s*name\s*:\s*['"]?([A-Za-z0-9][A-Za-z0-9._-]*)['"]?\s*$/m;
1701
+ function skillNameFromFrontmatter(text) {
1702
+ const t = text.trim();
1703
+ if (!t) return void 0;
1704
+ let yaml = t;
1705
+ if (!yaml.startsWith("---")) {
1706
+ const at = t.search(/(?:^|\n)---(?:\r?\n)/);
1707
+ if (at < 0 || at > 800) return void 0;
1708
+ yaml = t.slice(at).replace(/^\n/, "");
1709
+ }
1710
+ if (!yaml.startsWith("---")) return void 0;
1711
+ const close = yaml.indexOf("\n---", 3);
1712
+ if (close < 0) return void 0;
1713
+ return yaml.slice(3, close).match(SKILL_FRONTMATTER_NAME)?.[1];
1714
+ }
1715
+ function skillCommandFromBody(text) {
1716
+ return skillNameFromFrontmatter(text) ?? text.match(/^## Skill: \/([A-Za-z0-9][A-Za-z0-9._-]*)/m)?.[1] ?? text.match(/^Launching skill:\s*\/?([A-Za-z0-9][A-Za-z0-9._-]*)/im)?.[1];
1717
+ }
1700
1718
  function looksLikeSkillBody(text) {
1701
1719
  const t = text.trim();
1702
1720
  if (!t) return false;
1703
1721
  if (looksLikeComposerSkillExpansion(t)) return true;
1704
- return skillNameFromFrontmatter(t) != null;
1722
+ if (skillNameFromFrontmatter(t) != null) return true;
1723
+ return /^Launching skill:/im.test(t) && /Base directory for this skill:/i.test(t);
1705
1724
  }
1706
- function skillNameFromFrontmatter(text) {
1725
+ function looksLikeIncompleteSkillDump(text) {
1707
1726
  const t = text.trim();
1708
- if (!t.startsWith("---")) return void 0;
1709
- const close = t.indexOf("\n---", 3);
1710
- if (close < 0) return void 0;
1711
- const fm = t.slice(3, close);
1712
- const m = fm.match(/^\s*name\s*:\s*['"]?([A-Za-z0-9][A-Za-z0-9._-]*)['"]?\s*$/m);
1713
- return m?.[1];
1714
- }
1715
- function skillCommandFromBody(text) {
1716
- return skillNameFromFrontmatter(text) ?? text.match(/^## Skill: \/([A-Za-z0-9][A-Za-z0-9._-]*)/m)?.[1];
1727
+ if (!t || looksLikeSkillBody(t)) return false;
1728
+ if (/^## Skill: \//m.test(t)) return true;
1729
+ if (/^Launching skill:/im.test(t)) return true;
1730
+ if (/Base directory for this skill:/i.test(t)) return true;
1731
+ return t.startsWith("---") && SKILL_FRONTMATTER_NAME.test(t);
1717
1732
  }
1718
1733
  function isCompactToolName(name) {
1719
1734
  const short = (name ?? "").replace(/^mcp__[^_]+__/, "").trim();
@@ -2032,22 +2047,78 @@ function withPartTimes(next, prev) {
2032
2047
  const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
2033
2048
  return { ...next, startedAt, updatedAt: now };
2034
2049
  }
2035
- function skillToolFromTextPart(part, id) {
2050
+ function skillToolFromTextPart(part, id, status = "done") {
2036
2051
  const command = skillCommandFromBody(part.text);
2037
2052
  const input = command ? { skill: command } : void 0;
2038
2053
  return {
2039
2054
  type: "tool",
2040
- id,
2055
+ id: command ? `skill-${command}` : id,
2041
2056
  name: "Skill",
2042
2057
  input,
2043
2058
  description: toolDescription("Skill", input),
2044
- status: "done",
2059
+ status,
2045
2060
  result: clipToolResultForStore(part.text),
2046
2061
  ...part.parentId ? { parentId: part.parentId } : {},
2047
2062
  startedAt: part.startedAt,
2048
2063
  updatedAt: Date.now()
2049
2064
  };
2050
2065
  }
2066
+ function lastSkillToolIndex(parts, parentId) {
2067
+ for (let i = parts.length - 1; i >= 0; i--) {
2068
+ const p = parts[i];
2069
+ if (p.type === "tool" && isSkillToolName(p.name) && sameParentId(p.parentId, parentId)) {
2070
+ return i;
2071
+ }
2072
+ }
2073
+ return -1;
2074
+ }
2075
+ function lastRunningSkillIndex(parts, parentId) {
2076
+ for (let i = parts.length - 1; i >= 0; i--) {
2077
+ const p = parts[i];
2078
+ if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
2079
+ return i;
2080
+ }
2081
+ }
2082
+ return -1;
2083
+ }
2084
+ function markRunningSkillsDone(parts, parentId) {
2085
+ return parts.map((p) => {
2086
+ if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
2087
+ return withPartTimes({ ...p, status: "done" }, p);
2088
+ }
2089
+ return p;
2090
+ });
2091
+ }
2092
+ function promoteOrFoldSkillText(parts, textIdx) {
2093
+ const acc = parts[textIdx];
2094
+ if (acc?.type !== "text") return parts;
2095
+ const complete = looksLikeSkillBody(acc.text);
2096
+ const incomplete = looksLikeIncompleteSkillDump(acc.text);
2097
+ if (!complete && !incomplete) return parts;
2098
+ const existing = lastSkillToolIndex(parts, acc.parentId);
2099
+ if (existing >= 0 && existing !== textIdx) {
2100
+ const prev = parts[existing];
2101
+ const next2 = parts.filter((_, i) => i !== textIdx);
2102
+ const existingIdx = lastSkillToolIndex(next2, acc.parentId);
2103
+ if (existingIdx < 0) return parts;
2104
+ next2[existingIdx] = withPartTimes(
2105
+ {
2106
+ ...prev,
2107
+ status: complete ? "done" : prev.status,
2108
+ result: clipToolResultForStore(acc.text) ?? prev.result
2109
+ },
2110
+ prev
2111
+ );
2112
+ return next2;
2113
+ }
2114
+ const next = [...parts];
2115
+ next[textIdx] = skillToolFromTextPart(
2116
+ acc,
2117
+ `skill-${next.length}`,
2118
+ complete ? "done" : "running"
2119
+ );
2120
+ return next;
2121
+ }
2051
2122
  function compactSummaryPart(text, id = "compact-summary") {
2052
2123
  return compactToolPart(text, id, { status: "done" });
2053
2124
  }
@@ -2136,6 +2207,24 @@ function applyAgentEvent(parts, event) {
2136
2207
  })
2137
2208
  ];
2138
2209
  }
2210
+ const runningSkillIdx = lastRunningSkillIndex(parts, event.parentId);
2211
+ if (runningSkillIdx >= 0) {
2212
+ const prev = parts[runningSkillIdx];
2213
+ const result = `${prev.result ?? ""}${data}`;
2214
+ const next2 = [...parts];
2215
+ next2[runningSkillIdx] = withPartTimes(
2216
+ {
2217
+ ...prev,
2218
+ result: clipToolResultForStore(result),
2219
+ // Stay running after the YAML `name:` fence so later content_block_delta
2220
+ // tokens of SKILL.md stay on this row. thinking / tool_use / finalizeParts
2221
+ // mark the row done so the real reply can be chat.
2222
+ status: "running"
2223
+ },
2224
+ prev
2225
+ );
2226
+ return next2;
2227
+ }
2139
2228
  const next = [...parts];
2140
2229
  const last = next[next.length - 1];
2141
2230
  if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
@@ -2157,9 +2246,10 @@ function applyAgentEvent(parts, event) {
2157
2246
  );
2158
2247
  }
2159
2248
  const acc = next[next.length - 1];
2160
- if (acc?.type === "text" && looksLikeComposerSkillExpansion(acc.text)) {
2161
- next[next.length - 1] = skillToolFromTextPart(acc, `skill-${next.length}`);
2162
- } else if (acc?.type === "text" && looksLikeConversationSummary(acc.text)) {
2249
+ if (acc?.type === "text" && (looksLikeSkillBody(acc.text) || looksLikeIncompleteSkillDump(acc.text))) {
2250
+ return promoteOrFoldSkillText(next, next.length - 1);
2251
+ }
2252
+ if (acc?.type === "text" && looksLikeConversationSummary(acc.text)) {
2163
2253
  next[next.length - 1] = compactToolPart(acc.text, `compact-${next.length}`, {
2164
2254
  parentId: acc.parentId,
2165
2255
  status: "done",
@@ -2171,6 +2261,7 @@ function applyAgentEvent(parts, event) {
2171
2261
  if (event.type === "thinking") {
2172
2262
  const data = event.data;
2173
2263
  if (!data) return parts;
2264
+ parts = markRunningSkillsDone(parts, event.parentId);
2174
2265
  const next = [...parts];
2175
2266
  if (event.replace) {
2176
2267
  for (let i = next.length - 1; i >= 0; i--) {
@@ -2218,6 +2309,7 @@ function applyAgentEvent(parts, event) {
2218
2309
  return withCompactFinish(next, data, event.parentId);
2219
2310
  }
2220
2311
  if (event.type === "tool_use") {
2312
+ parts = markRunningSkillsDone(parts, event.parentId);
2221
2313
  const input = asRecord(event.input);
2222
2314
  const diff = diffFromInput(input);
2223
2315
  const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
@@ -2534,13 +2626,17 @@ function eventsFromInjectedUserText(text, parentId) {
2534
2626
  if (looksLikeConversationSummary(text)) return compactToolEventsFromBody(text, parentId);
2535
2627
  return [];
2536
2628
  }
2629
+ function eventsFromAssistantText(text, parentId) {
2630
+ if (looksLikeSkillBody(text)) return skillToolEventsFromBody(text, parentId);
2631
+ return [withEventParentId({ type: "stdout", data: text }, parentId)];
2632
+ }
2537
2633
  function eventsFromUserContent(content, parentId) {
2538
2634
  if (typeof content === "string" && content.trim()) {
2539
2635
  return eventsFromInjectedUserText(content, parentId);
2540
2636
  }
2541
2637
  if (!Array.isArray(content) || content.length === 0) return [];
2542
2638
  const out = [];
2543
- const textBlocks = [];
2639
+ const injected = [];
2544
2640
  for (const block of content) {
2545
2641
  if (!block?.type) continue;
2546
2642
  if (block.type === "tool_use" && block.id && block.name) {
@@ -2571,13 +2667,12 @@ function eventsFromUserContent(content, parentId) {
2571
2667
  );
2572
2668
  continue;
2573
2669
  }
2574
- if (block.type === "text" && block.text) textBlocks.push(block.text);
2575
- }
2576
- if (out.length > 0) return out;
2577
- for (const text of textBlocks) {
2578
- out.push(...eventsFromInjectedUserText(text, parentId));
2670
+ if (block.type === "text" && block.text) {
2671
+ injected.push(...eventsFromInjectedUserText(block.text, parentId));
2672
+ }
2579
2673
  }
2580
- return out;
2674
+ if (out.length === 0 && injected.length === 0) return [];
2675
+ return [...out, ...injected];
2581
2676
  }
2582
2677
  function eventsFromContentBlocks(blocks, parentId) {
2583
2678
  if (!blocks?.length) return [];
@@ -2585,7 +2680,7 @@ function eventsFromContentBlocks(blocks, parentId) {
2585
2680
  for (const block of blocks) {
2586
2681
  if (!block?.type) continue;
2587
2682
  if (block.type === "text" && block.text) {
2588
- out.push(withEventParentId({ type: "stdout", data: block.text }, parentId));
2683
+ out.push(...eventsFromAssistantText(block.text, parentId));
2589
2684
  continue;
2590
2685
  }
2591
2686
  if ((block.type === "thinking" || block.type === "redacted_thinking") && block.thinking) {
@@ -3014,7 +3109,9 @@ var claudeAdapter = {
3014
3109
  events.push({ type: "stderr", data: errorDetail });
3015
3110
  } else {
3016
3111
  const text = obj.result;
3017
- if (typeof text === "string" && text) events.push({ type: "stdout", data: text });
3112
+ if (typeof text === "string" && text) {
3113
+ events.push(...eventsFromAssistantText(text));
3114
+ }
3018
3115
  }
3019
3116
  const usage = usageFromClaude(obj.usage);
3020
3117
  if (usage) {
@@ -25,7 +25,7 @@ import {
25
25
  resolveQuotaFallbackAgent,
26
26
  stripBrightsyNdjsonNoise,
27
27
  toolDescription
28
- } from "./chunk-CYYQ6577.js";
28
+ } from "./chunk-RLNEKXXT.js";
29
29
  import {
30
30
  SLACK_REPLY_FORMATTING,
31
31
  addWorkspace,
@@ -558,9 +558,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
558
558
  if (assistantText && (parsed.data === assistantText || assistantText.endsWith(parsed.data))) {
559
559
  continue;
560
560
  }
561
- assistantText += parsed.data;
562
561
  }
563
562
  parts = applyAgentEvent(parts, parsed);
563
+ if (parsed.type === "stdout") {
564
+ assistantText = partsToAssistantText(parts);
565
+ }
564
566
  outbound.push(parsed);
565
567
  }
566
568
  });
@@ -4221,7 +4223,7 @@ async function createThread(input, _onSetupLine) {
4221
4223
  return readThread(thread.id) ?? thread;
4222
4224
  }
4223
4225
  async function listLinearIssues(agent, repoPath) {
4224
- const { getAdapter: getAdapter2 } = await import("./agents-Q23R4WOR.js");
4226
+ const { getAdapter: getAdapter2 } = await import("./agents-47WI5IV2.js");
4225
4227
  await requireAgent(agent, { requireLinear: true });
4226
4228
  const adapter = getAdapter2(agent);
4227
4229
  if (!adapter.listLinearIssues) {
@@ -7406,7 +7408,7 @@ function formatScheduledPrompt(name, prompt) {
7406
7408
  ${prompt}`;
7407
7409
  }
7408
7410
  async function defaultDeps() {
7409
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ABF6QFE6.js");
7411
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-2QGJ7CW2.js");
7410
7412
  const orch = getOrchestrator2();
7411
7413
  return {
7412
7414
  findThread: (id) => findThreadByRef(id),
@@ -1187,23 +1187,38 @@ function looksLikeComposerSkillExpansion(text) {
1187
1187
  const t = text.trim();
1188
1188
  return /^## Skill: \//m.test(t) && /Follow this skill for the request above:/i.test(t);
1189
1189
  }
1190
+ var SKILL_FRONTMATTER_NAME = /^\s*name\s*:\s*['"]?([A-Za-z0-9][A-Za-z0-9._-]*)['"]?\s*$/m;
1191
+ function skillNameFromFrontmatter(text) {
1192
+ const t = text.trim();
1193
+ if (!t) return void 0;
1194
+ let yaml = t;
1195
+ if (!yaml.startsWith("---")) {
1196
+ const at = t.search(/(?:^|\n)---(?:\r?\n)/);
1197
+ if (at < 0 || at > 800) return void 0;
1198
+ yaml = t.slice(at).replace(/^\n/, "");
1199
+ }
1200
+ if (!yaml.startsWith("---")) return void 0;
1201
+ const close = yaml.indexOf("\n---", 3);
1202
+ if (close < 0) return void 0;
1203
+ return yaml.slice(3, close).match(SKILL_FRONTMATTER_NAME)?.[1];
1204
+ }
1205
+ function skillCommandFromBody(text) {
1206
+ return skillNameFromFrontmatter(text) ?? text.match(/^## Skill: \/([A-Za-z0-9][A-Za-z0-9._-]*)/m)?.[1] ?? text.match(/^Launching skill:\s*\/?([A-Za-z0-9][A-Za-z0-9._-]*)/im)?.[1];
1207
+ }
1190
1208
  function looksLikeSkillBody(text) {
1191
1209
  const t = text.trim();
1192
1210
  if (!t) return false;
1193
1211
  if (looksLikeComposerSkillExpansion(t)) return true;
1194
- return skillNameFromFrontmatter(t) != null;
1212
+ if (skillNameFromFrontmatter(t) != null) return true;
1213
+ return /^Launching skill:/im.test(t) && /Base directory for this skill:/i.test(t);
1195
1214
  }
1196
- function skillNameFromFrontmatter(text) {
1215
+ function looksLikeIncompleteSkillDump(text) {
1197
1216
  const t = text.trim();
1198
- if (!t.startsWith("---")) return void 0;
1199
- const close = t.indexOf("\n---", 3);
1200
- if (close < 0) return void 0;
1201
- const fm = t.slice(3, close);
1202
- const m = fm.match(/^\s*name\s*:\s*['"]?([A-Za-z0-9][A-Za-z0-9._-]*)['"]?\s*$/m);
1203
- return m?.[1];
1204
- }
1205
- function skillCommandFromBody(text) {
1206
- return skillNameFromFrontmatter(text) ?? text.match(/^## Skill: \/([A-Za-z0-9][A-Za-z0-9._-]*)/m)?.[1];
1217
+ if (!t || looksLikeSkillBody(t)) return false;
1218
+ if (/^## Skill: \//m.test(t)) return true;
1219
+ if (/^Launching skill:/im.test(t)) return true;
1220
+ if (/Base directory for this skill:/i.test(t)) return true;
1221
+ return t.startsWith("---") && SKILL_FRONTMATTER_NAME.test(t);
1207
1222
  }
1208
1223
  function isCompactToolName(name) {
1209
1224
  const short = (name ?? "").replace(/^mcp__[^_]+__/, "").trim();
@@ -1605,22 +1620,78 @@ function withPartTimes(next, prev) {
1605
1620
  const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
1606
1621
  return { ...next, startedAt, updatedAt: now };
1607
1622
  }
1608
- function skillToolFromTextPart(part, id) {
1623
+ function skillToolFromTextPart(part, id, status = "done") {
1609
1624
  const command = skillCommandFromBody(part.text);
1610
1625
  const input = command ? { skill: command } : void 0;
1611
1626
  return {
1612
1627
  type: "tool",
1613
- id,
1628
+ id: command ? `skill-${command}` : id,
1614
1629
  name: "Skill",
1615
1630
  input,
1616
1631
  description: toolDescription("Skill", input),
1617
- status: "done",
1632
+ status,
1618
1633
  result: clipToolResultForStore(part.text),
1619
1634
  ...part.parentId ? { parentId: part.parentId } : {},
1620
1635
  startedAt: part.startedAt,
1621
1636
  updatedAt: Date.now()
1622
1637
  };
1623
1638
  }
1639
+ function lastSkillToolIndex(parts, parentId) {
1640
+ for (let i = parts.length - 1; i >= 0; i--) {
1641
+ const p = parts[i];
1642
+ if (p.type === "tool" && isSkillToolName(p.name) && sameParentId(p.parentId, parentId)) {
1643
+ return i;
1644
+ }
1645
+ }
1646
+ return -1;
1647
+ }
1648
+ function lastRunningSkillIndex(parts, parentId) {
1649
+ for (let i = parts.length - 1; i >= 0; i--) {
1650
+ const p = parts[i];
1651
+ if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
1652
+ return i;
1653
+ }
1654
+ }
1655
+ return -1;
1656
+ }
1657
+ function markRunningSkillsDone(parts, parentId) {
1658
+ return parts.map((p) => {
1659
+ if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
1660
+ return withPartTimes({ ...p, status: "done" }, p);
1661
+ }
1662
+ return p;
1663
+ });
1664
+ }
1665
+ function promoteOrFoldSkillText(parts, textIdx) {
1666
+ const acc = parts[textIdx];
1667
+ if (acc?.type !== "text") return parts;
1668
+ const complete = looksLikeSkillBody(acc.text);
1669
+ const incomplete = looksLikeIncompleteSkillDump(acc.text);
1670
+ if (!complete && !incomplete) return parts;
1671
+ const existing = lastSkillToolIndex(parts, acc.parentId);
1672
+ if (existing >= 0 && existing !== textIdx) {
1673
+ const prev = parts[existing];
1674
+ const next2 = parts.filter((_, i) => i !== textIdx);
1675
+ const existingIdx = lastSkillToolIndex(next2, acc.parentId);
1676
+ if (existingIdx < 0) return parts;
1677
+ next2[existingIdx] = withPartTimes(
1678
+ {
1679
+ ...prev,
1680
+ status: complete ? "done" : prev.status,
1681
+ result: clipToolResultForStore(acc.text) ?? prev.result
1682
+ },
1683
+ prev
1684
+ );
1685
+ return next2;
1686
+ }
1687
+ const next = [...parts];
1688
+ next[textIdx] = skillToolFromTextPart(
1689
+ acc,
1690
+ `skill-${next.length}`,
1691
+ complete ? "done" : "running"
1692
+ );
1693
+ return next;
1694
+ }
1624
1695
  function compactSummaryPart(text, id = "compact-summary") {
1625
1696
  return compactToolPart(text, id, { status: "done" });
1626
1697
  }
@@ -1716,6 +1787,24 @@ function applyAgentEvent(parts, event) {
1716
1787
  })
1717
1788
  ];
1718
1789
  }
1790
+ const runningSkillIdx = lastRunningSkillIndex(parts, event.parentId);
1791
+ if (runningSkillIdx >= 0) {
1792
+ const prev = parts[runningSkillIdx];
1793
+ const result = `${prev.result ?? ""}${data}`;
1794
+ const next2 = [...parts];
1795
+ next2[runningSkillIdx] = withPartTimes(
1796
+ {
1797
+ ...prev,
1798
+ result: clipToolResultForStore(result),
1799
+ // Stay running after the YAML `name:` fence so later content_block_delta
1800
+ // tokens of SKILL.md stay on this row. thinking / tool_use / finalizeParts
1801
+ // mark the row done so the real reply can be chat.
1802
+ status: "running"
1803
+ },
1804
+ prev
1805
+ );
1806
+ return next2;
1807
+ }
1719
1808
  const next = [...parts];
1720
1809
  const last = next[next.length - 1];
1721
1810
  if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
@@ -1737,9 +1826,10 @@ function applyAgentEvent(parts, event) {
1737
1826
  );
1738
1827
  }
1739
1828
  const acc = next[next.length - 1];
1740
- if (acc?.type === "text" && looksLikeComposerSkillExpansion(acc.text)) {
1741
- next[next.length - 1] = skillToolFromTextPart(acc, `skill-${next.length}`);
1742
- } else if (acc?.type === "text" && looksLikeConversationSummary(acc.text)) {
1829
+ if (acc?.type === "text" && (looksLikeSkillBody(acc.text) || looksLikeIncompleteSkillDump(acc.text))) {
1830
+ return promoteOrFoldSkillText(next, next.length - 1);
1831
+ }
1832
+ if (acc?.type === "text" && looksLikeConversationSummary(acc.text)) {
1743
1833
  next[next.length - 1] = compactToolPart(acc.text, `compact-${next.length}`, {
1744
1834
  parentId: acc.parentId,
1745
1835
  status: "done",
@@ -1751,6 +1841,7 @@ function applyAgentEvent(parts, event) {
1751
1841
  if (event.type === "thinking") {
1752
1842
  const data = event.data;
1753
1843
  if (!data) return parts;
1844
+ parts = markRunningSkillsDone(parts, event.parentId);
1754
1845
  const next = [...parts];
1755
1846
  if (event.replace) {
1756
1847
  for (let i = next.length - 1; i >= 0; i--) {
@@ -1798,6 +1889,7 @@ function applyAgentEvent(parts, event) {
1798
1889
  return withCompactFinish(next, data, event.parentId);
1799
1890
  }
1800
1891
  if (event.type === "tool_use") {
1892
+ parts = markRunningSkillsDone(parts, event.parentId);
1801
1893
  const input = asRecord(event.input);
1802
1894
  const diff = diffFromInput(input);
1803
1895
  const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
@@ -1908,6 +2000,12 @@ function partsToAssistantText(parts) {
1908
2000
  (p) => p.type === "text" && !messagePartParentId(p)
1909
2001
  ).map((p) => p.text).join("").trim();
1910
2002
  }
2003
+ function visibleAssistantText(text, parts) {
2004
+ const fromParts = partsToAssistantText(parts ?? []);
2005
+ if (fromParts) return fromParts;
2006
+ if (parts?.some((p) => p.type === "tool" || p.type === "thinking")) return "";
2007
+ return (text ?? "").trim();
2008
+ }
1911
2009
  function lastAssistantMessageText(parts) {
1912
2010
  if (!parts?.length) return "";
1913
2011
  for (let i = parts.length - 1; i >= 0; i--) {
@@ -2114,13 +2212,17 @@ function eventsFromInjectedUserText(text, parentId) {
2114
2212
  if (looksLikeConversationSummary(text)) return compactToolEventsFromBody(text, parentId);
2115
2213
  return [];
2116
2214
  }
2215
+ function eventsFromAssistantText(text, parentId) {
2216
+ if (looksLikeSkillBody(text)) return skillToolEventsFromBody(text, parentId);
2217
+ return [withEventParentId({ type: "stdout", data: text }, parentId)];
2218
+ }
2117
2219
  function eventsFromUserContent(content, parentId) {
2118
2220
  if (typeof content === "string" && content.trim()) {
2119
2221
  return eventsFromInjectedUserText(content, parentId);
2120
2222
  }
2121
2223
  if (!Array.isArray(content) || content.length === 0) return [];
2122
2224
  const out = [];
2123
- const textBlocks = [];
2225
+ const injected = [];
2124
2226
  for (const block of content) {
2125
2227
  if (!block?.type) continue;
2126
2228
  if (block.type === "tool_use" && block.id && block.name) {
@@ -2151,13 +2253,12 @@ function eventsFromUserContent(content, parentId) {
2151
2253
  );
2152
2254
  continue;
2153
2255
  }
2154
- if (block.type === "text" && block.text) textBlocks.push(block.text);
2155
- }
2156
- if (out.length > 0) return out;
2157
- for (const text of textBlocks) {
2158
- out.push(...eventsFromInjectedUserText(text, parentId));
2256
+ if (block.type === "text" && block.text) {
2257
+ injected.push(...eventsFromInjectedUserText(block.text, parentId));
2258
+ }
2159
2259
  }
2160
- return out;
2260
+ if (out.length === 0 && injected.length === 0) return [];
2261
+ return [...out, ...injected];
2161
2262
  }
2162
2263
  function eventsFromContentBlocks(blocks, parentId) {
2163
2264
  if (!blocks?.length) return [];
@@ -2165,7 +2266,7 @@ function eventsFromContentBlocks(blocks, parentId) {
2165
2266
  for (const block of blocks) {
2166
2267
  if (!block?.type) continue;
2167
2268
  if (block.type === "text" && block.text) {
2168
- out.push(withEventParentId({ type: "stdout", data: block.text }, parentId));
2269
+ out.push(...eventsFromAssistantText(block.text, parentId));
2169
2270
  continue;
2170
2271
  }
2171
2272
  if ((block.type === "thinking" || block.type === "redacted_thinking") && block.thinking) {
@@ -2594,7 +2695,9 @@ var claudeAdapter = {
2594
2695
  events.push({ type: "stderr", data: errorDetail });
2595
2696
  } else {
2596
2697
  const text = obj.result;
2597
- if (typeof text === "string" && text) events.push({ type: "stdout", data: text });
2698
+ if (typeof text === "string" && text) {
2699
+ events.push(...eventsFromAssistantText(text));
2700
+ }
2598
2701
  }
2599
2702
  const usage = usageFromClaude(obj.usage);
2600
2703
  if (usage) {
@@ -4419,9 +4522,10 @@ export {
4419
4522
  isSkillToolName,
4420
4523
  skillCommandFromInput,
4421
4524
  looksLikeComposerSkillExpansion,
4422
- looksLikeSkillBody,
4423
4525
  skillNameFromFrontmatter,
4424
4526
  skillCommandFromBody,
4527
+ looksLikeSkillBody,
4528
+ looksLikeIncompleteSkillDump,
4425
4529
  isCompactToolName,
4426
4530
  isContextCompactDone,
4427
4531
  isContextCompactInProgress,
@@ -4447,6 +4551,7 @@ export {
4447
4551
  compactPartsForSummaryMessage,
4448
4552
  applyAgentEvent,
4449
4553
  partsToAssistantText,
4554
+ visibleAssistantText,
4450
4555
  lastAssistantMessageText,
4451
4556
  stripBrightsyNdjsonNoise,
4452
4557
  isBrightsyNdjsonLine,
@@ -44,7 +44,7 @@ import {
44
44
  summarizeTurnStderr,
45
45
  toolDescription,
46
46
  turnFailChatText
47
- } from "./chunk-2QRU4IFI.js";
47
+ } from "./chunk-HRWCW6LC.js";
48
48
  import {
49
49
  SLACK_REPLY_FORMATTING,
50
50
  addWorkspace,
@@ -606,9 +606,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
606
606
  if (assistantText && (parsed.data === assistantText || assistantText.endsWith(parsed.data))) {
607
607
  continue;
608
608
  }
609
- assistantText += parsed.data;
610
609
  }
611
610
  parts = applyAgentEvent(parts, parsed);
611
+ if (parsed.type === "stdout") {
612
+ assistantText = partsToAssistantText(parts);
613
+ }
612
614
  outbound.push(parsed);
613
615
  }
614
616
  });
@@ -7058,7 +7060,7 @@ function formatScheduledPrompt(name, prompt) {
7058
7060
  ${prompt}`;
7059
7061
  }
7060
7062
  async function defaultDeps() {
7061
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ZJ6SYGW7.js");
7063
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-DPMS5KMY.js");
7062
7064
  const orch = getOrchestrator2();
7063
7065
  return {
7064
7066
  findThread: (id) => findThreadByRef(id),