neko-harness 0.3.0 → 0.3.2

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/neko.js CHANGED
@@ -79,7 +79,7 @@ function updateAction(input) {
79
79
  return "install";
80
80
  return "none";
81
81
  }
82
- var RUNNING_VERSION = "0.3.0", APPLICATION_PROTOCOL = 3, ApplicationProtocolError;
82
+ var RUNNING_VERSION = "0.3.2", APPLICATION_PROTOCOL = 4, ApplicationProtocolError;
83
83
  var init_application_version = __esm(() => {
84
84
  ApplicationProtocolError = class ApplicationProtocolError extends Error {
85
85
  constructor() {
@@ -803,7 +803,10 @@ function summarizeToolCall(name, args) {
803
803
  const record = args ?? {};
804
804
  switch (name) {
805
805
  case "subagent":
806
- return { args: `${record.type}: ${record.title}`, detail: String(record.task ?? "") };
806
+ return {
807
+ args: record.action === "list" ? "List agents" : `${record.agentId ?? record.type}: ${record.title}`,
808
+ detail: String(record.task ?? "")
809
+ };
807
810
  case "find_files":
808
811
  case "search_text":
809
812
  return {
@@ -843,6 +846,51 @@ function toolResultText(content) {
843
846
  return content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text ?? "").join(`
844
847
  `);
845
848
  }
849
+ function countDiffLines(diff) {
850
+ let added = 0;
851
+ let removed = 0;
852
+ for (const line of diff.split(`
853
+ `)) {
854
+ if (line.startsWith("+") && !line.startsWith("+++"))
855
+ added++;
856
+ else if (line.startsWith("-") && !line.startsWith("---"))
857
+ removed++;
858
+ }
859
+ return { added, removed };
860
+ }
861
+ function editResultDiff(details) {
862
+ if (typeof details !== "object" || details === null)
863
+ return null;
864
+ const diff = details.diff;
865
+ return typeof diff === "string" ? diff : null;
866
+ }
867
+ function synthesizedWriteDiff(args) {
868
+ const record = args ?? {};
869
+ if (typeof record.content !== "string")
870
+ return null;
871
+ const content = record.content;
872
+ const shown = [];
873
+ let offset = 0;
874
+ let bytes = 0;
875
+ while (offset < content.length && shown.length < WRITE_DIFF_MAX_LINES) {
876
+ const newline = content.indexOf(`
877
+ `, offset);
878
+ const end = newline === -1 ? content.length : newline;
879
+ const overhead = shown.length === 0 ? 2 : 3;
880
+ const remaining = WRITE_DIFF_MAX_BYTES - bytes - overhead;
881
+ if (end - offset > remaining)
882
+ break;
883
+ const line = content.slice(offset, end);
884
+ const lineBytes = Buffer.byteLength(line, "utf8");
885
+ if (lineBytes > remaining)
886
+ break;
887
+ shown.push(`+ ${line}`);
888
+ bytes += lineBytes + overhead;
889
+ offset = newline === -1 ? content.length : end + 1;
890
+ }
891
+ return { diff: shown.join(`
892
+ `), added: shown.length, removed: 0, truncated: offset < content.length };
893
+ }
846
894
  function totalUsage(state) {
847
895
  const total = {
848
896
  input: 0,
@@ -875,6 +923,8 @@ function projectChatView(view, modelLabel, options = {}) {
875
923
  if (message.role !== "toolResult")
876
924
  continue;
877
925
  const diagnostics = entry.data?.diagnostics ?? [];
926
+ const diff = message.toolName === "edit" ? editResultDiff(message.details) : null;
927
+ const counts = diff === null ? null : countDiffLines(diff);
878
928
  results.set(message.toolCallId, {
879
929
  type: "tool",
880
930
  key: `t${message.toolCallId}`,
@@ -891,7 +941,10 @@ function projectChatView(view, modelLabel, options = {}) {
891
941
  ].filter(Boolean).join(`
892
942
  `),
893
943
  durationMs: message.durationMs ?? null,
894
- note: null
944
+ note: null,
945
+ diff,
946
+ diffAdded: counts?.added ?? null,
947
+ diffRemoved: counts?.removed ?? null
895
948
  });
896
949
  }
897
950
  }
@@ -926,6 +979,7 @@ function projectChatView(view, modelLabel, options = {}) {
926
979
  const defaultImagePrompt = entry.kind === NekoInput.kind ? savedDefaultImagePrompt(message.content, entry.data?.defaultImagePrompt) : undefined;
927
980
  items.push({
928
981
  type: "user",
982
+ ...entry.data?.subagentRoundId ? { subagentRoundId: entry.data.subagentRoundId } : {},
929
983
  key: `e${entry.id}`,
930
984
  id: entry.id,
931
985
  text: defaultImagePrompt && Array.isArray(message.content) ? userText(message.content.slice(1)) : userText(message.content),
@@ -948,6 +1002,7 @@ function projectChatView(view, modelLabel, options = {}) {
948
1002
  } else {
949
1003
  const summary = summarizeToolCall(block.name, block.arguments);
950
1004
  const slot = live.tools?.find((s) => s.callId === block.id);
1005
+ const synthesized = block.name === "write" ? synthesizedWriteDiff(block.arguments) : null;
951
1006
  items.push({
952
1007
  type: "tool",
953
1008
  key: `t${block.id}`,
@@ -960,7 +1015,11 @@ function projectChatView(view, modelLabel, options = {}) {
960
1015
  output: slot?.output ?? null,
961
1016
  result: null,
962
1017
  durationMs: null,
963
- note: ran ? null : "Not run: response interrupted."
1018
+ note: ran ? null : "Not run: response interrupted.",
1019
+ diff: synthesized?.diff ?? null,
1020
+ diffAdded: synthesized?.added ?? null,
1021
+ diffRemoved: synthesized?.removed ?? null,
1022
+ diffTruncated: synthesized?.truncated ?? false
964
1023
  });
965
1024
  }
966
1025
  }
@@ -1008,7 +1067,17 @@ function projectChatView(view, modelLabel, options = {}) {
1008
1067
  }
1009
1068
  function withArgs(item, name, args) {
1010
1069
  const summary = summarizeToolCall(name, args);
1011
- return { ...item, name, summary: summary.args, detail: summary.detail ?? null };
1070
+ const synthesized = name === "write" ? synthesizedWriteDiff(args) : null;
1071
+ return {
1072
+ ...item,
1073
+ name,
1074
+ summary: summary.args,
1075
+ detail: summary.detail ?? null,
1076
+ diff: synthesized?.diff ?? item.diff,
1077
+ diffAdded: synthesized?.added ?? item.diffAdded,
1078
+ diffRemoved: synthesized?.removed ?? item.diffRemoved,
1079
+ diffTruncated: synthesized?.truncated ?? false
1080
+ };
1012
1081
  }
1013
1082
  function assistantItem(message, id) {
1014
1083
  let thinking = "";
@@ -1061,13 +1130,18 @@ ${block.text}`;
1061
1130
  if (block.type === "toolCall") {
1062
1131
  const slot = live.tools?.find((candidate) => candidate.callId === block.id);
1063
1132
  const summary = summarizeToolCall(block.name, block.arguments);
1133
+ const synthesized = block.name === "write" ? synthesizedWriteDiff(block.arguments) : null;
1064
1134
  toolCalls.push({
1065
1135
  callId: block.id,
1066
1136
  name: block.name,
1067
1137
  summary: summary.args,
1068
1138
  detail: summary.detail ?? null,
1069
1139
  phase: slot === undefined || slot.status === "pending" ? "pending" : slot.status,
1070
- output: slot?.status === "running" ? slot.output ?? null : null
1140
+ output: slot?.status === "running" ? slot.output ?? null : null,
1141
+ diff: synthesized?.diff ?? null,
1142
+ diffAdded: synthesized?.added ?? null,
1143
+ diffRemoved: synthesized?.removed ?? null,
1144
+ diffTruncated: synthesized?.truncated ?? false
1071
1145
  });
1072
1146
  }
1073
1147
  }
@@ -1087,10 +1161,12 @@ function contextTokens(entries) {
1087
1161
  }
1088
1162
  return null;
1089
1163
  }
1164
+ var WRITE_DIFF_MAX_LINES = 2000, WRITE_DIFF_MAX_BYTES;
1090
1165
  var init_projection = __esm(() => {
1091
1166
  init_image_prompt();
1092
1167
  init_input();
1093
1168
  init_workspace();
1169
+ WRITE_DIFF_MAX_BYTES = 64 * 1024;
1094
1170
  });
1095
1171
 
1096
1172
  // packages/core/src/prompt/frontmatter.ts
@@ -1485,164 +1561,403 @@ var init_invoke = __esm(() => {
1485
1561
  init_skills();
1486
1562
  });
1487
1563
 
1564
+ // packages/core/src/subagent-usage.ts
1565
+ function subtractSubagentUsage(end, start) {
1566
+ const result = emptySubagentUsage();
1567
+ for (const bucket of ["models", "tools"]) {
1568
+ for (const [key, usage] of Object.entries(end[bucket])) {
1569
+ const before = start[bucket][key];
1570
+ const cost = usage.cost;
1571
+ const next = {
1572
+ input: usage.input - (before?.input ?? 0),
1573
+ output: usage.output - (before?.output ?? 0),
1574
+ cacheRead: usage.cacheRead - (before?.cacheRead ?? 0),
1575
+ cacheWrite: usage.cacheWrite - (before?.cacheWrite ?? 0),
1576
+ totalTokens: usage.totalTokens - (before?.totalTokens ?? 0),
1577
+ cost: {
1578
+ input: cost.input - (before?.cost.input ?? 0),
1579
+ output: cost.output - (before?.cost.output ?? 0),
1580
+ cacheRead: cost.cacheRead - (before?.cost.cacheRead ?? 0),
1581
+ cacheWrite: cost.cacheWrite - (before?.cost.cacheWrite ?? 0),
1582
+ total: cost.total - (before?.cost.total ?? 0)
1583
+ }
1584
+ };
1585
+ if (usage.reasoning !== undefined)
1586
+ next.reasoning = usage.reasoning - (before?.reasoning ?? 0);
1587
+ if (usage.cacheWrite1h !== undefined)
1588
+ next.cacheWrite1h = usage.cacheWrite1h - (before?.cacheWrite1h ?? 0);
1589
+ result[bucket][key] = next;
1590
+ }
1591
+ }
1592
+ return result;
1593
+ }
1594
+ function subagentUsageTotals(state) {
1595
+ const totals = totalUsage(state);
1596
+ return {
1597
+ input: totals.input,
1598
+ output: totals.output,
1599
+ cacheRead: totals.cacheRead,
1600
+ cacheWrite: totals.cacheWrite,
1601
+ cost: totals.cost.total
1602
+ };
1603
+ }
1604
+ var emptySubagentUsage = () => ({ models: {}, tools: {} });
1605
+ var init_subagent_usage = __esm(() => {
1606
+ init_projection();
1607
+ });
1608
+
1488
1609
  // packages/core/src/subagents.ts
1489
1610
  import {
1490
1611
  AssistantEntry,
1491
1612
  configure,
1492
1613
  defineDoc,
1493
1614
  defineExtension as defineExtension3,
1494
- defineTool as defineTool2
1615
+ defineTask,
1616
+ defineTool as defineTool2,
1617
+ LiveDoc,
1618
+ UsageDoc
1495
1619
  } from "@earendil-works/pi-durable";
1496
1620
  import { Type as Type2 } from "typebox";
1621
+ import { Value } from "typebox/value";
1497
1622
  async function refreshDelegations(tx, id) {
1498
1623
  const doc = await tx.doc(DelegationsDoc, id);
1624
+ const identities = await tx.doc(SubagentsDoc, id);
1499
1625
  for (const item of doc.items) {
1626
+ item.agentId ??= agentIdFor(item.childId);
1627
+ item.round ??= doc.items.filter((d) => d.childId === item.childId && d.taskId <= item.taskId).length;
1500
1628
  if (item.historyThrough !== undefined)
1501
1629
  continue;
1502
- if (item.status === "completed" || item.status === "failed" || item.status === "cancelled")
1503
- continue;
1630
+ if (item.inputEntryId === undefined) {
1631
+ const submission = await tx.submissionByRequest(item.childId, `subagent:${item.taskId}`);
1632
+ if (submission?.type === "input" && submission.entry !== undefined)
1633
+ item.inputEntryId = submission.entry;
1634
+ }
1635
+ let identity = identities.items.find((a) => a.childId === item.childId);
1636
+ if (!identity) {
1637
+ const role = await tx.doc(SubagentDoc, item.childId);
1638
+ if (role.parentId !== id || role.role !== item.role)
1639
+ continue;
1640
+ identity = {
1641
+ agentId: item.agentId,
1642
+ parentId: id,
1643
+ childId: item.childId,
1644
+ role: item.role,
1645
+ cwd: "",
1646
+ title: item.title,
1647
+ createdAt: item.createdAt,
1648
+ activeRoundId: item.taskId
1649
+ };
1650
+ identities.items.push(identity);
1651
+ identity = identities.items.at(-1);
1652
+ }
1504
1653
  const task = await tx.task(item.taskId);
1505
- if (task?.state.status !== "terminal")
1506
- continue;
1507
- item.status = task.state.outcome.status === "aborted" ? "cancelled" : "failed";
1508
- item.endedAt = Date.now();
1654
+ const runner = item.runnerId === undefined ? undefined : await tx.task(item.runnerId);
1655
+ const live = await tx.doc(LiveDoc, item.childId);
1656
+ const idle = live.run === undefined && !live.compactions?.length;
1657
+ if (task?.state.status === "terminal") {
1658
+ if (item.status === "queued" || item.status === "running") {
1659
+ item.status = task.state.outcome.status === "aborted" ? "cancelled" : "failed";
1660
+ item.endedAt ??= Date.now();
1661
+ }
1662
+ if (identity.activeRoundId === item.taskId && idle && (!runner || runner.state.status === "terminal"))
1663
+ identity.activeRoundId = null;
1664
+ }
1509
1665
  }
1510
1666
  return JSON.parse(JSON.stringify(doc.items));
1511
1667
  }
1512
- async function admit(api, cwd, role, context) {
1513
- for (;; ) {
1514
- context.abortSignal?.throwIfAborted();
1515
- const acquired = await api.commit(async (tx) => {
1516
- const slots = await tx.doc(SlotsDoc);
1517
- const live = [];
1518
- for (const slot of slots.items) {
1519
- const task = await tx.task(slot.taskId);
1520
- if (task && task.state.status !== "terminal")
1521
- live.push(slot);
1522
- }
1523
- slots.items = live;
1524
- if (live.some((slot) => slot.taskId === api.taskId))
1525
- return true;
1526
- if (live.filter((slot) => slot.parentId === api.conversationId).length >= 3 || role === "coder" && live.some((slot) => slot.coder && slot.cwd === cwd))
1527
- return false;
1528
- slots.items.push({ taskId: api.taskId, parentId: api.conversationId, cwd, coder: role === "coder" });
1529
- return true;
1530
- }, context);
1531
- if (acquired)
1532
- return;
1533
- await new Promise((resolve, reject) => {
1534
- const cleanup = () => {
1535
- clearTimeout(timer);
1536
- context.abortSignal?.removeEventListener("abort", abort);
1537
- };
1538
- const abort = () => {
1539
- cleanup();
1540
- reject(context.abortSignal?.reason ?? new Error("Cancelled"));
1541
- };
1542
- const timer = setTimeout(() => {
1543
- cleanup();
1544
- resolve();
1545
- }, 100);
1546
- context.abortSignal?.addEventListener("abort", abort, { once: true });
1547
- if (context.abortSignal?.aborted)
1548
- abort();
1549
- });
1550
- }
1668
+ function summarizeSubagents(rounds, identities = []) {
1669
+ const groups = new Map;
1670
+ for (const round of rounds) {
1671
+ const id = round.agentId ?? agentIdFor(round.childId);
1672
+ groups.set(id, [...groups.get(id) ?? [], round]);
1673
+ }
1674
+ return [...groups].map(([agentId, entries]) => {
1675
+ const first = entries[0];
1676
+ const last = entries.at(-1);
1677
+ const identity = identities.find((a) => a.agentId === agentId);
1678
+ const active = entries.find((r) => identity ? r.taskId === identity.activeRoundId : r.status === "running" || r.status === "queued");
1679
+ return {
1680
+ agentId,
1681
+ parentId: first.parentId,
1682
+ childId: first.childId,
1683
+ role: first.role,
1684
+ title: identity?.title ?? first.title,
1685
+ status: active ? active.status === "queued" ? "queued" : "running" : "idle",
1686
+ activeRoundId: active?.taskId ?? null,
1687
+ lastRoundId: last.taskId,
1688
+ lastReport: last.report?.slice(0, 512) ?? null,
1689
+ roundCount: entries.length,
1690
+ taskTitle: (active ?? last).title,
1691
+ approvalCount: active?.approvalCount ?? 0,
1692
+ createdAt: first.createdAt,
1693
+ ...first.historyThrough !== undefined ? { historyThrough: first.historyThrough, historyUsage: first.historyUsage } : {}
1694
+ };
1695
+ });
1696
+ }
1697
+ async function acquireSlot(tx, round, cwd) {
1698
+ const slots = await tx.doc(SlotsDoc);
1699
+ const live = [];
1700
+ for (const slot of slots.items) {
1701
+ const task = await tx.task(slot.taskId);
1702
+ if (task && task.state.status !== "terminal")
1703
+ live.push(slot);
1704
+ }
1705
+ slots.items = live;
1706
+ if (live.some((slot) => slot.taskId === round.taskId))
1707
+ return true;
1708
+ if (live.filter((slot) => slot.parentId === round.parentId).length >= 3 || round.role === "coder" && live.some((slot) => slot.coder && slot.cwd === cwd))
1709
+ return false;
1710
+ slots.items.push({ taskId: round.taskId, parentId: round.parentId, cwd, coder: round.role === "coder" });
1711
+ return true;
1551
1712
  }
1552
1713
  function createSubagentExtension(prepare, discard) {
1714
+ const finish = async (tx, input, status, report) => {
1715
+ const round = (await tx.doc(DelegationsDoc, input.parentId)).items.find((r) => r.taskId === input.taskId);
1716
+ round.status = status;
1717
+ round.report = report;
1718
+ round.endedAt ??= Date.now();
1719
+ round.usageEnd = JSON.parse(JSON.stringify(await tx.doc(UsageDoc, input.childId)));
1720
+ round.roundUsage = round.usageStart ? subagentUsageTotals(subtractSubagentUsage(round.usageEnd, round.usageStart)) : round.startedAt === null ? subagentUsageTotals(emptySubagentUsage()) : null;
1721
+ };
1722
+ const RoundTask = defineTask({
1723
+ name: "app.neko.subagent-round",
1724
+ version: 1,
1725
+ initial: () => ({ phase: "admission" }),
1726
+ phases: {
1727
+ admission: async (task, runtime, ctx) => {
1728
+ const env = await runtime.env(ctx);
1729
+ const canonical = env && await env.canonicalPath(task.input.cwd, ctx);
1730
+ const slotCwd = canonical?.ok ? canonical.value : task.input.cwd;
1731
+ let admitted = false;
1732
+ while (!admitted) {
1733
+ await runtime.commit(async (tx) => {
1734
+ const round = (await tx.doc(DelegationsDoc, task.input.parentId)).items.find((r) => r.taskId === task.input.taskId);
1735
+ if (!await acquireSlot(tx, round, slotCwd))
1736
+ return;
1737
+ admitted = true;
1738
+ const duplicate = await tx.submissionByRequest(task.input.childId, `subagent:${round.taskId}`);
1739
+ if (!duplicate && !round.usageStart)
1740
+ round.usageStart = JSON.parse(JSON.stringify(await tx.doc(UsageDoc, task.input.childId)));
1741
+ round.status = "running";
1742
+ round.startedAt ??= Date.now();
1743
+ return { status: "running", checkpoint: { phase: "deliver" } };
1744
+ }, ctx);
1745
+ if (!admitted)
1746
+ await runtime.sleep(runtime.now() + 100, ctx);
1747
+ }
1748
+ },
1749
+ deliver: async (task, runtime, ctx) => {
1750
+ const input = task.input;
1751
+ const child = await runtime.conversation(input.childId, ctx);
1752
+ const requestId = `subagent:${input.taskId}`;
1753
+ try {
1754
+ let duplicate = false;
1755
+ await runtime.commit(async (tx) => {
1756
+ duplicate = !!await tx.submissionByRequest(input.childId, requestId);
1757
+ }, ctx);
1758
+ if (!duplicate)
1759
+ await prepare(input.childId, requestId, input.cwd);
1760
+ const submission = await child.submit({
1761
+ type: "input",
1762
+ content: input.task,
1763
+ requestId,
1764
+ whenBusy: "reject",
1765
+ entry: {
1766
+ kind: NekoInput.kind,
1767
+ data: { at: Date.now(), prompt: input.task, images: [], subagentRoundId: input.taskId }
1768
+ }
1769
+ }, ctx);
1770
+ const placed = await submission.status(ctx);
1771
+ await runtime.commit(async (tx) => {
1772
+ if (placed.type === "input" && placed.entry !== undefined)
1773
+ (await tx.doc(DelegationsDoc, input.parentId)).items.find((r) => r.taskId === input.taskId).inputEntryId = placed.entry;
1774
+ return { status: "running", checkpoint: { phase: "settle" } };
1775
+ }, ctx);
1776
+ } catch (error) {
1777
+ discard(input.childId, requestId);
1778
+ if (ctx.abortSignal?.aborted)
1779
+ throw error;
1780
+ await child.abort(ctx);
1781
+ const report = `Subagent failed: ${String(error)}`;
1782
+ await runtime.commit(async (tx) => {
1783
+ await finish(tx, input, "failed", report);
1784
+ return { status: "terminal", outcome: { status: "completed", result: { status: "failed", report } } };
1785
+ }, ctx);
1786
+ }
1787
+ },
1788
+ settle: async (task, runtime, ctx) => {
1789
+ const input = task.input;
1790
+ const child = await runtime.conversation(input.childId, ctx);
1791
+ const submission = await child.submit({ type: "input", content: input.task, requestId: `subagent:${input.taskId}` }, ctx);
1792
+ const settled = await submission.wait(ctx);
1793
+ await child.waitForIdle(ctx);
1794
+ await runtime.commit(async (tx) => {
1795
+ let report = "";
1796
+ let status = "failed";
1797
+ const round = (await tx.doc(DelegationsDoc, input.parentId)).items.find((r) => r.taskId === input.taskId);
1798
+ if (settled.type === "input" && settled.entry !== undefined)
1799
+ round.inputEntryId = settled.entry;
1800
+ if (settled.status === "done" && settled.type === "input") {
1801
+ round.answerEntryId = settled.answer;
1802
+ const message = (await tx.entry(AssistantEntry, settled.answer))?.model?.[0];
1803
+ report = message?.role === "assistant" ? message.content.flatMap((b) => b.type === "text" ? [b.text] : []).join("") : "";
1804
+ status = "completed";
1805
+ } else if (settled.status === "unanswered") {
1806
+ status = settled.reason === "aborted" ? "cancelled" : "failed";
1807
+ report = `Subagent ${status}: ${settled.reason}`;
1808
+ }
1809
+ await finish(tx, input, status, report);
1810
+ return { status: "terminal", outcome: { status: "completed", result: { status, report } } };
1811
+ }, ctx);
1812
+ }
1813
+ },
1814
+ abort: async (task, runtime, ctx) => {
1815
+ const input = task.input;
1816
+ let own = false;
1817
+ await runtime.commit(async (tx) => {
1818
+ own = (await tx.doc(SubagentsDoc, input.parentId)).items.some((a) => a.childId === input.childId && a.activeRoundId === input.taskId);
1819
+ }, ctx);
1820
+ if (own)
1821
+ await (await runtime.conversation(input.childId, ctx))?.abort(ctx);
1822
+ await runtime.commit(async (tx) => {
1823
+ await finish(tx, input, "cancelled", "Subagent cancelled");
1824
+ return { status: "terminal", outcome: { status: "aborted" } };
1825
+ }, ctx);
1826
+ }
1827
+ });
1553
1828
  const extension = defineExtension3({
1554
1829
  name: "neko-subagents",
1830
+ tasks: [RoundTask],
1555
1831
  tools: [
1556
1832
  defineTool2({
1557
1833
  name: "subagent",
1558
- description: "Delegate a self-contained task to an independent foreground subagent. explore: investigation, Bash requires user approval; plan: read-only implementation planning; coder: scoped changes and verification. Include all needed context, constraints and expected delivery. Use only when isolation or parallel independent work helps; handle simple tasks directly. Returns the final report; the main agent waits and must verify/integrate it.",
1559
- parameters: Type2.Object({
1560
- type: Type2.Union([Type2.Literal("explore"), Type2.Literal("plan"), Type2.Literal("coder")]),
1561
- title: Type2.String({ minLength: 1, maxLength: 80 }),
1562
- task: Type2.String({ minLength: 1, maxLength: 32768 })
1563
- }),
1834
+ description: "Manage persistent foreground subagents. create (default): type explore/plan/coder, title, task. continue: agentId, title, task; reuses its own context, only while idle. list: discover this session's agents, optional cursor/limit. Explore Bash requires per-call user approval. Keep constraints explicit; verify and integrate final reports. Subagents cannot delegate.",
1835
+ parameters,
1836
+ structuredOutputSchema,
1564
1837
  replay: "safe",
1565
- execute: async (args, api, context) => {
1566
- if (!args.task.trim() || !args.title.trim() || Buffer.byteLength(args.task) > 32768)
1567
- throw new Error("Task/title must be nonempty; task limit is 32 KiB.");
1838
+ execute: async (input, api, context) => {
1839
+ if (!Value.Check(actionParameters, input))
1840
+ throw new Error("Invalid subagent arguments. create requires type/title/task; continue requires agentId/title/task; list accepts only cursor/limit. Do not mix fields from different actions.");
1841
+ const args = input;
1568
1842
  if ((await api.snapshot(SubagentDoc, api.conversationId, context))?.role)
1569
1843
  throw new Error("Subagents cannot delegate.");
1844
+ if (args.action === "list") {
1845
+ const list = await api.commit(async (tx) => {
1846
+ const rounds = await refreshDelegations(tx, api.conversationId);
1847
+ return summarizeSubagents(rounds, (await tx.doc(SubagentsDoc, api.conversationId)).items).filter((a) => a.historyThrough === undefined);
1848
+ }, context);
1849
+ const offset = args.cursor ?? 0;
1850
+ const items = list.slice(offset, offset + (args.limit ?? 50));
1851
+ const result = { items, next: offset + items.length < list.length ? offset + items.length : null };
1852
+ return { output: [{ type: "text", text: JSON.stringify(result) }], structuredOutput: result };
1853
+ }
1854
+ if (!args.task.trim() || !args.title.trim() || Buffer.byteLength(args.task) > 32768)
1855
+ throw new Error("Task/title must be nonempty; task limit is 32 KiB.");
1570
1856
  const agent = await api.agent(context);
1571
- const childId = await api.commit(async (tx) => {
1572
- const existing = (await tx.scanConversations({ ownerTaskId: api.taskId }, 1)).items[0];
1573
- if (existing)
1574
- return existing.id;
1575
- const child = await tx.createConversation({ ownership: { kind: "task", taskId: api.taskId } });
1576
- await configure(tx, child.id, {
1577
- model: agent.model,
1578
- thinkingLevel: agent.thinkingLevel,
1579
- cwd: agent.cwd,
1580
- extensions: agent.extensions.filter((e) => e.name !== extension.name),
1581
- tools: agent.tools.filter((tool) => tool.name !== "subagent" && (args.type === "coder" || READ_TOOLS.has(tool.name) || args.type === "explore" && tool.name === "bash"))
1582
- });
1583
- Object.assign(await tx.doc(SubagentDoc, child.id), { role: args.type, parentId: api.conversationId });
1584
- (await tx.doc(DelegationsDoc, api.conversationId)).items.push({
1857
+ const round = await api.commit(async (tx) => {
1858
+ const rounds = await refreshDelegations(tx, api.conversationId);
1859
+ const receipt = rounds.find((r) => r.taskId === api.taskId);
1860
+ if (receipt?.runnerId !== undefined)
1861
+ return receipt;
1862
+ const identities = await tx.doc(SubagentsDoc, api.conversationId);
1863
+ let identity;
1864
+ if (args.action === "continue") {
1865
+ identity = identities.items.find((a) => a.agentId === args.agentId);
1866
+ if (!identity)
1867
+ throw new Error("Unknown subagent or it belongs to another conversation.");
1868
+ const info = await tx.doc(SubagentDoc, identity.childId);
1869
+ if (info.parentId !== api.conversationId || info.role !== identity.role)
1870
+ throw new Error("Subagent ownership mismatch.");
1871
+ if (identity.activeRoundId !== null)
1872
+ throw new Error("Subagent is busy; continue only when idle.");
1873
+ } else if (receipt) {
1874
+ identity = identities.items.find((a) => a.childId === receipt.childId);
1875
+ }
1876
+ const existing = identity ? undefined : (await tx.scanConversations({ ownerTaskId: api.taskId }, 1)).items[0];
1877
+ const child = identity ? await tx.conversation(identity.childId) : existing;
1878
+ if (!identity) {
1879
+ const created = child ?? await tx.createConversation({ ownership: { kind: "task", taskId: api.taskId } });
1880
+ const role = args.action === "continue" ? "explore" : args.type;
1881
+ await configure(tx, created.id, {
1882
+ model: agent.model,
1883
+ thinkingLevel: agent.thinkingLevel,
1884
+ cwd: agent.cwd,
1885
+ extensions: agent.extensions.filter((e) => e.name !== extension.name),
1886
+ tools: agent.tools.filter((t) => t.name !== "subagent" && (role === "coder" || READ_TOOLS.has(t.name) || role === "explore" && t.name === "bash"))
1887
+ });
1888
+ Object.assign(await tx.doc(SubagentDoc, created.id), { role, parentId: api.conversationId });
1889
+ identity = {
1890
+ agentId: agentIdFor(created.id),
1891
+ parentId: api.conversationId,
1892
+ childId: created.id,
1893
+ role,
1894
+ cwd: agent.cwd ?? "",
1895
+ title: args.title,
1896
+ createdAt: Date.now(),
1897
+ activeRoundId: null
1898
+ };
1899
+ identities.items.push(identity);
1900
+ }
1901
+ const record = receipt ?? {
1585
1902
  parentId: api.conversationId,
1586
1903
  taskId: api.taskId,
1587
1904
  callId: api.callId,
1588
- childId: child.id,
1589
- role: args.type,
1905
+ childId: identity.childId,
1906
+ agentId: identity.agentId,
1907
+ round: rounds.filter((r) => r.childId === identity.childId).length + 1,
1908
+ role: identity.role,
1590
1909
  title: args.title,
1591
1910
  createdAt: Date.now(),
1592
1911
  startedAt: null,
1593
1912
  endedAt: null,
1594
1913
  status: "queued",
1595
1914
  report: null
1596
- });
1597
- return child.id;
1598
- }, context);
1599
- await api.details({ conversationId: childId, role: args.type, title: args.title }, context);
1600
- const cwd = api.env ? await api.env.canonicalPath(agent.cwd ?? api.env.cwd, context) : undefined;
1601
- await admit(api, cwd?.ok ? cwd.value : agent.cwd ?? "", args.type, context);
1602
- await api.commit(async (tx) => {
1603
- const item = (await tx.doc(DelegationsDoc, api.conversationId)).items.find((d) => d.taskId === api.taskId);
1604
- item.status = "running";
1605
- item.startedAt ??= Date.now();
1915
+ };
1916
+ identities.items.find((a) => a.agentId === identity.agentId).activeRoundId = api.taskId;
1917
+ const runner = await tx.createTask(RoundTask, {
1918
+ parentId: api.conversationId,
1919
+ childId: identity.childId,
1920
+ taskId: api.taskId,
1921
+ task: args.task,
1922
+ cwd: identity.cwd || agent.cwd || ""
1923
+ }, { ownership: { kind: "task", taskId: api.taskId } });
1924
+ record.runnerId = runner;
1925
+ const doc = await tx.doc(DelegationsDoc, api.conversationId);
1926
+ if (receipt)
1927
+ Object.assign(doc.items.find((r) => r.taskId === receipt.taskId), record);
1928
+ else
1929
+ doc.items.push(record);
1930
+ return record;
1606
1931
  }, context);
1607
- const child = await api.conversation(childId, context);
1608
- const requestId = `subagent:${api.taskId}`;
1609
- const duplicate = await api.commit((tx) => tx.submissionByRequest(childId, requestId), context);
1610
- if (!duplicate)
1611
- await prepare(childId, requestId, agent.cwd ?? "");
1612
- let submission;
1613
- try {
1614
- submission = await child.submit({
1615
- type: "input",
1616
- content: args.task,
1617
- requestId,
1618
- entry: { kind: NekoInput.kind, data: { at: Date.now(), prompt: args.task, images: [] } }
1619
- }, context);
1620
- } catch (error) {
1621
- discard(childId, requestId);
1622
- throw error;
1623
- }
1624
- const settled = await submission.wait(context);
1625
- let report = "";
1626
- let status = "failed";
1627
- if (settled.status === "done" && settled.type === "input") {
1628
- const entry = await api.commit((tx) => tx.entry(AssistantEntry, settled.answer), context);
1629
- const message = entry?.model?.[0];
1630
- report = message?.role === "assistant" ? message.content.flatMap((b) => b.type === "text" ? [b.text] : []).join("") : "";
1631
- status = "completed";
1632
- } else if (settled.status === "unanswered") {
1633
- status = settled.reason === "aborted" ? "cancelled" : "failed";
1634
- report = `Subagent ${status}: ${settled.reason}`;
1635
- }
1636
- await api.commit(async (tx) => {
1637
- const item = (await tx.doc(DelegationsDoc, api.conversationId)).items.find((d) => d.taskId === api.taskId);
1638
- item.status = status;
1639
- item.report = report;
1640
- item.endedAt = Date.now();
1932
+ await api.details({
1933
+ conversationId: round.childId,
1934
+ agentId: round.agentId,
1935
+ roundId: round.taskId,
1936
+ role: round.role,
1937
+ title: round.title
1641
1938
  }, context);
1939
+ const settled = await api.waitForTask(round.runnerId, context);
1940
+ const outcome = settled.state.outcome;
1941
+ const result = outcome.status === "completed" ? outcome.result : { status: outcome.status === "aborted" ? "cancelled" : "failed", report: `Subagent ${outcome.status}` };
1642
1942
  return {
1643
- output: [{ type: "text", text: report || "Subagent completed without a text report." }],
1644
- isError: status !== "completed",
1645
- details: { conversationId: childId, role: args.type, title: args.title, status }
1943
+ output: [
1944
+ {
1945
+ type: "text",
1946
+ text: `Agent: ${round.agentId}
1947
+ Status: ${result.status}
1948
+ ${result.report || "Subagent completed without a text report."}`
1949
+ }
1950
+ ],
1951
+ isError: result.status !== "completed",
1952
+ structuredOutput: { agentId: round.agentId, roundId: round.taskId, ...result },
1953
+ details: {
1954
+ conversationId: round.childId,
1955
+ agentId: round.agentId,
1956
+ roundId: round.taskId,
1957
+ role: round.role,
1958
+ title: round.title,
1959
+ status: result.status
1960
+ }
1646
1961
  };
1647
1962
  }
1648
1963
  })
@@ -1651,11 +1966,67 @@ function createSubagentExtension(prepare, discard) {
1651
1966
  return extension;
1652
1967
  }
1653
1968
  async function listDelegations(harness, parent, context) {
1969
+ const rounds = await harness.commit((tx) => refreshDelegations(tx, parent), context);
1970
+ for (const round of rounds) {
1971
+ if (round.historyThrough !== undefined)
1972
+ continue;
1973
+ const task = await harness.getTask(round.taskId, context);
1974
+ if (task?.state.status !== "terminal")
1975
+ continue;
1976
+ const child = await harness.conversation(round.childId, context);
1977
+ const live = await harness.snapshot(LiveDoc, round.childId, context);
1978
+ const identity = (await harness.snapshot(SubagentsDoc, parent, context))?.items.find((a) => a.childId === round.childId);
1979
+ if (identity?.activeRoundId === round.taskId && live?.run !== undefined)
1980
+ await child?.abort(context);
1981
+ }
1654
1982
  return harness.commit((tx) => refreshDelegations(tx, parent), context);
1655
1983
  }
1656
- var DelegationsDoc, SubagentDoc, SlotsDoc, ROLE_PROMPTS, SUBAGENT_DELIVERY = " Work only from the assigned task and your project resources; you cannot see the main conversation. Do not delegate or ask the user directly. Your final message must be a self-contained delivery to the main agent, including evidence, results and limitations. Skills never expand your allowed tools.", READ_TOOLS;
1984
+ async function installSubagentCoordinator(harness, parents, context) {
1985
+ const known = new Set(parents);
1986
+ let running = false;
1987
+ let dirty = false;
1988
+ const reconcile = async () => {
1989
+ if (running) {
1990
+ dirty = true;
1991
+ return;
1992
+ }
1993
+ running = true;
1994
+ try {
1995
+ do {
1996
+ dirty = false;
1997
+ for (const id of known)
1998
+ await listDelegations(harness, id, context);
1999
+ } while (dirty);
2000
+ } finally {
2001
+ running = false;
2002
+ }
2003
+ };
2004
+ for (const parent of parents) {
2005
+ await harness.commit((tx) => refreshDelegations(tx, parent), context);
2006
+ for (const identity of (await harness.snapshot(SubagentsDoc, parent, context))?.items ?? []) {
2007
+ if (identity.cwd)
2008
+ continue;
2009
+ const child = await harness.conversation(identity.childId, context);
2010
+ const cwd = (await child?.agent(context))?.cwd;
2011
+ if (cwd)
2012
+ await harness.commit(async (tx) => {
2013
+ (await tx.doc(SubagentsDoc, parent)).items.find((a) => a.agentId === identity.agentId).cwd = cwd;
2014
+ }, context);
2015
+ }
2016
+ }
2017
+ const stop = harness.subscribeCommits(({ changes }) => {
2018
+ for (const c of changes)
2019
+ if (c.type === "document" && c.record.kind === "app.neko.delegations" && c.conversationId !== undefined)
2020
+ known.add(c.conversationId);
2021
+ if (changes.some((c) => c.type === "task" && c.value.state.status === "terminal"))
2022
+ reconcile().catch(() => {});
2023
+ });
2024
+ harness.closed.finally(stop).catch(() => {});
2025
+ }
2026
+ var agentIdFor = (childId) => `agent-${childId}`, DelegationsDoc, SubagentsDoc, SubagentDoc, SlotsDoc, ROLE_PROMPTS, SUBAGENT_DELIVERY = " Work from the assigned task, your own prior conversation context and project resources; you cannot see the main conversation. Do not delegate or ask the user directly. Your final message must be a self-contained delivery to the main agent, including evidence, results and limitations. Skills never expand your allowed tools.", READ_TOOLS, actionParameters, parameters, structuredOutputSchema;
1657
2027
  var init_subagents = __esm(() => {
1658
2028
  init_input();
2029
+ init_subagent_usage();
1659
2030
  DelegationsDoc = defineDoc({
1660
2031
  kind: "app.neko.delegations",
1661
2032
  version: 1,
@@ -1664,6 +2035,14 @@ var init_subagents = __esm(() => {
1664
2035
  fork: "initial",
1665
2036
  initial: () => ({ items: [] })
1666
2037
  });
2038
+ SubagentsDoc = defineDoc({
2039
+ kind: "app.neko.subagents",
2040
+ version: 1,
2041
+ scope: "conversation",
2042
+ history: "latest",
2043
+ fork: "initial",
2044
+ initial: () => ({ items: [] })
2045
+ });
1667
2046
  SubagentDoc = defineDoc({
1668
2047
  kind: "app.neko.subagent",
1669
2048
  version: 1,
@@ -1684,6 +2063,60 @@ var init_subagents = __esm(() => {
1684
2063
  coder: "You are neko's coding subagent. Implement only the assigned scope and verify it. Coordinate through the main agent; do not overwrite unrelated changes. Report changed files and verification results."
1685
2064
  };
1686
2065
  READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract"]);
2066
+ actionParameters = Type2.Union([
2067
+ Type2.Object({
2068
+ action: Type2.Optional(Type2.Literal("create")),
2069
+ type: Type2.Enum(["explore", "plan", "coder"]),
2070
+ title: Type2.String({ minLength: 1, maxLength: 80 }),
2071
+ task: Type2.String({ minLength: 1, maxLength: 32768 })
2072
+ }, { additionalProperties: false }),
2073
+ Type2.Object({
2074
+ action: Type2.Literal("continue"),
2075
+ agentId: Type2.String(),
2076
+ title: Type2.String({ minLength: 1, maxLength: 80 }),
2077
+ task: Type2.String({ minLength: 1, maxLength: 32768 })
2078
+ }, { additionalProperties: false }),
2079
+ Type2.Object({
2080
+ action: Type2.Literal("list"),
2081
+ cursor: Type2.Optional(Type2.Integer({ minimum: 0 })),
2082
+ limit: Type2.Optional(Type2.Integer({ minimum: 1, maximum: 100 }))
2083
+ }, { additionalProperties: false })
2084
+ ]);
2085
+ parameters = Type2.Object({
2086
+ action: Type2.Optional(Type2.String({ enum: ["create", "continue", "list"], description: "Defaults to create." })),
2087
+ type: Type2.Optional(Type2.String({ enum: ["explore", "plan", "coder"], description: "Required for create only." })),
2088
+ agentId: Type2.Optional(Type2.String({ description: "Required for continue only; use an ID returned by create/list." })),
2089
+ title: Type2.Optional(Type2.String({ minLength: 1, maxLength: 80, description: "Required for create/continue." })),
2090
+ task: Type2.Optional(Type2.String({ minLength: 1, maxLength: 32768, description: "Required for create/continue." })),
2091
+ cursor: Type2.Optional(Type2.Integer({ minimum: 0, description: "List pagination cursor only." })),
2092
+ limit: Type2.Optional(Type2.Integer({ minimum: 1, maximum: 100, description: "List page size only." }))
2093
+ }, { additionalProperties: false });
2094
+ structuredOutputSchema = Type2.Union([
2095
+ Type2.Object({
2096
+ agentId: Type2.String(),
2097
+ roundId: Type2.Integer(),
2098
+ status: Type2.Enum(["completed", "failed", "cancelled"]),
2099
+ report: Type2.String()
2100
+ }, { additionalProperties: false }),
2101
+ Type2.Object({
2102
+ items: Type2.Array(Type2.Object({
2103
+ agentId: Type2.String(),
2104
+ parentId: Type2.Integer(),
2105
+ childId: Type2.Integer(),
2106
+ role: Type2.Enum(["explore", "plan", "coder"]),
2107
+ title: Type2.String(),
2108
+ status: Type2.Enum(["idle", "queued", "running"]),
2109
+ activeRoundId: Type2.Union([Type2.Integer(), Type2.Null()]),
2110
+ lastRoundId: Type2.Integer(),
2111
+ lastReport: Type2.Optional(Type2.Union([Type2.String(), Type2.Null()])),
2112
+ roundCount: Type2.Integer(),
2113
+ taskTitle: Type2.String(),
2114
+ approvalCount: Type2.Integer(),
2115
+ createdAt: Type2.Number()
2116
+ }, { additionalProperties: false })),
2117
+ next: Type2.Union([Type2.Integer(), Type2.Null()])
2118
+ }, { additionalProperties: false })
2119
+ ]);
1687
2120
  });
1688
2121
 
1689
2122
  // packages/core/src/prompt/prompt.ts
@@ -1744,7 +2177,7 @@ var init_prompt = __esm(() => {
1744
2177
  KEYS = ["preamble", "rules", "project_context", "skills", "cwd"];
1745
2178
  TOOL_GUIDELINES = {
1746
2179
  subagent: [
1747
- "Delegate only independent, sufficiently scoped work when isolation or parallelism helps. Handle simple tasks directly. Pass necessary context and user constraints; research-only requests must not become coding tasks. Verify and integrate reports. Do not modify files concurrently with a coder assigned those files."
2180
+ "Delegate only independent, sufficiently scoped work when isolation or parallelism helps. Handle simple tasks directly. Pass necessary context and user constraints; research-only requests must not become coding tasks. For related follow-up work, list and continue a suitable idle subagent using its stable agentId; busy means no task was accepted. Do not change its role. Verify and integrate reports. Do not modify files concurrently with a coder assigned those files."
1748
2181
  ],
1749
2182
  find_files: [
1750
2183
  "Use find_files to discover files; query is a literal substring. Narrow the directory when results are partial."
@@ -1899,9 +2332,9 @@ import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
1899
2332
  import {
1900
2333
  defineDoc as defineDoc2,
1901
2334
  defineExtension as defineExtension5,
1902
- defineTask,
2335
+ defineTask as defineTask2,
1903
2336
  InboxDoc,
1904
- LiveDoc
2337
+ LiveDoc as LiveDoc2
1905
2338
  } from "@earendil-works/pi-durable";
1906
2339
  function shortText(value, limit = 60) {
1907
2340
  return [
@@ -1943,7 +2376,7 @@ function mergeUsage(base, extra) {
1943
2376
  function namingTask() {
1944
2377
  let active = 0;
1945
2378
  const waits = new Set;
1946
- return defineTask({
2379
+ return defineTask2({
1947
2380
  name: "app.neko.name",
1948
2381
  version: 1,
1949
2382
  initial: () => ({ phase: "wait", until: Date.now() + 100 }),
@@ -2157,7 +2590,7 @@ class SessionMetadataManager {
2157
2590
  const answer = record.status === "done" ? (await this.storage.entry(record.answer, this.context))?.entry.model?.[0] : undefined;
2158
2591
  return { record, data, answer: answer?.role === "assistant" ? answer : undefined };
2159
2592
  }));
2160
- const live = await this.harness.snapshot(LiveDoc, id, this.context);
2593
+ const live = await this.harness.snapshot(LiveDoc2, id, this.context);
2161
2594
  await this.harness.commit(async (tx) => {
2162
2595
  const d = await tx.doc(SessionMetadataDoc, id);
2163
2596
  for (const { record, data, answer } of updates) {
@@ -2359,7 +2792,8 @@ function createApprovalExtension(service) {
2359
2792
  const child = await approvals.harness.conversation(api.conversationId, context);
2360
2793
  const agent = await child.agent(context);
2361
2794
  const cwd = await realpath2(agent.cwd);
2362
- const delegation = (await api.snapshot(DelegationsDoc, parent, context))?.items.find((d) => d.childId === api.conversationId && d.historyThrough === undefined);
2795
+ const identity = (await api.snapshot(SubagentsDoc, parent, context))?.items.find((a) => a.childId === api.conversationId);
2796
+ const delegation = (await api.snapshot(DelegationsDoc, parent, context))?.items.find((d) => d.childId === api.conversationId && d.historyThrough === undefined && d.taskId === identity?.activeRoundId);
2363
2797
  if (!delegation)
2364
2798
  return { block: "Missing delegation" };
2365
2799
  await approvals.ensure(parent, {
@@ -2990,7 +3424,7 @@ import {
2990
3424
  Harness,
2991
3425
  LiveDoc as importLiveDoc,
2992
3426
  MemoryStorage,
2993
- UsageDoc,
3427
+ UsageDoc as UsageDoc2,
2994
3428
  watchEvents
2995
3429
  } from "@earendil-works/pi-durable";
2996
3430
  import { createNodePhotonImages } from "@earendil-works/pi-durable/images/node";
@@ -3073,6 +3507,7 @@ async function createNekoRuntime(options = {}) {
3073
3507
  approvals = new ToolApprovals(harness, context);
3074
3508
  const existing = await scanConversations();
3075
3509
  try {
3510
+ await installSubagentCoordinator(harness, existing.map((r) => r.id), context);
3076
3511
  await prepareApprovalConversations(harness, existing.map((r) => r.id), approvalExtension, context);
3077
3512
  for (const record of existing) {
3078
3513
  const target = await harness.conversation(record.id, context);
@@ -3257,7 +3692,7 @@ async function createNekoRuntime(options = {}) {
3257
3692
  return watchEvents(harness, target.id, context);
3258
3693
  },
3259
3694
  async usage() {
3260
- return mergeUsage(await harness.snapshot(UsageDoc, target.id, context) ?? { models: {}, tools: {} }, (await harness.snapshot(SessionMetadataDoc, target.id, context))?.namingUsage ?? { models: {}, tools: {} });
3695
+ return mergeUsage(await harness.snapshot(UsageDoc2, target.id, context) ?? { models: {}, tools: {} }, (await harness.snapshot(SessionMetadataDoc, target.id, context))?.namingUsage ?? { models: {}, tools: {} });
3261
3696
  },
3262
3697
  compactionRecord() {
3263
3698
  return readCompaction(harness, storage, target.id, context);
@@ -3306,7 +3741,7 @@ async function createNekoRuntime(options = {}) {
3306
3741
  async globalUsage() {
3307
3742
  let total = { models: {}, tools: {} };
3308
3743
  for (const record of await scanConversations()) {
3309
- total = mergeUsage(total, await harness.snapshot(UsageDoc, record.id, context) ?? { models: {}, tools: {} });
3744
+ total = mergeUsage(total, await harness.snapshot(UsageDoc2, record.id, context) ?? { models: {}, tools: {} });
3310
3745
  total = mergeUsage(total, (await harness.snapshot(SessionMetadataDoc, record.id, context))?.namingUsage ?? { models: {}, tools: {} });
3311
3746
  }
3312
3747
  return total;
@@ -3341,14 +3776,24 @@ async function createNekoRuntime(options = {}) {
3341
3776
  }
3342
3777
  cursor = page.next;
3343
3778
  } while (cursor);
3779
+ const frozen = new Map;
3344
3780
  for (const reference of references) {
3345
3781
  if (!calls.has(reference.callId))
3346
3782
  continue;
3347
- const latest = await tx.scanEntries({ conversationId: reference.childId }, 1);
3348
- const usage = totalUsage(await tx.doc(UsageDoc, reference.childId));
3783
+ let snapshot = frozen.get(reference.childId);
3784
+ if (!snapshot) {
3785
+ const latest = await tx.scanEntries({ conversationId: reference.childId }, 1);
3786
+ snapshot = {
3787
+ through: latest.items[0]?.id ?? 0,
3788
+ state: JSON.parse(JSON.stringify(await tx.doc(UsageDoc2, reference.childId)))
3789
+ };
3790
+ frozen.set(reference.childId, snapshot);
3791
+ }
3792
+ const usage = totalUsage(snapshot.state);
3349
3793
  inherited.push({
3350
3794
  ...reference,
3351
- historyThrough: reference.historyThrough ?? latest.items[0]?.id ?? 0,
3795
+ historyThrough: reference.historyThrough ?? snapshot.through,
3796
+ roundUsage: reference.roundUsage ?? (reference.usageStart ? subagentUsageTotals(subtractSubagentUsage(snapshot.state, reference.usageStart)) : null),
3352
3797
  historyUsage: reference.historyUsage ?? {
3353
3798
  input: usage.input,
3354
3799
  output: usage.output,
@@ -3561,6 +4006,7 @@ var init_runtime = __esm(() => {
3561
4006
  init_prompt();
3562
4007
  init_resources();
3563
4008
  init_session_metadata();
4009
+ init_subagent_usage();
3564
4010
  init_subagents();
3565
4011
  init_tool_approvals();
3566
4012
  init_web_search();
@@ -3615,6 +4061,7 @@ var init_src = __esm(() => {
3615
4061
  init_frontmatter();
3616
4062
  init_runtime();
3617
4063
  init_session_metadata();
4064
+ init_subagent_usage();
3618
4065
  init_subagents();
3619
4066
  init_tool_approvals();
3620
4067
  init_workspace();
@@ -3674,6 +4121,60 @@ function compactInstructions(text) {
3674
4121
  return match ? match[1]?.trim() ?? "" : null;
3675
4122
  }
3676
4123
 
4124
+ // packages/core/src/conversation-items.ts
4125
+ function userText2(item) {
4126
+ if (item.skill === undefined)
4127
+ return item.text;
4128
+ return item.skill.args === "" ? `/skill:${item.skill.name}` : `/skill:${item.skill.name} ${item.skill.args}`;
4129
+ }
4130
+ function toolStatus(item) {
4131
+ return item.phase !== "done" ? item.phase : item.isError ? "error" : "ok";
4132
+ }
4133
+ function toolOutput(item) {
4134
+ return (item.phase === "done" ? item.result : item.output) ?? "";
4135
+ }
4136
+ function imageLabel(image) {
4137
+ const size = image.width > 0 && image.height > 0 ? ` \xB7 ${image.width}\xD7${image.height}` : "";
4138
+ return `${image.name} \xB7 ${image.mimeType}${size}`;
4139
+ }
4140
+ function formatDuration(ms) {
4141
+ return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
4142
+ }
4143
+ function oneLine(text) {
4144
+ return text.replace(/\s+/g, " ").trim();
4145
+ }
4146
+ var FALLBACK_EXPORT_TITLE = "neko-session";
4147
+
4148
+ // packages/core/src/input-history.ts
4149
+ function promptHistory(items) {
4150
+ const entries = [];
4151
+ for (const item of items) {
4152
+ if (item.type !== "user" || item.subagentRoundId !== undefined)
4153
+ continue;
4154
+ const text = userText2(item).trim();
4155
+ if (text === "" || entries.at(-1) === text)
4156
+ continue;
4157
+ entries.push(text);
4158
+ }
4159
+ return entries.reverse().slice(0, INPUT_HISTORY_LIMIT);
4160
+ }
4161
+ var INPUT_HISTORY_LIMIT = 100;
4162
+ var init_input_history = () => {};
4163
+
4164
+ // packages/core/src/session-display.ts
4165
+ function relativeTime(at, now, language = "en") {
4166
+ const minutes = Math.floor(Math.max(0, now - at) / 60000);
4167
+ if (minutes < 1)
4168
+ return language === "zh" ? "\u521A\u521A" : "now";
4169
+ const hours = Math.floor(minutes / 60), days = Math.floor(hours / 24);
4170
+ if (days >= 30)
4171
+ return new Date(at).toLocaleDateString(language === "zh" ? "zh-CN" : "en-GB");
4172
+ const value = days || hours || minutes;
4173
+ const unit = days ? ["\u5929", "d"] : hours ? ["\u5C0F\u65F6", "h"] : ["\u5206\u949F", "m"];
4174
+ return language === "zh" ? `${value} ${unit[0]}\u524D` : `${value}${unit[1]}`;
4175
+ }
4176
+ var sessionName = (session, empty = "New session") => session.title || session.firstPromptPreview || empty, messageTime = (session) => session.lastMessageAt ?? session.createdAt, byMessage = (a, b) => messageTime(b) - messageTime(a) || Number(b.id) - Number(a.id), byVisited = (a, b) => b.lastUsedAt - a.lastUsedAt || Number(b.id) - Number(a.id);
4177
+
3677
4178
  // packages/gateway/src/contract.ts
3678
4179
  import { method, remote } from "@earendil-works/chord";
3679
4180
  import { Type as Type4 } from "typebox";
@@ -3687,7 +4188,7 @@ function contract(name, def) {
3687
4188
  ]));
3688
4189
  return remote(name, { ...def, variants });
3689
4190
  }
3690
- var rpc = (args, result) => [args, result], str, num, bool, nil, integer, nullableString, phase, level, thinking, model, change, usage, image, skill, call, DelegationSchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ApplicationInfoSchema, NekoApplicationInfo;
4191
+ var rpc = (args, result) => [args, result], str, num, bool, nil, integer, nullableString, phase, level, thinking, model, change, usage, image, skill, call, DelegationSchema, SubagentSummarySchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ApplicationInfoSchema, NekoApplicationInfo;
3691
4192
  var init_contract = __esm(() => {
3692
4193
  str = Type4.String();
3693
4194
  num = Type4.Number();
@@ -3717,10 +4218,20 @@ var init_contract = __esm(() => {
3717
4218
  summary: str,
3718
4219
  detail: nullableString,
3719
4220
  phase,
3720
- output: nullableString
4221
+ output: nullableString,
4222
+ diff: nullableString,
4223
+ diffAdded: Type4.Union([num, nil]),
4224
+ diffRemoved: Type4.Union([num, nil]),
4225
+ diffTruncated: Type4.Optional(bool)
3721
4226
  });
3722
4227
  DelegationSchema = Type4.Object({
3723
4228
  parentId: integer,
4229
+ agentId: Type4.Optional(str),
4230
+ round: Type4.Optional(integer),
4231
+ runnerId: Type4.Optional(integer),
4232
+ inputEntryId: Type4.Optional(integer),
4233
+ answerEntryId: Type4.Optional(integer),
4234
+ roundUsage: Type4.Optional(Type4.Union([usage, nil])),
3724
4235
  taskId: integer,
3725
4236
  callId: str,
3726
4237
  childId: integer,
@@ -3735,8 +4246,27 @@ var init_contract = __esm(() => {
3735
4246
  historyThrough: Type4.Optional(integer),
3736
4247
  historyUsage: Type4.Optional(usage)
3737
4248
  });
4249
+ SubagentSummarySchema = Type4.Object({
4250
+ agentId: str,
4251
+ parentId: integer,
4252
+ childId: integer,
4253
+ role: Type4.Enum(["explore", "plan", "coder"]),
4254
+ title: str,
4255
+ status: Type4.Enum(["idle", "queued", "running"]),
4256
+ activeRoundId: Type4.Union([integer, nil]),
4257
+ lastRoundId: integer,
4258
+ lastReport: Type4.Optional(nullableString),
4259
+ roundCount: integer,
4260
+ taskTitle: str,
4261
+ approvalCount: integer,
4262
+ createdAt: num,
4263
+ historyThrough: Type4.Optional(integer),
4264
+ historyUsage: Type4.Optional(usage)
4265
+ });
3738
4266
  ChatViewSchema = Type4.Object({
3739
4267
  historyBefore: Type4.Optional(Type4.Union([integer, nil])),
4268
+ agents: Type4.Optional(Type4.Array(SubagentSummarySchema)),
4269
+ rounds: Type4.Optional(Type4.Array(DelegationSchema)),
3740
4270
  subagents: Type4.Optional(Type4.Array(DelegationSchema)),
3741
4271
  taskUsage: Type4.Optional(usage),
3742
4272
  model: str,
@@ -3800,7 +4330,11 @@ var init_contract = __esm(() => {
3800
4330
  output: nullableString,
3801
4331
  result: nullableString,
3802
4332
  durationMs: Type4.Union([num, nil]),
3803
- note: nullableString
4333
+ note: nullableString,
4334
+ diff: nullableString,
4335
+ diffAdded: Type4.Union([num, nil]),
4336
+ diffRemoved: Type4.Union([num, nil]),
4337
+ diffTruncated: Type4.Optional(bool)
3804
4338
  }),
3805
4339
  Type4.Object({ type: Type4.Literal("system"), order: Type4.Optional(num), key: str, id: integer, text: str })
3806
4340
  ])),
@@ -3892,7 +4426,12 @@ var init_contract = __esm(() => {
3892
4426
  imageChunk = Type4.Object({ data: str, total: integer, mimeType: str });
3893
4427
  NekoSubagent = contract("neko.subagent", {
3894
4428
  version: 1,
3895
- input: Type4.Object({ sessionId: str, attachmentId: str, taskId: integer }),
4429
+ input: Type4.Object({
4430
+ sessionId: str,
4431
+ attachmentId: str,
4432
+ taskId: Type4.Optional(integer),
4433
+ agentId: Type4.Optional(str)
4434
+ }),
3896
4435
  variants: {
3897
4436
  ready: {
3898
4437
  state: { revision: integer },
@@ -3905,7 +4444,7 @@ var init_contract = __esm(() => {
3905
4444
  }), Type4.Object({ token: integer, data: str, next: Type4.Union([integer, nil]) })),
3906
4445
  getImage: rpc(Type4.Object({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk),
3907
4446
  decideApproval: rpc(Type4.Object({ id: integer, version: str, decision: Type4.Enum(["allowed", "denied"]) }), Type4.Enum(["pending", "allowed", "denied", "cancelled"])),
3908
- stop: rpc(nil, nil)
4447
+ stop: rpc(Type4.Union([nil, Type4.Object({ taskId: integer })]), nil)
3909
4448
  }
3910
4449
  }
3911
4450
  }
@@ -4312,17 +4851,18 @@ class ApprovalQueue {
4312
4851
  update(entries) {
4313
4852
  const pending = entries.filter((d) => d.approvalCount && d.historyThrough === undefined);
4314
4853
  for (const [id, reader] of this.#readers) {
4315
- if (!pending.some((d) => d.taskId === id)) {
4854
+ if (!pending.some((d) => (d.agentId ?? d.taskId) === id)) {
4316
4855
  this.#readers.delete(id);
4317
4856
  reader.stop();
4318
4857
  }
4319
4858
  }
4320
4859
  for (const entry of pending) {
4321
- if (this.#readers.has(entry.taskId))
4860
+ const key = entry.agentId ?? entry.taskId;
4861
+ if (this.#readers.has(key))
4322
4862
  continue;
4323
- const service = this.#connect(entry.taskId);
4863
+ const service = this.#connect(key);
4324
4864
  const reader = { service, title: entry.title, stop: () => {} };
4325
- this.#readers.set(entry.taskId, reader);
4865
+ this.#readers.set(key, reader);
4326
4866
  reader.stop = service.view.subscribe(() => this.#changed());
4327
4867
  }
4328
4868
  this.#changed();
@@ -4392,10 +4932,10 @@ function createGatewayConnection(link, serverId) {
4392
4932
  let closing;
4393
4933
  const connection = {
4394
4934
  application,
4395
- subagent(taskId) {
4935
+ subagent(target) {
4396
4936
  if (!attachment)
4397
4937
  throw new Error("No active session");
4398
- const input = { ...attachment, taskId };
4938
+ const input = { ...attachment, ...typeof target === "string" ? { agentId: target } : { taskId: target } };
4399
4939
  return subagentClient(() => host.get(NekoSubagent, input, { at: serverId }));
4400
4940
  },
4401
4941
  host,
@@ -4620,8 +5160,8 @@ var init_palette = __esm(() => {
4620
5160
  codeFg: { fg: "#C0CAF5" },
4621
5161
  border: { fg: "#737DA2" },
4622
5162
  borderMuted: { fg: "#545E7C" },
4623
- diffAdded: { fg: "#9ECE6A" },
4624
- diffRemoved: { fg: "#F7768E" },
5163
+ diffAdded: { fg: "#9ECE6A", bg: "#232C32" },
5164
+ diffRemoved: { fg: "#F7768E", bg: "#302735" },
4625
5165
  diffContext: { fg: "#9AA5CE" },
4626
5166
  syntaxComment: { fg: "#8993B3", italic: true },
4627
5167
  syntaxKeyword: { fg: "#BB9AF7", bold: true },
@@ -4655,8 +5195,8 @@ var init_palette = __esm(() => {
4655
5195
  codeFg: { fg: sakura.s800 },
4656
5196
  border: { fg: ink.s300 },
4657
5197
  borderMuted: { fg: ink.s200 },
4658
- diffAdded: { fg: "#1A7F37" },
4659
- diffRemoved: { fg: sakura.s800 },
5198
+ diffAdded: { fg: "#1A7F37", bg: "#E4F3E8" },
5199
+ diffRemoved: { fg: sakura.s800, bg: "#FBE7EC" },
4660
5200
  diffContext: { fg: ink.s400 },
4661
5201
  syntaxComment: { fg: ink.s600, italic: true },
4662
5202
  syntaxKeyword: { fg: sakura.s800 },
@@ -4749,6 +5289,10 @@ function getActiveThemeMode() {
4749
5289
  function delegate(name) {
4750
5290
  return (text) => activeTheme[name](text);
4751
5291
  }
5292
+ function diffBackgroundAnsi(kind) {
5293
+ const style = (activeMode === "dark" ? DARK_PALETTE : LIGHT_PALETTE)[kind === "added" ? "diffAdded" : "diffRemoved"];
5294
+ return backgroundAnsi(parseColor(style.bg), getTerminalColorMode());
5295
+ }
4752
5296
  var darkTheme, lightTheme, THEMES, activeMode = "dark", activeTheme, theme, sgr = (attrs) => (text) => styleText(text, {
4753
5297
  ...attrs.bold !== undefined ? { bold: attrs.bold } : {},
4754
5298
  ...attrs.italic !== undefined ? { italic: attrs.italic } : {},
@@ -14173,7 +14717,7 @@ function solidBlock(lines, fullWidth) {
14173
14717
  const max = Math.max(fullWidth ?? 0, ...lines.map((line) => line.width));
14174
14718
  return lines.map((line) => theme.codeBg(line.styled + " ".repeat(Math.max(0, max - line.width))));
14175
14719
  }
14176
- function renderHighlighted(html, fullWidth) {
14720
+ function htmlToSegmentLines(html) {
14177
14721
  const segments = [];
14178
14722
  const scopes = [];
14179
14723
  let textBuffer = "";
@@ -14226,25 +14770,31 @@ function renderHighlighted(html, fullWidth) {
14226
14770
  }
14227
14771
  flushText();
14228
14772
  const lines = [];
14229
- let current = "";
14230
- let currentWidth = 0;
14773
+ let current = [];
14231
14774
  for (const segment of segments) {
14232
14775
  const parts = segment.text.split(`
14233
14776
  `);
14234
14777
  for (const [i, part] of parts.entries()) {
14235
14778
  if (i > 0) {
14236
- lines.push({ styled: current, width: currentWidth });
14237
- current = "";
14238
- currentWidth = 0;
14779
+ lines.push(current);
14780
+ current = [];
14239
14781
  }
14240
14782
  if (part === "")
14241
14783
  continue;
14242
- currentWidth += visibleWidth(part);
14243
- current += segment.slot === "plain" ? theme.codeFg(part) : theme[segment.slot](part);
14784
+ current.push({ slot: segment.slot, text: part });
14244
14785
  }
14245
14786
  }
14246
- lines.push({ styled: current, width: currentWidth });
14247
- return solidBlock(lines, fullWidth);
14787
+ lines.push(current);
14788
+ return lines;
14789
+ }
14790
+ function styleSegmentLine(segments) {
14791
+ return {
14792
+ styled: segments.map((s) => s.slot === "plain" ? theme.codeFg(s.text) : theme[s.slot](s.text)).join(""),
14793
+ width: segments.reduce((total, s) => total + visibleWidth(s.text), 0)
14794
+ };
14795
+ }
14796
+ function renderHighlighted(html, fullWidth) {
14797
+ return solidBlock(htmlToSegmentLines(html).map(styleSegmentLine), fullWidth);
14248
14798
  }
14249
14799
  function normalizeLang(lang) {
14250
14800
  if (lang === undefined)
@@ -14252,6 +14802,28 @@ function normalizeLang(lang) {
14252
14802
  const lowered = lang.trim().toLowerCase();
14253
14803
  return LANG_ALIASES[lowered] ?? lowered;
14254
14804
  }
14805
+ function highlightCodeToSegments(code, lang) {
14806
+ const normalized = normalizeLang(lang);
14807
+ if (normalized === "" || core_default.getLanguage(normalized) === undefined)
14808
+ return null;
14809
+ try {
14810
+ return htmlToSegmentLines(core_default.highlight(code, { language: normalized }).value);
14811
+ } catch {
14812
+ return null;
14813
+ }
14814
+ }
14815
+ function languageForPath(path) {
14816
+ if (path === undefined)
14817
+ return;
14818
+ const base = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
14819
+ const dot = base.lastIndexOf(".");
14820
+ if (dot <= 0)
14821
+ return;
14822
+ const ext = base.slice(dot + 1);
14823
+ if (ext === "" || core_default.getLanguage(ext) === undefined)
14824
+ return;
14825
+ return ext;
14826
+ }
14255
14827
  function highlightCodeToAnsiLines(code, lang, fullWidth) {
14256
14828
  const normalized = normalizeLang(lang);
14257
14829
  if (normalized !== "" && core_default.getLanguage(normalized) !== undefined) {
@@ -14571,32 +15143,1894 @@ var init_approval_dock = __esm(() => {
14571
15143
  init_theme();
14572
15144
  });
14573
15145
 
14574
- // packages/tui/src/components/assistant-message.ts
14575
- import { Container, Markdown, MouseRegion, Spacer, Text as Text2 } from "@earendil-works/pi-tui";
14576
- var THINKING_COLLAPSED_LABEL = "\u2727 Thinking\u2026 (click to expand)", AssistantMessageComponent;
14577
- var init_assistant_message = __esm(() => {
14578
- init_theme();
14579
- AssistantMessageComponent = class AssistantMessageComponent extends Container {
14580
- #renderTheme = getActiveThemeMode();
14581
- #lastContent;
14582
- #isStreaming = false;
14583
- #thinkingExpanded = false;
14584
- #renderWidth = 80;
14585
- constructor(content, isStreaming = false) {
14586
- super();
14587
- if (content !== undefined)
14588
- this.updateContent(content, isStreaming);
14589
- }
14590
- render(width) {
14591
- if (this.#renderTheme !== getActiveThemeMode()) {
14592
- this.#renderTheme = getActiveThemeMode();
14593
- this.#rebuild();
14594
- }
14595
- this.#renderWidth = width;
14596
- return super.render(width);
15146
+ // node_modules/marked/lib/marked.esm.js
15147
+ function I() {
15148
+ return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null };
15149
+ }
15150
+ function W(l) {
15151
+ P = l;
15152
+ }
15153
+ function C(l) {
15154
+ let e = [];
15155
+ return (t) => {
15156
+ let n = Math.max(0, Math.min(3, t - 1)), s = e[n];
15157
+ return s || (s = l(n), e[n] = s), s;
15158
+ };
15159
+ }
15160
+ function h(l, e = "") {
15161
+ let t = typeof l == "string" ? l : l.source, n = { replace: (s, r) => {
15162
+ let o = typeof r == "string" ? r : r.source;
15163
+ return o = o.replace(x.caret, "$1"), t = t.replace(s, o), n;
15164
+ }, getRegex: () => new RegExp(t, e) };
15165
+ return n;
15166
+ }
15167
+ function O(l, e) {
15168
+ if (e) {
15169
+ if (x.escapeTest.test(l))
15170
+ return l.replace(x.escapeReplace, Re);
15171
+ } else if (x.escapeTestNoEncode.test(l))
15172
+ return l.replace(x.escapeReplaceNoEncode, Re);
15173
+ return l;
15174
+ }
15175
+ function Te(l) {
15176
+ return l.replace(x.numericCharacterReference, (e, t, n) => {
15177
+ let s = t === undefined ? Number.parseInt(n, 16) : Number.parseInt(t, 10);
15178
+ return s === 0 || s > 1114111 || s >= 55296 && s <= 57343 ? "\uFFFD" : String.fromCodePoint(s);
15179
+ });
15180
+ }
15181
+ function re(l) {
15182
+ try {
15183
+ l = encodeURI(l).replace(x.percentDecode, "%");
15184
+ } catch {
15185
+ return null;
15186
+ }
15187
+ return l;
15188
+ }
15189
+ function se(l, e) {
15190
+ let t = l.replace(x.findPipe, (r, o, i) => {
15191
+ let u = false, a = o;
15192
+ for (;--a >= 0 && i[a] === "\\"; )
15193
+ u = !u;
15194
+ return u ? "|" : " |";
15195
+ }), n = t.split(x.splitPipe), s = 0;
15196
+ if (n[0].trim() || n.shift(), n.length > 0 && !n.at(-1)?.trim() && n.pop(), e)
15197
+ if (n.length > e)
15198
+ n.splice(e);
15199
+ else
15200
+ for (;n.length < e; )
15201
+ n.push("");
15202
+ for (;s < n.length; s++)
15203
+ n[s] = n[s].trim().replace(x.slashPipe, "|");
15204
+ return n;
15205
+ }
15206
+ function L(l, e, t) {
15207
+ let n = l.length;
15208
+ if (n === 0)
15209
+ return "";
15210
+ let s = 0;
15211
+ for (;s < n; ) {
15212
+ let r = l.charAt(n - s - 1);
15213
+ if (r === e && !t)
15214
+ s++;
15215
+ else if (r !== e && t)
15216
+ s++;
15217
+ else
15218
+ break;
15219
+ }
15220
+ return l.slice(0, n - s);
15221
+ }
15222
+ function ie(l) {
15223
+ let e = l.split(`
15224
+ `), t = e.length - 1;
15225
+ for (;t >= 0 && x.blankLine.test(e[t]); )
15226
+ t--;
15227
+ return e.length - t <= 2 ? l : e.slice(0, t + 1).join(`
15228
+ `);
15229
+ }
15230
+ function q(l) {
15231
+ return l.trim().toLowerCase().toUpperCase().toLowerCase();
15232
+ }
15233
+ function oe(l, e) {
15234
+ if (l.indexOf(e[0]) === -1 && l.indexOf(e[1]) === -1)
15235
+ return -1;
15236
+ let t = 0;
15237
+ for (let n = 0;n < l.length; n++)
15238
+ if (l[n] === "\\")
15239
+ n++;
15240
+ else if (l[n] === e[0])
15241
+ t++;
15242
+ else if (l[n] === e[1] && (t--, t < 0))
15243
+ return n;
15244
+ return t > 0 ? -2 : -1;
15245
+ }
15246
+ function ae(l, e = 0) {
15247
+ let t = e, n = "";
15248
+ for (let s of l)
15249
+ if (s === "\t") {
15250
+ let r = 4 - t % 4;
15251
+ n += " ".repeat(r), t += r;
15252
+ } else
15253
+ n += s, t++;
15254
+ return n;
15255
+ }
15256
+ function Oe(l, e, t, n, s) {
15257
+ let r = e.href, o = e.title || null, i = l[1].replace(s.other.outputLinkReplace, "$1"), u = l[0].charAt(0) === "!";
15258
+ n.state.inLink = true;
15259
+ let a = n.state.linkEmitted, p = n.state.inRawBlock;
15260
+ n.state.linkEmitted = false;
15261
+ let c = n.inlineTokens(i), d = n.state.linkEmitted;
15262
+ if (n.state.linkEmitted = a, n.state.inLink = false, !u) {
15263
+ if (d) {
15264
+ n.state.inRawBlock = p;
15265
+ return;
14597
15266
  }
14598
- #highlightWidth() {
14599
- return Math.max(10, this.#renderWidth - 4);
15267
+ n.state.linkEmitted = true;
15268
+ }
15269
+ return { type: u ? "image" : "link", raw: t, href: r, title: o, text: i, tokens: c };
15270
+ }
15271
+ function Pt(l, e, t) {
15272
+ let n = l.match(t.other.indentCodeCompensation);
15273
+ if (n === null)
15274
+ return e;
15275
+ let s = n[1];
15276
+ return e.split(`
15277
+ `).map((r) => {
15278
+ let o = r.match(t.other.beginningSpace);
15279
+ if (o === null)
15280
+ return r;
15281
+ let [i] = o;
15282
+ return r.slice(Math.min(i.length, s.length));
15283
+ }).join(`
15284
+ `);
15285
+ }
15286
+ function we(l, e, t, n) {
15287
+ if (!e.includes("<"))
15288
+ return false;
15289
+ for (let s = 0;s < e.length; s++) {
15290
+ if (e[s] === "\\") {
15291
+ s++;
15292
+ continue;
15293
+ }
15294
+ if (e[s] === "`") {
15295
+ let i = n.inline.code.exec(e.slice(s));
15296
+ if (i) {
15297
+ s += i[0].length - 1;
15298
+ continue;
15299
+ }
15300
+ }
15301
+ if (e[s] !== "<")
15302
+ continue;
15303
+ let r = l.slice(t + s), o = n.inline.tag.exec(r) || n.inline.autolink.exec(r);
15304
+ if (o) {
15305
+ if (o[0].length > e.length - s)
15306
+ return true;
15307
+ s += o[0].length - 1;
15308
+ }
15309
+ }
15310
+ return false;
15311
+ }
15312
+ function k(l, e) {
15313
+ return E.parse(l, e);
15314
+ }
15315
+ function yt(...l) {
15316
+ return E.use(...l), k.defaults = E.defaults, W(k.defaults), k;
15317
+ }
15318
+ var P, A, _e, x, $e, Le, ze, G, Ae, J, he, de, Ee, V, Me, Y, Ie, Ce, N = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul", ee, Be, ke = (l) => h(V).replace("hr", G).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", l).replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", N).getRegex(), De, qe, ve, te, ue, Ze, He, Ge, Ne, ge, Qe, $, B, Q, je, Fe, fe, Ue, Ke, We, me, Xe, Je, Ve, Ye, xe = "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)", et, tt, nt = "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)[\\s](\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|(?:(?!\\*)punct|notPunctSpace)(\\*+)(?!\\*)(?=notPunctSpace)", rt, st, it = "^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)[\\s](_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)|(?:(?!_)punct|notPunctSpace)(_+)(?!_)(?=notPunctSpace)", ot, at, lt = "^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)", ut, pt, ct, ht, dt, be, U, kt, gt, ft, pe, mt, xt, ce, bt, Rt, ne, Tt, X, Ot, j, D, wt, Re = (l) => wt[l], y = class {
15319
+ options;
15320
+ rules;
15321
+ lexer;
15322
+ constructor(e) {
15323
+ this.options = e || P;
15324
+ }
15325
+ space(e) {
15326
+ let t = this.rules.block.newline.exec(e);
15327
+ if (t && t[0].length > 0)
15328
+ return { type: "space", raw: t[0] };
15329
+ }
15330
+ code(e) {
15331
+ let t = this.rules.block.code.exec(e);
15332
+ if (t) {
15333
+ let n = this.options.pedantic ? t[0] : ie(t[0]), s = n.replace(this.rules.other.codeRemoveIndent, "");
15334
+ return { type: "code", raw: n, codeBlockStyle: "indented", text: s };
15335
+ }
15336
+ }
15337
+ fences(e) {
15338
+ let t = this.rules.block.fences.exec(e);
15339
+ if (t) {
15340
+ let n = t[0], s = Pt(n, t[3] || "", this.rules);
15341
+ return { type: "code", raw: n, lang: t[2] ? t[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : t[2], text: s };
15342
+ }
15343
+ }
15344
+ heading(e) {
15345
+ let t = this.rules.block.heading.exec(e);
15346
+ if (t) {
15347
+ let n = t[2].trim();
15348
+ if (this.rules.other.endingHash.test(n)) {
15349
+ let s = L(n, "#");
15350
+ (this.options.pedantic || !s || this.rules.other.endingSpaceTabChar.test(s)) && (n = s.trim());
15351
+ }
15352
+ return { type: "heading", raw: L(t[0], `
15353
+ `), depth: t[1].length, text: n, tokens: this.lexer.inline(n) };
15354
+ }
15355
+ }
15356
+ hr(e) {
15357
+ let t = this.rules.block.hr.exec(e);
15358
+ if (t)
15359
+ return { type: "hr", raw: L(t[0], `
15360
+ `) };
15361
+ }
15362
+ blockquote(e) {
15363
+ let t = this.rules.block.blockquote.exec(e);
15364
+ if (t) {
15365
+ let n = L(t[0], `
15366
+ `).split(`
15367
+ `), s = "", r = "", o = [];
15368
+ for (;n.length > 0; ) {
15369
+ let i = false, u = [], a;
15370
+ for (a = 0;a < n.length; a++)
15371
+ if (this.rules.other.blockquoteStart.test(n[a]))
15372
+ u.push(n[a]), i = true;
15373
+ else if (!i)
15374
+ u.push(n[a]);
15375
+ else
15376
+ break;
15377
+ n = n.slice(a);
15378
+ let p = u.join(`
15379
+ `), c = p.replace(this.rules.other.blockquoteSetextReplace, `
15380
+ $1`).replace(this.rules.other.blockquoteSetextReplace2, "");
15381
+ s = s ? `${s}
15382
+ ${p}` : p, r = r ? `${r}
15383
+ ${c}` : c;
15384
+ let d = this.lexer.state.top;
15385
+ if (this.lexer.state.top = true, this.lexer.blockTokens(c, o, true), this.lexer.state.top = d, n.length === 0)
15386
+ break;
15387
+ let m = o.at(-1);
15388
+ if (m?.type === "code")
15389
+ break;
15390
+ if (m?.type === "blockquote") {
15391
+ let b = m, g = n.join(`
15392
+ `), w = b.raw + `
15393
+ ` + g.replace(this.rules.other.blockquoteSetextReplace2, ""), f = this.blockquote(w);
15394
+ o[o.length - 1] = f;
15395
+ let M = w.substring(f.raw.length).replace(/^\n/, ""), v = M ? M.split(`
15396
+ `).length : 0, Z = v ? n.slice(0, -v) : n;
15397
+ Z.length > 0 && (s = `${s}
15398
+ ${Z.join(`
15399
+ `)}`), r = r.substring(0, r.length - b.text.length) + f.text;
15400
+ break;
15401
+ } else if (m?.type === "list") {
15402
+ let b = m, g = b.raw + `
15403
+ ` + n.join(`
15404
+ `), w = this.list(g);
15405
+ o[o.length - 1] = w, s = s.substring(0, s.length - m.raw.length) + w.raw, r = r.substring(0, r.length - b.raw.length) + w.raw, n = g.substring(o.at(-1).raw.length).split(`
15406
+ `);
15407
+ continue;
15408
+ }
15409
+ }
15410
+ return { type: "blockquote", raw: s, tokens: o, text: r };
15411
+ }
15412
+ }
15413
+ list(e) {
15414
+ let t = this.rules.block.list.exec(e);
15415
+ if (t) {
15416
+ let n = t[1].trim(), s = n.length > 1, r = { type: "list", raw: "", ordered: s, start: s ? +n.slice(0, -1) : "", loose: false, items: [] };
15417
+ n = s ? `\\d{1,9}\\${n.slice(-1)}` : `\\${n}`, this.options.pedantic && (n = s ? n : "[*+-]");
15418
+ let o = this.rules.other.listItemRegex(n), i = false;
15419
+ for (;e; ) {
15420
+ let a = false, p = "", c = "";
15421
+ if (!(t = o.exec(e)) || this.rules.block.hr.test(e))
15422
+ break;
15423
+ p = t[0], e = e.substring(p.length);
15424
+ let d = t[2].split(`
15425
+ `, 1)[0], m = t[1].length, b = this.options.pedantic ? ae(d, m) : d.replace(this.rules.other.leadingSpaceTab, (M) => ae(M, m)), g = e.split(`
15426
+ `, 1)[0], w = !b.trim(), f = 0;
15427
+ if (this.options.pedantic ? (f = 2, c = b.trimStart()) : w ? f = m + 1 : (f = b.search(this.rules.other.nonSpaceChar), f = f > 4 ? 1 : f, c = b.slice(f), f += m), w && this.rules.other.blankLine.test(g) && (p += g + `
15428
+ `, e = e.substring(g.length + 1), a = true), !a) {
15429
+ let M = this.rules.other.nextBulletRegex(f), v = this.rules.other.hrRegex(f), Z = this.rules.other.fencesBeginRegex(f), le = this.rules.other.headingBeginRegex(f), Pe = this.rules.other.htmlBeginRegex(f), ye = this.rules.other.blockquoteBeginRegex(f);
15430
+ for (;e; ) {
15431
+ let K = e.split(`
15432
+ `, 1)[0], H;
15433
+ if (g = K, this.options.pedantic ? (g = g.replace(this.rules.other.listReplaceNesting, " "), H = g) : H = g.replace(this.rules.other.leadingSpaceTab, (Se) => Se.replace(this.rules.other.tabCharGlobal, " ")), Z.test(g) || le.test(g) || Pe.test(g) || ye.test(g) || M.test(g) || v.test(g))
15434
+ break;
15435
+ if (H.search(this.rules.other.nonSpaceChar) >= f || !g.trim())
15436
+ c += `
15437
+ ` + H.slice(f);
15438
+ else {
15439
+ if (w || b.replace(this.rules.other.tabCharGlobal, " ").search(this.rules.other.nonSpaceChar) >= 4 || Z.test(b) || le.test(b) || v.test(b))
15440
+ break;
15441
+ c += `
15442
+ ` + g;
15443
+ }
15444
+ w = !g.trim(), p += K + `
15445
+ `, e = e.substring(K.length + 1), b = H.slice(f);
15446
+ }
15447
+ }
15448
+ r.loose || (i ? r.loose = true : this.rules.other.doubleBlankLine.test(p) && (i = true)), r.items.push({ type: "list_item", raw: p, task: !!this.options.gfm && this.rules.other.listIsTask.test(c), loose: false, text: c, tokens: [] }), r.raw += p;
15449
+ }
15450
+ let u = r.items.at(-1);
15451
+ if (u)
15452
+ u.raw = u.raw.trimEnd(), u.text = u.text.trimEnd();
15453
+ else
15454
+ return;
15455
+ r.raw = r.raw.trimEnd();
15456
+ for (let a of r.items)
15457
+ if (this.lexer.state.top = false, a.tokens = this.lexer.blockTokens(a.text, []), !r.loose) {
15458
+ let p = a.tokens.filter((d) => d.type === "space"), c = p.length > 0 && p.some((d) => this.rules.other.anyLine.test(d.raw));
15459
+ r.loose = c;
15460
+ }
15461
+ for (let a of r.items) {
15462
+ let p = a.tokens[0];
15463
+ if (a.task && (p?.type === "text" || p?.type === "paragraph")) {
15464
+ a.text = a.text.replace(this.rules.other.listReplaceTask, ""), p.raw = p.raw.replace(this.rules.other.listReplaceTask, ""), p.text = p.text.replace(this.rules.other.listReplaceTask, "");
15465
+ for (let d = this.lexer.inlineQueue.length - 1;d >= 0; d--)
15466
+ if (this.rules.other.listIsTask.test(this.lexer.inlineQueue[d].src)) {
15467
+ this.lexer.inlineQueue[d].src = this.lexer.inlineQueue[d].src.replace(this.rules.other.listReplaceTask, "");
15468
+ break;
15469
+ }
15470
+ let c = this.rules.other.listTaskCheckbox.exec(a.raw);
15471
+ if (c) {
15472
+ let d = { type: "checkbox", raw: c[0] + " ", checked: c[0] !== "[ ]" };
15473
+ a.checked = d.checked, r.loose ? a.tokens[0] && ["paragraph", "text"].includes(a.tokens[0].type) && "tokens" in a.tokens[0] && a.tokens[0].tokens ? (a.tokens[0].raw = d.raw + a.tokens[0].raw, a.tokens[0].text = d.raw + a.tokens[0].text, a.tokens[0].tokens.unshift(d)) : a.tokens.unshift({ type: "paragraph", raw: d.raw, text: d.raw, tokens: [d] }) : a.tokens.unshift(d);
15474
+ }
15475
+ } else
15476
+ a.task && (a.task = false);
15477
+ }
15478
+ if (r.loose)
15479
+ for (let a of r.items) {
15480
+ a.loose = true;
15481
+ for (let p of a.tokens)
15482
+ p.type === "text" && (p.type = "paragraph");
15483
+ }
15484
+ return r;
15485
+ }
15486
+ }
15487
+ html(e) {
15488
+ let t = this.rules.block.html.exec(e);
15489
+ if (t) {
15490
+ let n = ie(t[0]);
15491
+ return { type: "html", block: true, raw: n, pre: t[1] === "pre" || t[1] === "script" || t[1] === "style", text: n };
15492
+ }
15493
+ }
15494
+ def(e) {
15495
+ let t = this.rules.block.def.exec(e);
15496
+ if (t) {
15497
+ if (!this.rules.other.startAngleBracket.test(t[2]) && oe(t[2], "()") !== -1)
15498
+ return;
15499
+ let n = q(t[1]).replace(this.rules.other.multipleSpaceGlobal, " "), s = t[2] ? t[2].replace(this.rules.other.hrefBrackets, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "", r = t[3] ? t[3].substring(1, t[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : t[3];
15500
+ return { type: "def", tag: n, raw: L(t[0], `
15501
+ `), href: s, title: r };
15502
+ }
15503
+ }
15504
+ table(e) {
15505
+ let t = this.rules.block.table.exec(e);
15506
+ if (!t || !this.rules.other.tableDelimiter.test(t[2]))
15507
+ return;
15508
+ let n = se(t[1]), s = t[2].replace(this.rules.other.tableAlignChars, "").split("|"), r = t[3]?.trim() ? t[3].replace(this.rules.other.tableRowBlankLine, "").split(`
15509
+ `) : [], o = { type: "table", raw: L(t[0], `
15510
+ `), header: [], align: [], rows: [] };
15511
+ if (n.length === s.length) {
15512
+ for (let i of s)
15513
+ this.rules.other.tableAlignRight.test(i) ? o.align.push("right") : this.rules.other.tableAlignCenter.test(i) ? o.align.push("center") : this.rules.other.tableAlignLeft.test(i) ? o.align.push("left") : o.align.push(null);
15514
+ for (let i = 0;i < n.length; i++)
15515
+ o.header.push({ text: n[i], tokens: this.lexer.inline(n[i]), header: true, align: o.align[i] });
15516
+ for (let i of r)
15517
+ o.rows.push(se(i, o.header.length).map((u, a) => ({ text: u, tokens: this.lexer.inline(u), header: false, align: o.align[a] })));
15518
+ return o;
15519
+ }
15520
+ }
15521
+ lheading(e) {
15522
+ let t = this.rules.block.lheading.exec(e);
15523
+ if (t) {
15524
+ let n = t[1].trim();
15525
+ return { type: "heading", raw: L(t[0], `
15526
+ `), depth: t[2].charAt(0) === "=" ? 1 : 2, text: n, tokens: this.lexer.inline(n) };
15527
+ }
15528
+ }
15529
+ paragraph(e) {
15530
+ let t = this.rules.block.paragraph.exec(e);
15531
+ if (t) {
15532
+ let n = t[1].charAt(t[1].length - 1) === `
15533
+ ` ? t[1].slice(0, -1) : t[1];
15534
+ return { type: "paragraph", raw: t[0], text: n, tokens: this.lexer.inline(n) };
15535
+ }
15536
+ }
15537
+ text(e) {
15538
+ let t = this.rules.block.text.exec(e);
15539
+ if (t)
15540
+ return { type: "text", raw: t[0], text: t[0], tokens: this.lexer.inline(t[0]) };
15541
+ }
15542
+ escape(e) {
15543
+ let t = this.rules.inline.escape.exec(e);
15544
+ if (t)
15545
+ return { type: "escape", raw: t[0], text: t[1] };
15546
+ }
15547
+ tag(e) {
15548
+ let t = this.rules.inline.tag.exec(e);
15549
+ if (t)
15550
+ return !this.lexer.state.inLink && this.rules.other.startATag.test(t[0]) ? this.lexer.state.inLink = true : this.lexer.state.inLink && this.rules.other.endATag.test(t[0]) && (this.lexer.state.inLink = false), !this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(t[0]) ? this.lexer.state.inRawBlock = true : this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(t[0]) && (this.lexer.state.inRawBlock = false), { type: "html", raw: t[0], inLink: this.lexer.state.inLink, inRawBlock: this.lexer.state.inRawBlock, block: false, text: t[0] };
15551
+ }
15552
+ link(e) {
15553
+ if (this.lexer.state.linkParenPossible === false)
15554
+ return;
15555
+ let t = this.rules.inline.link.exec(e);
15556
+ if (t) {
15557
+ let n = t[0].charAt(0) === "!" ? 2 : 1;
15558
+ if (!this.options.pedantic && we(e, t[1], n, this.rules))
15559
+ return;
15560
+ let s = t[2].trim();
15561
+ if (!this.options.pedantic && this.rules.other.startAngleBracket.test(s)) {
15562
+ if (!this.rules.other.endAngleBracket.test(s))
15563
+ return;
15564
+ let i = L(s.slice(0, -1), "\\");
15565
+ if ((s.length - i.length) % 2 === 0)
15566
+ return;
15567
+ } else {
15568
+ let i = oe(t[2], "()");
15569
+ if (i === -2)
15570
+ return;
15571
+ if (i > -1) {
15572
+ let a = (t[0].indexOf("!") === 0 ? 5 : 4) + t[1].length + i;
15573
+ t[2] = t[2].substring(0, i), t[0] = t[0].substring(0, a).trim(), t[3] = "";
15574
+ }
15575
+ }
15576
+ let r = t[2], o = "";
15577
+ if (this.options.pedantic) {
15578
+ let i = this.rules.other.pedanticHrefTitle.exec(r);
15579
+ i && (r = i[1], o = i[3]);
15580
+ } else
15581
+ o = t[3] ? t[3].slice(1, -1) : "";
15582
+ return r = r.trim(), this.rules.other.startAngleBracket.test(r) && (this.options.pedantic && !this.rules.other.endAngleBracket.test(s) ? r = r.slice(1) : r = r.slice(1, -1)), Oe(t, { href: r && r.replace(this.rules.inline.anyPunctuation, "$1"), title: o && o.replace(this.rules.inline.anyPunctuation, "$1") }, t[0], this.lexer, this.rules);
15583
+ }
15584
+ }
15585
+ reflink(e, t) {
15586
+ let n;
15587
+ if ((n = this.rules.inline.reflink.exec(e)) || (n = this.rules.inline.nolink.exec(e))) {
15588
+ let s = n[0].charAt(0) === "!" ? 2 : 1;
15589
+ if (!this.options.pedantic && we(e, n[1], s, this.rules))
15590
+ return;
15591
+ let r = (n[2] || n[1]).replace(this.rules.other.multipleSpaceGlobal, " "), o = t[q(r)];
15592
+ if (!o) {
15593
+ let i = n[0].charAt(0);
15594
+ return { type: "text", raw: i, text: i };
15595
+ }
15596
+ return Oe(n, o, n[0], this.lexer, this.rules);
15597
+ }
15598
+ }
15599
+ emStrong(e, t, n = "") {
15600
+ let s = this.rules.inline.emStrongLDelim.exec(e);
15601
+ if (!s || !s[1] && !s[2] && !s[3] && !s[4] || s[4] && n.match(this.rules.other.unicodeAlphaNumeric))
15602
+ return;
15603
+ if (!(s[1] || s[3] || "") || !n || this.rules.inline.punctuation.exec(n)) {
15604
+ let o = [...s[0]].length - 1, i, u, a = o, p = 0, c = s[0][0], d = n === c, m = c === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
15605
+ for (m.lastIndex = 0, t = t.slice(-1 * e.length + o);(s = m.exec(t)) !== null; ) {
15606
+ if (i = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !i)
15607
+ continue;
15608
+ if (u = [...i].length, s[3] || s[4]) {
15609
+ a += u;
15610
+ continue;
15611
+ } else if (s[5] || s[6]) {
15612
+ if (o % 3 && !((o + u) % 3)) {
15613
+ p += u;
15614
+ continue;
15615
+ }
15616
+ if (d)
15617
+ break;
15618
+ }
15619
+ if (a -= u, a > 0)
15620
+ continue;
15621
+ u = Math.min(u, u + a + p);
15622
+ let b = [...s[0]][0].length, g = e.slice(0, o + s.index + b + u);
15623
+ if (Math.min(o, u) % 2) {
15624
+ let f = g.slice(1, -1);
15625
+ return { type: "em", raw: g, text: f, tokens: this.lexer.inlineTokens(f) };
15626
+ }
15627
+ let w = g.slice(2, -2);
15628
+ return { type: "strong", raw: g, text: w, tokens: this.lexer.inlineTokens(w) };
15629
+ }
15630
+ }
15631
+ }
15632
+ codespan(e) {
15633
+ let t = this.rules.inline.code.exec(e);
15634
+ if (t) {
15635
+ let n = t[2].replace(this.rules.other.newLineCharGlobal, " "), s = this.rules.other.nonSpaceChar.test(n), r = this.rules.other.startingSpaceChar.test(n) && this.rules.other.endingSpaceChar.test(n);
15636
+ return s && r && (n = n.substring(1, n.length - 1)), { type: "codespan", raw: t[0], text: n };
15637
+ }
15638
+ }
15639
+ br(e) {
15640
+ let t = this.rules.inline.br.exec(e);
15641
+ if (t)
15642
+ return { type: "br", raw: t[0] };
15643
+ }
15644
+ del(e, t, n = "") {
15645
+ let s = this.rules.inline.delLDelim.exec(e);
15646
+ if (!s)
15647
+ return;
15648
+ if (!(s[1] || "") || !n || this.rules.inline.punctuation.exec(n)) {
15649
+ let o = [...s[0]].length - 1, i, u, a = o, p = this.rules.inline.delRDelim;
15650
+ for (p.lastIndex = 0, t = t.slice(-1 * e.length + o);(s = p.exec(t)) !== null; ) {
15651
+ if (i = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !i || (u = [...i].length, u !== o))
15652
+ continue;
15653
+ if (s[3] || s[4]) {
15654
+ a += u;
15655
+ continue;
15656
+ }
15657
+ if (a -= u, a > 0)
15658
+ continue;
15659
+ u = Math.min(u, u + a);
15660
+ let c = [...s[0]][0].length, d = e.slice(0, o + s.index + c + u), m = d.slice(o, -o);
15661
+ return { type: "del", raw: d, text: m, tokens: this.lexer.inlineTokens(m) };
15662
+ }
15663
+ }
15664
+ }
15665
+ autolink(e) {
15666
+ let t = this.rules.inline.autolink.exec(e);
15667
+ if (t) {
15668
+ let n, s;
15669
+ return t[2] === "@" ? (n = t[1], s = "mailto:" + n) : (n = t[1], s = n), { type: "link", raw: t[0], text: n, href: s, autolink: true, tokens: [{ type: "text", raw: n, text: n }] };
15670
+ }
15671
+ }
15672
+ url(e) {
15673
+ let t;
15674
+ if (t = this.rules.inline.url.exec(e)) {
15675
+ let n, s;
15676
+ if (t[2] === "@")
15677
+ n = t[0], s = "mailto:" + n;
15678
+ else {
15679
+ let r;
15680
+ do
15681
+ r = t[0], t[0] = this.rules.inline._backpedal.exec(t[0])?.[0] ?? "";
15682
+ while (r !== t[0]);
15683
+ n = t[0], t[1] === "www." ? s = "http://" + t[0] : s = t[0];
15684
+ }
15685
+ return { type: "link", raw: t[0], text: n, href: s, autolink: true, tokens: [{ type: "text", raw: n, text: n }] };
15686
+ }
15687
+ }
15688
+ inlineText(e) {
15689
+ let t = this.rules.inline.text.exec(e);
15690
+ if (t) {
15691
+ let n = this.lexer.state.inRawBlock;
15692
+ return { type: "text", raw: t[0], text: n ? t[0] : Te(t[0]), escaped: n };
15693
+ }
15694
+ }
15695
+ }, R = class l {
15696
+ tokens;
15697
+ options;
15698
+ state;
15699
+ inlineQueue;
15700
+ tokenizer;
15701
+ constructor(e) {
15702
+ this.tokens = [], this.tokens.links = Object.create(null), this.options = e || P, this.options.tokenizer = this.options.tokenizer || new y, this.tokenizer = this.options.tokenizer, this.tokenizer.options = this.options, this.tokenizer.lexer = this, this.inlineQueue = [], this.state = { inLink: false, inRawBlock: false, linkEmitted: false, linkParenPossible: true, top: true };
15703
+ let t = { other: x, block: j.normal, inline: D.normal };
15704
+ this.options.pedantic ? (t.block = j.pedantic, t.inline = D.pedantic) : this.options.gfm && (t.block = j.gfm, this.options.breaks ? t.inline = D.breaks : t.inline = D.gfm), this.tokenizer.rules = t;
15705
+ }
15706
+ static get rules() {
15707
+ return { block: j, inline: D };
15708
+ }
15709
+ static lex(e, t) {
15710
+ return new l(t).lex(e);
15711
+ }
15712
+ static lexInline(e, t) {
15713
+ return new l(t).inlineTokens(e);
15714
+ }
15715
+ lex(e) {
15716
+ e = e.replace(x.carriageReturn, `
15717
+ `), this.blockTokens(e, this.tokens);
15718
+ for (let t = 0;t < this.inlineQueue.length; t++) {
15719
+ let n = this.inlineQueue[t];
15720
+ this.inlineTokens(n.src, n.tokens);
15721
+ }
15722
+ return this.inlineQueue = [], this.tokens;
15723
+ }
15724
+ blockTokens(e, t = [], n = false) {
15725
+ this.tokenizer.lexer = this, this.options.pedantic && (e = e.replace(x.tabCharGlobal, " ").replace(x.spaceLine, ""));
15726
+ let s = 1 / 0;
15727
+ for (;e; ) {
15728
+ if (e.length < s)
15729
+ s = e.length;
15730
+ else {
15731
+ this.infiniteLoopError(e.charCodeAt(0));
15732
+ break;
15733
+ }
15734
+ let r;
15735
+ if (this.options.extensions?.block?.some((i) => (r = i.call({ lexer: this }, e, t)) ? (e = e.substring(r.raw.length), t.push(r), true) : false))
15736
+ continue;
15737
+ if (r = this.tokenizer.space(e)) {
15738
+ e = e.substring(r.raw.length);
15739
+ let i = t.at(-1);
15740
+ r.raw.length === 1 && i !== undefined ? i.raw += `
15741
+ ` : t.push(r);
15742
+ continue;
15743
+ }
15744
+ if (r = this.tokenizer.code(e)) {
15745
+ e = e.substring(r.raw.length);
15746
+ let i = t.at(-1);
15747
+ i?.type === "paragraph" || i?.type === "text" ? (i.raw += (i.raw.endsWith(`
15748
+ `) ? "" : `
15749
+ `) + r.raw, i.text += `
15750
+ ` + r.text, this.inlineQueue.at(-1).src = i.text) : t.push(r);
15751
+ continue;
15752
+ }
15753
+ if (r = this.tokenizer.fences(e)) {
15754
+ e = e.substring(r.raw.length), t.push(r);
15755
+ continue;
15756
+ }
15757
+ if (r = this.tokenizer.heading(e)) {
15758
+ e = e.substring(r.raw.length), t.push(r);
15759
+ continue;
15760
+ }
15761
+ if (r = this.tokenizer.hr(e)) {
15762
+ e = e.substring(r.raw.length), t.push(r);
15763
+ continue;
15764
+ }
15765
+ if (r = this.tokenizer.blockquote(e)) {
15766
+ e = e.substring(r.raw.length), t.push(r);
15767
+ continue;
15768
+ }
15769
+ if (r = this.tokenizer.list(e)) {
15770
+ e = e.substring(r.raw.length), t.push(r);
15771
+ continue;
15772
+ }
15773
+ if (r = this.tokenizer.html(e)) {
15774
+ e = e.substring(r.raw.length), t.push(r);
15775
+ continue;
15776
+ }
15777
+ if (r = this.tokenizer.def(e)) {
15778
+ e = e.substring(r.raw.length);
15779
+ let i = t.at(-1);
15780
+ i?.type === "paragraph" || i?.type === "text" ? (i.raw += (i.raw.endsWith(`
15781
+ `) ? "" : `
15782
+ `) + r.raw, i.text += `
15783
+ ` + r.raw, this.inlineQueue.at(-1).src = i.text) : this.tokens.links[r.tag] || (this.tokens.links[r.tag] = { href: r.href, title: r.title }, t.push(r));
15784
+ continue;
15785
+ }
15786
+ if (r = this.tokenizer.table(e)) {
15787
+ e = e.substring(r.raw.length), t.push(r);
15788
+ continue;
15789
+ }
15790
+ if (r = this.tokenizer.lheading(e)) {
15791
+ e = e.substring(r.raw.length), t.push(r);
15792
+ continue;
15793
+ }
15794
+ let o = e;
15795
+ if (this.options.extensions?.startBlock) {
15796
+ let i = 1 / 0, u = e.slice(1), a;
15797
+ this.options.extensions.startBlock.forEach((p) => {
15798
+ a = p.call({ lexer: this }, u), typeof a == "number" && a >= 0 && (i = Math.min(i, a));
15799
+ }), i < 1 / 0 && i >= 0 && (o = e.substring(0, i + 1));
15800
+ }
15801
+ if (this.state.top && (r = this.tokenizer.paragraph(o))) {
15802
+ let i = t.at(-1);
15803
+ n && i?.type === "paragraph" ? (i.raw += (i.raw.endsWith(`
15804
+ `) ? "" : `
15805
+ `) + r.raw, i.text += `
15806
+ ` + r.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = i.text) : t.push(r), n = o.length !== e.length, e = e.substring(r.raw.length);
15807
+ continue;
15808
+ }
15809
+ if (r = this.tokenizer.text(e)) {
15810
+ e = e.substring(r.raw.length);
15811
+ let i = t.at(-1);
15812
+ i?.type === "text" ? (i.raw += (i.raw.endsWith(`
15813
+ `) ? "" : `
15814
+ `) + r.raw, i.text += `
15815
+ ` + r.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = i.text) : t.push(r);
15816
+ continue;
15817
+ }
15818
+ if (e) {
15819
+ this.infiniteLoopError(e.charCodeAt(0));
15820
+ break;
15821
+ }
15822
+ }
15823
+ return this.state.top = true, t;
15824
+ }
15825
+ inline(e, t = []) {
15826
+ return this.inlineQueue.push({ src: e, tokens: t }), t;
15827
+ }
15828
+ linkInText(e) {
15829
+ if (!e.includes("["))
15830
+ return false;
15831
+ let t = this.tokenizer.rules.inline.link;
15832
+ for (let n of e.matchAll(this.tokenizer.rules.inline.blockSkip))
15833
+ if (t.test(n[0]) && e.charAt(n.index - 1) !== "!")
15834
+ return true;
15835
+ for (let n of e.matchAll(this.tokenizer.rules.inline.reflinkSearch)) {
15836
+ let s = n[0], r = s.lastIndexOf("[");
15837
+ if (!(s.charAt(0) === "!" || !Object.hasOwn(this.tokens.links, q(s.slice(r + 1, -1)))) && !(r > 1 && this.linkInText(s.slice(1, r - 1))))
15838
+ return true;
15839
+ }
15840
+ return false;
15841
+ }
15842
+ inlineTokens(e, t = []) {
15843
+ this.tokenizer.lexer = this;
15844
+ let n = this.state.linkParenPossible;
15845
+ this.state.linkParenPossible = n && e.includes(")");
15846
+ try {
15847
+ return this.#e(e, t);
15848
+ } finally {
15849
+ this.state.linkParenPossible = n;
15850
+ }
15851
+ }
15852
+ #e(e, t) {
15853
+ let n = e;
15854
+ if (this.tokens.links && e.includes("[")) {
15855
+ let i = this.tokenizer.rules.inline.reflinkSearch, u = (a) => {
15856
+ let p = a.lastIndexOf("[");
15857
+ if (!Object.hasOwn(this.tokens.links, q(a.slice(p + 1, -1))))
15858
+ return a;
15859
+ if (p > 1 && a.charAt(0) !== "!") {
15860
+ let c = a.slice(1, p - 1);
15861
+ if (this.linkInText(c))
15862
+ return "[" + c.replace(i, u) + "][" + "a".repeat(a.length - p - 2) + "]";
15863
+ }
15864
+ return "[" + "a".repeat(a.length - 2) + "]";
15865
+ };
15866
+ n = n.replace(i, u);
15867
+ }
15868
+ n = n.replace(this.tokenizer.rules.inline.anyPunctuation, (i) => "+".repeat(i.length)), n = n.replace(this.tokenizer.rules.inline.blockSkip, (i, u, a) => {
15869
+ let p = a ? a.length : 0;
15870
+ return i.slice(0, p) + "[" + "a".repeat(i.length - p - 2) + "]";
15871
+ }), n = this.options.hooks?.emStrongMask?.call({ lexer: this }, n) ?? n;
15872
+ let s = false, r = "", o = 1 / 0;
15873
+ for (;e; ) {
15874
+ if (e.length < o)
15875
+ o = e.length;
15876
+ else {
15877
+ this.infiniteLoopError(e.charCodeAt(0));
15878
+ break;
15879
+ }
15880
+ s || (r = ""), s = false;
15881
+ let i;
15882
+ if (this.options.extensions?.inline?.some((a) => (i = a.call({ lexer: this }, e, t)) ? (e = e.substring(i.raw.length), t.push(i), true) : false))
15883
+ continue;
15884
+ if (i = this.tokenizer.escape(e)) {
15885
+ e = e.substring(i.raw.length), t.push(i);
15886
+ continue;
15887
+ }
15888
+ if (i = this.tokenizer.tag(e)) {
15889
+ e = e.substring(i.raw.length), t.push(i);
15890
+ continue;
15891
+ }
15892
+ if (i = this.tokenizer.link(e)) {
15893
+ e = e.substring(i.raw.length), t.push(i);
15894
+ continue;
15895
+ }
15896
+ if (i = this.tokenizer.reflink(e, this.tokens.links)) {
15897
+ e = e.substring(i.raw.length);
15898
+ let a = t.at(-1);
15899
+ i.type === "text" && a?.type === "text" ? (a.raw += i.raw, a.text += i.text) : t.push(i);
15900
+ continue;
15901
+ }
15902
+ if (i = this.tokenizer.emStrong(e, n, r)) {
15903
+ e = e.substring(i.raw.length), t.push(i);
15904
+ continue;
15905
+ }
15906
+ if (i = this.tokenizer.codespan(e)) {
15907
+ e = e.substring(i.raw.length), t.push(i);
15908
+ continue;
15909
+ }
15910
+ if (i = this.tokenizer.br(e)) {
15911
+ e = e.substring(i.raw.length), t.push(i);
15912
+ continue;
15913
+ }
15914
+ if (i = this.tokenizer.del(e, n, r)) {
15915
+ e = e.substring(i.raw.length), t.push(i);
15916
+ continue;
15917
+ }
15918
+ if (i = this.tokenizer.autolink(e)) {
15919
+ e = e.substring(i.raw.length), t.push(i);
15920
+ continue;
15921
+ }
15922
+ if (!this.state.inLink && (i = this.tokenizer.url(e))) {
15923
+ e = e.substring(i.raw.length), t.push(i);
15924
+ continue;
15925
+ }
15926
+ let u = e;
15927
+ if (this.options.extensions?.startInline) {
15928
+ let a = 1 / 0, p = e.slice(1), c;
15929
+ this.options.extensions.startInline.forEach((d) => {
15930
+ c = d.call({ lexer: this }, p), typeof c == "number" && c >= 0 && (a = Math.min(a, c));
15931
+ }), a < 1 / 0 && a >= 0 && (u = e.substring(0, a + 1));
15932
+ }
15933
+ if (i = this.tokenizer.inlineText(u)) {
15934
+ e = e.substring(i.raw.length), i.raw.slice(-1) !== "_" && (r = i.raw.slice(-1)), s = true;
15935
+ let a = t.at(-1);
15936
+ a?.type === "text" ? (a.raw += i.raw, a.text += i.text) : t.push(i);
15937
+ continue;
15938
+ }
15939
+ if (e) {
15940
+ this.infiniteLoopError(e.charCodeAt(0));
15941
+ break;
15942
+ }
15943
+ }
15944
+ return t;
15945
+ }
15946
+ infiniteLoopError(e) {
15947
+ let t = "Infinite loop on byte: " + e;
15948
+ if (this.options.silent)
15949
+ console.error(t);
15950
+ else
15951
+ throw new Error(t);
15952
+ }
15953
+ }, S = class {
15954
+ options;
15955
+ parser;
15956
+ constructor(e) {
15957
+ this.options = e || P;
15958
+ }
15959
+ space(e) {
15960
+ return "";
15961
+ }
15962
+ code({ text: e, lang: t, escaped: n }) {
15963
+ let s = (t || "").match(x.notSpaceStart)?.[0], r = e ? e.replace(x.endingNewline, "") + `
15964
+ ` : "";
15965
+ return s ? '<pre><code class="language-' + O(s) + '">' + (n ? r : O(r, true)) + `</code></pre>
15966
+ ` : "<pre><code>" + (n ? r : O(r, true)) + `</code></pre>
15967
+ `;
15968
+ }
15969
+ blockquote({ tokens: e }) {
15970
+ return `<blockquote>
15971
+ ${this.parser.parse(e)}</blockquote>
15972
+ `;
15973
+ }
15974
+ html({ text: e }) {
15975
+ return e;
15976
+ }
15977
+ def(e) {
15978
+ return "";
15979
+ }
15980
+ heading({ tokens: e, depth: t }) {
15981
+ return `<h${t}>${this.parser.parseInline(e)}</h${t}>
15982
+ `;
15983
+ }
15984
+ hr(e) {
15985
+ return `<hr>
15986
+ `;
15987
+ }
15988
+ list(e) {
15989
+ let { ordered: t, start: n } = e, s = "";
15990
+ for (let i = 0;i < e.items.length; i++) {
15991
+ let u = e.items[i];
15992
+ s += this.listitem(u);
15993
+ }
15994
+ let r = t ? "ol" : "ul", o = t && n !== 1 ? ' start="' + n + '"' : "";
15995
+ return "<" + r + o + `>
15996
+ ` + s + "</" + r + `>
15997
+ `;
15998
+ }
15999
+ listitem(e) {
16000
+ return `<li>${this.parser.parse(e.tokens)}</li>
16001
+ `;
16002
+ }
16003
+ checkbox({ checked: e }) {
16004
+ return "<input " + (e ? 'checked="" ' : "") + 'disabled="" type="checkbox"> ';
16005
+ }
16006
+ paragraph({ tokens: e }) {
16007
+ return `<p>${this.parser.parseInline(e)}</p>
16008
+ `;
16009
+ }
16010
+ table(e) {
16011
+ let t = "", n = "";
16012
+ for (let r = 0;r < e.header.length; r++)
16013
+ n += this.tablecell(e.header[r]);
16014
+ t += this.tablerow({ text: n });
16015
+ let s = "";
16016
+ for (let r = 0;r < e.rows.length; r++) {
16017
+ let o = e.rows[r];
16018
+ n = "";
16019
+ for (let i = 0;i < o.length; i++)
16020
+ n += this.tablecell(o[i]);
16021
+ s += this.tablerow({ text: n });
16022
+ }
16023
+ return s && (s = `<tbody>${s}</tbody>`), `<table>
16024
+ <thead>
16025
+ ` + t + `</thead>
16026
+ ` + s + `</table>
16027
+ `;
16028
+ }
16029
+ tablerow({ text: e }) {
16030
+ return `<tr>
16031
+ ${e}</tr>
16032
+ `;
16033
+ }
16034
+ tablecell(e) {
16035
+ let t = this.parser.parseInline(e.tokens), n = e.header ? "th" : "td";
16036
+ return (e.align ? `<${n} align="${e.align}">` : `<${n}>`) + t + `</${n}>
16037
+ `;
16038
+ }
16039
+ strong({ tokens: e }) {
16040
+ return `<strong>${this.parser.parseInline(e)}</strong>`;
16041
+ }
16042
+ em({ tokens: e }) {
16043
+ return `<em>${this.parser.parseInline(e)}</em>`;
16044
+ }
16045
+ codespan({ text: e }) {
16046
+ return `<code>${O(e, true)}</code>`;
16047
+ }
16048
+ br(e) {
16049
+ return "<br>";
16050
+ }
16051
+ del({ tokens: e }) {
16052
+ return `<del>${this.parser.parseInline(e)}</del>`;
16053
+ }
16054
+ link({ href: e, title: t, text: n, tokens: s, autolink: r }) {
16055
+ let o = r ? O(n, true) : this.parser.parseInline(s), i = re(e);
16056
+ if (i === null)
16057
+ return o;
16058
+ e = O(i, r);
16059
+ let u = '<a href="' + e + '"';
16060
+ return t && (u += ' title="' + O(t) + '"'), u += ">" + o + "</a>", u;
16061
+ }
16062
+ image({ href: e, title: t, text: n, tokens: s }) {
16063
+ s && (n = this.parser.parseInline(s, this.parser.textRenderer));
16064
+ let r = re(e);
16065
+ if (r === null)
16066
+ return O(n);
16067
+ e = r;
16068
+ let o = `<img src="${O(e)}" alt="${O(n)}"`;
16069
+ return t && (o += ` title="${O(t)}"`), o += ">", o;
16070
+ }
16071
+ text(e) {
16072
+ return "tokens" in e && e.tokens ? this.parser.parseInline(e.tokens) : ("escaped" in e) && e.escaped ? e.text : O(e.text);
16073
+ }
16074
+ }, z = class {
16075
+ strong({ text: e }) {
16076
+ return e;
16077
+ }
16078
+ em({ text: e }) {
16079
+ return e;
16080
+ }
16081
+ codespan({ text: e }) {
16082
+ return e;
16083
+ }
16084
+ del({ text: e }) {
16085
+ return e;
16086
+ }
16087
+ html({ text: e }) {
16088
+ return e;
16089
+ }
16090
+ text({ text: e }) {
16091
+ return e;
16092
+ }
16093
+ link({ text: e }) {
16094
+ return "" + e;
16095
+ }
16096
+ image({ text: e }) {
16097
+ return "" + e;
16098
+ }
16099
+ br() {
16100
+ return "";
16101
+ }
16102
+ checkbox({ raw: e }) {
16103
+ return e;
16104
+ }
16105
+ }, T = class l {
16106
+ options;
16107
+ renderer;
16108
+ textRenderer;
16109
+ constructor(e) {
16110
+ this.options = e || P, this.options.renderer = this.options.renderer || new S, this.renderer = this.options.renderer, this.renderer.options = this.options, this.renderer.parser = this, this.textRenderer = new z;
16111
+ }
16112
+ static parse(e, t) {
16113
+ return new l(t).parse(e);
16114
+ }
16115
+ static parseInline(e, t) {
16116
+ return new l(t).parseInline(e);
16117
+ }
16118
+ parse(e) {
16119
+ this.renderer.parser = this;
16120
+ let t = "";
16121
+ for (let n = 0;n < e.length; n++) {
16122
+ let s = e[n];
16123
+ if (this.options.extensions?.renderers?.[s.type]) {
16124
+ let o = s, i = this.options.extensions.renderers[o.type].call({ parser: this }, o);
16125
+ if (i !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "checkbox", "html", "def", "paragraph", "text"].includes(o.type)) {
16126
+ t += i || "";
16127
+ continue;
16128
+ }
16129
+ }
16130
+ let r = s;
16131
+ switch (r.type) {
16132
+ case "space": {
16133
+ t += this.renderer.space(r);
16134
+ break;
16135
+ }
16136
+ case "hr": {
16137
+ t += this.renderer.hr(r);
16138
+ break;
16139
+ }
16140
+ case "heading": {
16141
+ t += this.renderer.heading(r);
16142
+ break;
16143
+ }
16144
+ case "code": {
16145
+ t += this.renderer.code(r);
16146
+ break;
16147
+ }
16148
+ case "table": {
16149
+ t += this.renderer.table(r);
16150
+ break;
16151
+ }
16152
+ case "blockquote": {
16153
+ t += this.renderer.blockquote(r);
16154
+ break;
16155
+ }
16156
+ case "list": {
16157
+ t += this.renderer.list(r);
16158
+ break;
16159
+ }
16160
+ case "checkbox": {
16161
+ t += this.renderer.checkbox(r);
16162
+ break;
16163
+ }
16164
+ case "html": {
16165
+ t += this.renderer.html(r);
16166
+ break;
16167
+ }
16168
+ case "def": {
16169
+ t += this.renderer.def(r);
16170
+ break;
16171
+ }
16172
+ case "paragraph": {
16173
+ t += this.renderer.paragraph(r);
16174
+ break;
16175
+ }
16176
+ case "text": {
16177
+ t += this.renderer.text(r);
16178
+ break;
16179
+ }
16180
+ default: {
16181
+ let o = 'Token with "' + r.type + '" type was not found.';
16182
+ if (this.options.silent)
16183
+ return console.error(o), "";
16184
+ throw new Error(o);
16185
+ }
16186
+ }
16187
+ }
16188
+ return t;
16189
+ }
16190
+ parseInline(e, t = this.renderer) {
16191
+ this.renderer.parser = this;
16192
+ let n = "";
16193
+ for (let s = 0;s < e.length; s++) {
16194
+ let r = e[s];
16195
+ if (this.options.extensions?.renderers?.[r.type]) {
16196
+ let i = this.options.extensions.renderers[r.type].call({ parser: this }, r);
16197
+ if (i !== false || !["escape", "html", "link", "image", "checkbox", "strong", "em", "codespan", "br", "del", "text"].includes(r.type)) {
16198
+ n += i || "";
16199
+ continue;
16200
+ }
16201
+ }
16202
+ let o = r;
16203
+ switch (o.type) {
16204
+ case "escape": {
16205
+ n += t.text(o);
16206
+ break;
16207
+ }
16208
+ case "html": {
16209
+ n += t.html(o);
16210
+ break;
16211
+ }
16212
+ case "link": {
16213
+ n += t.link(o);
16214
+ break;
16215
+ }
16216
+ case "image": {
16217
+ n += t.image(o);
16218
+ break;
16219
+ }
16220
+ case "checkbox": {
16221
+ n += t.checkbox(o);
16222
+ break;
16223
+ }
16224
+ case "strong": {
16225
+ n += t.strong(o);
16226
+ break;
16227
+ }
16228
+ case "em": {
16229
+ n += t.em(o);
16230
+ break;
16231
+ }
16232
+ case "codespan": {
16233
+ n += t.codespan(o);
16234
+ break;
16235
+ }
16236
+ case "br": {
16237
+ n += t.br(o);
16238
+ break;
16239
+ }
16240
+ case "del": {
16241
+ n += t.del(o);
16242
+ break;
16243
+ }
16244
+ case "text": {
16245
+ n += t.text(o);
16246
+ break;
16247
+ }
16248
+ default: {
16249
+ let i = 'Token with "' + o.type + '" type was not found.';
16250
+ if (this.options.silent)
16251
+ return console.error(i), "";
16252
+ throw new Error(i);
16253
+ }
16254
+ }
16255
+ }
16256
+ return n;
16257
+ }
16258
+ }, _, F = class {
16259
+ defaults = I();
16260
+ options = this.setOptions;
16261
+ parse = this.parseMarkdown(true);
16262
+ parseInline = this.parseMarkdown(false);
16263
+ Parser = T;
16264
+ Renderer = S;
16265
+ TextRenderer = z;
16266
+ Lexer = R;
16267
+ Tokenizer = y;
16268
+ Hooks = _;
16269
+ constructor(...e) {
16270
+ this.use(...e);
16271
+ }
16272
+ walkTokens(e, t) {
16273
+ let n = [];
16274
+ for (let s of e)
16275
+ switch (n = n.concat(t.call(this, s)), s.type) {
16276
+ case "table": {
16277
+ let r = s;
16278
+ for (let o of r.header)
16279
+ n = n.concat(this.walkTokens(o.tokens, t));
16280
+ for (let o of r.rows)
16281
+ for (let i of o)
16282
+ n = n.concat(this.walkTokens(i.tokens, t));
16283
+ break;
16284
+ }
16285
+ case "list": {
16286
+ let r = s;
16287
+ n = n.concat(this.walkTokens(r.items, t));
16288
+ break;
16289
+ }
16290
+ default: {
16291
+ let r = s;
16292
+ this.defaults.extensions?.childTokens?.[r.type] ? this.defaults.extensions.childTokens[r.type].forEach((o) => {
16293
+ let i = r[o].flat(1 / 0);
16294
+ n = n.concat(this.walkTokens(i, t));
16295
+ }) : r.tokens && (n = n.concat(this.walkTokens(r.tokens, t)));
16296
+ }
16297
+ }
16298
+ return n;
16299
+ }
16300
+ use(...e) {
16301
+ let t = this.defaults.extensions || { renderers: {}, childTokens: {} };
16302
+ return e.forEach((n) => {
16303
+ let s = { ...n };
16304
+ if (s.async = this.defaults.async || s.async || false, n.extensions && (n.extensions.forEach((r) => {
16305
+ if (!r.name)
16306
+ throw new Error("extension name required");
16307
+ if ("renderer" in r) {
16308
+ let o = t.renderers[r.name];
16309
+ o ? t.renderers[r.name] = function(...i) {
16310
+ let u = r.renderer.apply(this, i);
16311
+ return u === false && (u = o.apply(this, i)), u;
16312
+ } : t.renderers[r.name] = r.renderer;
16313
+ }
16314
+ if ("tokenizer" in r) {
16315
+ if (!r.level || r.level !== "block" && r.level !== "inline")
16316
+ throw new Error("extension level must be 'block' or 'inline'");
16317
+ let o = t[r.level];
16318
+ o ? o.unshift(r.tokenizer) : t[r.level] = [r.tokenizer], r.start && (r.level === "block" ? t.startBlock ? t.startBlock.push(r.start) : t.startBlock = [r.start] : r.level === "inline" && (t.startInline ? t.startInline.push(r.start) : t.startInline = [r.start]));
16319
+ }
16320
+ "childTokens" in r && r.childTokens && (t.childTokens[r.name] = r.childTokens);
16321
+ }), s.extensions = t), n.renderer) {
16322
+ let r = this.defaults.renderer || new S(this.defaults);
16323
+ for (let o in n.renderer) {
16324
+ if (!(o in r))
16325
+ throw new Error(`renderer '${o}' does not exist`);
16326
+ if (["options", "parser"].includes(o))
16327
+ continue;
16328
+ let i = o, u = n.renderer[i], a = r[i];
16329
+ r[i] = (...p) => {
16330
+ let c = u.apply(r, p);
16331
+ return c === false && (c = a.apply(r, p)), c || "";
16332
+ };
16333
+ }
16334
+ s.renderer = r;
16335
+ }
16336
+ if (n.tokenizer) {
16337
+ let r = this.defaults.tokenizer || new y(this.defaults);
16338
+ for (let o in n.tokenizer) {
16339
+ if (!(o in r))
16340
+ throw new Error(`tokenizer '${o}' does not exist`);
16341
+ if (["options", "rules", "lexer"].includes(o))
16342
+ continue;
16343
+ let i = o, u = n.tokenizer[i], a = r[i];
16344
+ r[i] = (...p) => {
16345
+ let c = u.apply(r, p);
16346
+ return c === false && (c = a.apply(r, p)), c;
16347
+ };
16348
+ }
16349
+ s.tokenizer = r;
16350
+ }
16351
+ if (n.hooks) {
16352
+ let r = this.defaults.hooks || new _;
16353
+ for (let o in n.hooks) {
16354
+ if (!(o in r))
16355
+ throw new Error(`hook '${o}' does not exist`);
16356
+ if (["options", "block"].includes(o))
16357
+ continue;
16358
+ let i = o, u = n.hooks[i], a = r[i];
16359
+ _.passThroughHooks.has(o) ? r[i] = (p) => {
16360
+ if (this.defaults.async && _.passThroughHooksRespectAsync.has(o))
16361
+ return (async () => {
16362
+ let d = await u.call(r, p);
16363
+ return a.call(r, d);
16364
+ })();
16365
+ let c = u.call(r, p);
16366
+ return a.call(r, c);
16367
+ } : r[i] = (...p) => {
16368
+ if (this.defaults.async)
16369
+ return (async () => {
16370
+ let d = await u.apply(r, p);
16371
+ return d === false && (d = await a.apply(r, p)), d;
16372
+ })();
16373
+ let c = u.apply(r, p);
16374
+ return c === false && (c = a.apply(r, p)), c;
16375
+ };
16376
+ }
16377
+ s.hooks = r;
16378
+ }
16379
+ if (n.walkTokens) {
16380
+ let r = this.defaults.walkTokens, o = n.walkTokens;
16381
+ s.walkTokens = function(i) {
16382
+ let u = [];
16383
+ return u.push(o.call(this, i)), r && (u = u.concat(r.call(this, i))), u;
16384
+ };
16385
+ }
16386
+ this.defaults = { ...this.defaults, ...s };
16387
+ }), this;
16388
+ }
16389
+ setOptions(e) {
16390
+ return this.defaults = { ...this.defaults, ...e }, this;
16391
+ }
16392
+ lexer(e, t) {
16393
+ return R.lex(e, t ?? this.defaults);
16394
+ }
16395
+ parser(e, t) {
16396
+ return T.parse(e, t ?? this.defaults);
16397
+ }
16398
+ parseMarkdown(e) {
16399
+ return (n, s) => {
16400
+ let r = { ...s }, o = { ...this.defaults, ...r }, i = this.onError(!!o.silent, !!o.async);
16401
+ if (this.defaults.async === true && r.async === false)
16402
+ return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));
16403
+ if (typeof n > "u" || n === null)
16404
+ return i(new Error("marked(): input parameter is undefined or null"));
16405
+ if (typeof n != "string")
16406
+ return i(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(n) + ", string expected"));
16407
+ if (o.hooks && (o.hooks.options = o, o.hooks.block = e), o.async)
16408
+ return (async () => {
16409
+ let u = o.hooks ? await o.hooks.preprocess(n) : n, p = await (o.hooks ? await o.hooks.provideLexer(e) : e ? R.lex : R.lexInline)(u, o), c = o.hooks ? await o.hooks.processAllTokens(p) : p;
16410
+ o.walkTokens && await Promise.all(this.walkTokens(c, o.walkTokens));
16411
+ let m = await (o.hooks ? await o.hooks.provideParser(e) : e ? T.parse : T.parseInline)(c, o);
16412
+ return o.hooks ? await o.hooks.postprocess(m) : m;
16413
+ })().catch(i);
16414
+ try {
16415
+ o.hooks && (n = o.hooks.preprocess(n));
16416
+ let a = (o.hooks ? o.hooks.provideLexer(e) : e ? R.lex : R.lexInline)(n, o);
16417
+ o.hooks && (a = o.hooks.processAllTokens(a)), o.walkTokens && this.walkTokens(a, o.walkTokens);
16418
+ let c = (o.hooks ? o.hooks.provideParser(e) : e ? T.parse : T.parseInline)(a, o);
16419
+ return o.hooks && (c = o.hooks.postprocess(c)), c;
16420
+ } catch (u) {
16421
+ return i(u);
16422
+ }
16423
+ };
16424
+ }
16425
+ onError(e, t) {
16426
+ return (n) => {
16427
+ if (n.message += `
16428
+ Please report this to https://github.com/markedjs/marked.`, e) {
16429
+ let s = "<p>An error occurred:</p><pre>" + O(n.message + "", true) + "</pre>";
16430
+ return t ? Promise.resolve(s) : s;
16431
+ }
16432
+ if (t)
16433
+ return Promise.reject(n);
16434
+ throw n;
16435
+ };
16436
+ }
16437
+ }, E, gn, fn, mn, xn, Rn, Tn;
16438
+ var init_marked_esm = __esm(() => {
16439
+ P = I();
16440
+ A = { exec: () => null };
16441
+ _e = ((l = "") => {
16442
+ try {
16443
+ return !!new RegExp("(?<=1)(?<!1)" + l);
16444
+ } catch {
16445
+ return false;
16446
+ }
16447
+ })();
16448
+ x = { codeRemoveIndent: /^(?: {0,3}\t| {1,4})/gm, outputLinkReplace: /\\([\[\]])/g, indentCodeCompensation: /^(\s+)(?:```)/, beginningSpace: /^\s+/, endingHash: /#$/, startingSpaceChar: /^ /, endingSpaceChar: / $/, endingSpaceTabChar: /[ \t]$/, nonSpaceChar: /[^ ]/, newLineCharGlobal: /\n/g, tabCharGlobal: /\t/g, leadingSpaceTab: /^[ \t]+/, multipleSpaceGlobal: /\s+/g, blankLine: /^[ \t]*$/, doubleBlankLine: /\n[ \t]*\n[ \t]*$/, blockquoteStart: /^ {0,3}>/, blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g, blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm, listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g, listIsTask: /^\[[ xX]\] +\S/, listReplaceTask: /^\[[ xX]\] +/, listTaskCheckbox: /\[[ xX]\]/, anyLine: /\n.*\n/, hrefBrackets: /^<(.*)>$/, tableDelimiter: /[:|]/, tableAlignChars: /^\||\| *$/g, tableRowBlankLine: /\n[ \t]*$/, tableAlignRight: /^ *-+: *$/, tableAlignCenter: /^ *:-+: *$/, tableAlignLeft: /^ *:-+ *$/, startATag: /^<a /i, endATag: /^<\/a>/i, startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i, endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i, startAngleBracket: /^</, endAngleBracket: />$/, pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/, unicodeAlphaNumeric: /[\p{L}\p{N}]/u, numericCharacterReference: /&#(?:(\d{1,7})|[Xx]([A-Fa-f0-9]{1,6}));/g, escapeTest: /[&<>"']/, escapeReplace: /[&<>"']/g, escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/, escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g, caret: /(^|[^\[])\^/g, percentDecode: /%25/g, findPipe: /\|/g, splitPipe: / \|/, slashPipe: /\\\|/g, carriageReturn: /\r\n|\r/g, spaceLine: /^ +$/gm, notSpaceStart: /^\S*/, endingNewline: /\n$/, listItemRegex: (l) => new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`), nextBulletRegex: C((l) => new RegExp(`^ {0,${l}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`)), hrRegex: C((l) => new RegExp(`^ {0,${l}}((?:-[ ]*){3,}|(?:_[ ]*){3,}|(?:\\*[ ]*){3,})(?:\\n+|$)`)), fencesBeginRegex: C((l) => new RegExp(`^ {0,${l}}(?:\`\`\`|~~~)`)), headingBeginRegex: C((l) => new RegExp(`^ {0,${l}}#`)), htmlBeginRegex: C((l) => new RegExp(`^ {0,${l}}(?:</?(?:${N})(?: +|$|/?>)|<(?:script|pre|style|textarea|!--))`, "i")), blockquoteBeginRegex: C((l) => new RegExp(`^ {0,${l}}>`)) };
16449
+ $e = /^(?:[ \t]*(?:\n|$))+/;
16450
+ Le = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/;
16451
+ ze = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
16452
+ G = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
16453
+ Ae = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
16454
+ J = / {0,3}(?:[*+-]|\d{1,9}[.)])/;
16455
+ he = /^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |fences|blockquote|heading|hr|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/;
16456
+ de = h(he).replace(/bull/g, J).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}(?:\s|$)/).replace(/hr/g, / {0,3}(?:(?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/\|table/g, "").getRegex();
16457
+ Ee = h(he).replace(/bull/g, J).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}(?:\s|$)/).replace(/hr/g, / {0,3}(?:(?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/table/g, / {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex();
16458
+ V = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|[ \t]+\n)[^\n]+)*)/;
16459
+ Me = /^[^\n]+/;
16460
+ Y = /(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/;
16461
+ Ie = h(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label", Y).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex();
16462
+ Ce = h(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g, J).getRegex();
16463
+ ee = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
16464
+ Be = h("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n*|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)|<![A-Z][\\s\\S]*?(?:>[^\\n]*\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>[^\\n]*\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][a-z0-9-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][a-z0-9-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))", "i").replace("comment", ee).replace("tag", N).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
16465
+ De = ke(/ {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]/);
16466
+ qe = ke(/ {0,3}(?:[*+-]|\d{1,9}[.)])(?:[ \t]|\n|$)/);
16467
+ ve = h(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", qe).getRegex();
16468
+ te = { blockquote: ve, code: Le, def: Ie, fences: ze, heading: Ae, hr: G, html: Be, lheading: de, list: Ce, newline: $e, paragraph: De, table: A, text: Me };
16469
+ ue = h("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", G).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", "(?: {4}| {0,3}\t)[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", N).getRegex();
16470
+ Ze = { ...te, lheading: Ee, table: ue, paragraph: h(V).replace("hr", G).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", ue).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", N).getRegex() };
16471
+ He = { ...te, html: h(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", ee).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(), def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, fences: A, lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, paragraph: h(V).replace("hr", G).replace("heading", ` *#{1,6} *[^
16472
+ ]`).replace("lheading", de).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex() };
16473
+ Ge = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
16474
+ Ne = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
16475
+ ge = /^( {2,}|\\)\n(?!\s*$)[ \t]*/;
16476
+ Qe = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
16477
+ $ = /[\p{P}\p{S}]/u;
16478
+ B = /[\s\p{P}\p{S}]/u;
16479
+ Q = /[^\s\p{P}\p{S}]/u;
16480
+ je = h(/^((?![*_])punctSpace)/, "u").replace(/punctSpace/g, B).getRegex();
16481
+ Fe = /[\p{Pi}\p{Ps}"']/u;
16482
+ fe = /(?!~)[\p{P}\p{S}]/u;
16483
+ Ue = /(?!~)[\s\p{P}\p{S}]/u;
16484
+ Ke = /(?:[^\s\p{P}\p{S}]|~)/u;
16485
+ We = h(/link|precode-code|html/, "g").replace("link", /\[(?:[^\[\]`]|(?<a>`+)[^`]+\k<a>(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-", _e ? "(?<!`)()" : "(^^|[^`])").replace("code", /(?<b>`+)[^`]+\k<b>(?!`)/).replace("html", /<(?! )[^<>]*?>/).getRegex();
16486
+ me = /^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/;
16487
+ Xe = h(me, "u").replace(/punct/g, $).getRegex();
16488
+ Je = h(me, "u").replace(/punct/g, fe).getRegex();
16489
+ Ve = /^(?:\*+(?:((?!\*)(?!openQuote)punct)|([^\s*]))?)|^_+(?:((?!_)(?!openQuote)punct)|([^\s_]))?/;
16490
+ Ye = h(Ve, "u").replace(/openQuote/g, Fe).replace(/punct/g, $).getRegex();
16491
+ et = h(xe, "gu").replace(/notPunctSpace/g, Q).replace(/punctSpace/g, B).replace(/punct/g, $).getRegex();
16492
+ tt = h(xe, "gu").replace(/notPunctSpace/g, Ke).replace(/punctSpace/g, Ue).replace(/punct/g, fe).getRegex();
16493
+ rt = h(nt, "gu").replace(/notPunctSpace/g, Q).replace(/punctSpace/g, B).replace(/punct/g, $).getRegex();
16494
+ st = h("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)", "gu").replace(/notPunctSpace/g, Q).replace(/punctSpace/g, B).replace(/punct/g, $).getRegex();
16495
+ ot = h(it, "gu").replace(/notPunctSpace/g, Q).replace(/punctSpace/g, B).replace(/punct/g, $).getRegex();
16496
+ at = h(/^~~?(?:((?!~)punct)|[^\s~])/, "u").replace(/punct/g, $).getRegex();
16497
+ ut = h(lt, "gu").replace(/notPunctSpace/g, Q).replace(/punctSpace/g, B).replace(/punct/g, $).getRegex();
16498
+ pt = h(/\\(punct)/, "gu").replace(/punct/g, $).getRegex();
16499
+ ct = h(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex();
16500
+ ht = h(ee).replace("(?:-->|$)", "-->").getRegex();
16501
+ dt = h("^comment|^</[a-zA-Z][a-zA-Z0-9-]*\\s*>|^<[a-zA-Z][a-zA-Z0-9-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment", ht).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex();
16502
+ be = /\[(?:\\[\s\S]|[^\[\]\\])*\]/;
16503
+ U = h(/(?:\[(?:brackets|\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/).replace("brackets", be).getRegex();
16504
+ kt = h(/^!?\[(label)\]\([ \t\n]*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?[ \t\n]*\)/).replace("label", U).replace("href", /<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]+|(?=\))/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex();
16505
+ gt = h(/^!?\[(label)\]\[(ref)\]/).replace("label", U).replace("ref", Y).getRegex();
16506
+ ft = h(/^!?\[(ref)\](?:\[\])?/).replace("ref", Y).getRegex();
16507
+ pe = /(?!\s*\])(?:\\[\s\S]|[^\[\]\\]){1,999}/;
16508
+ mt = h(/(?:[^\[\]\\`]*(?:\[(?:brackets|\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\]))){0,999}?[^\[\]\\`]*?/).replace("brackets", be).getRegex();
16509
+ xt = h("reflink|nolink(?!\\()", "g").replace("reflink", h(/^!?\[(label)\]\[(ref)\]/).replace("label", mt).replace("ref", pe).getRegex()).replace("nolink", h(/^!?\[(ref)\](?:\[\])?/).replace("ref", pe).getRegex()).getRegex();
16510
+ ce = /[hH][tT][tT][pP][sS]?|[fF][tT][pP]/;
16511
+ bt = /[A-Za-z0-9._+-]+@[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![\w-])/;
16512
+ Rt = h(/(?:mailto:email|xmpp:email(?:\/[A-Za-z0-9@.]+)?)/).replace(/email/g, bt).getRegex();
16513
+ ne = { _backpedal: A, anyPunctuation: pt, autolink: ct, blockSkip: We, br: ge, code: Ne, del: A, delLDelim: A, delRDelim: A, emStrongLDelim: Xe, emStrongRDelimAst: et, emStrongRDelimUnd: st, escape: Ge, link: kt, nolink: ft, punctuation: je, reflink: gt, reflinkSearch: xt, tag: dt, text: Qe, url: A };
16514
+ Tt = { ...ne, emStrongLDelim: Ye, emStrongRDelimAst: rt, emStrongRDelimUnd: ot, link: h(/^!?\[(label)\]\((.*?)\)/).replace("label", U).getRegex(), reflink: h(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", U).getRegex() };
16515
+ X = { ...ne, emStrongRDelimAst: tt, emStrongLDelim: Je, delLDelim: at, delRDelim: ut, url: h(/^emailProtocol|^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("emailProtocol", Rt).replace("protocol", ce).replace("email", /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![\w-])/).getRegex(), _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/, del: /^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/, text: h(/^(?:[^a-zA-Z0-9](?=emailProtocol)|(`+|~+|[^`~])(?:(?=[`~])|(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|protocol:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9](?=emailProtocol)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))))/).replace("protocol", ce).replace(/emailProtocol/g, /(?:mailto|xmpp):/).getRegex() };
16516
+ Ot = { ...X, br: h(ge).replace("{2,}", "*").getRegex(), text: h(X.text).replace("\\b_", "\\b_| {2,}\\n").replace(/\{2,\}/g, "*").getRegex() };
16517
+ j = { normal: te, gfm: Ze, pedantic: He };
16518
+ D = { normal: ne, gfm: X, breaks: Ot, pedantic: Tt };
16519
+ wt = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
16520
+ _ = class {
16521
+ options;
16522
+ block;
16523
+ constructor(e) {
16524
+ this.options = e || P;
16525
+ }
16526
+ static passThroughHooks = new Set(["preprocess", "postprocess", "processAllTokens", "emStrongMask"]);
16527
+ static passThroughHooksRespectAsync = new Set(["preprocess", "postprocess", "processAllTokens"]);
16528
+ preprocess(e) {
16529
+ return e;
16530
+ }
16531
+ postprocess(e) {
16532
+ return e;
16533
+ }
16534
+ processAllTokens(e) {
16535
+ return e;
16536
+ }
16537
+ emStrongMask(e) {
16538
+ return e;
16539
+ }
16540
+ provideLexer(e = this.block) {
16541
+ return e ? R.lex : R.lexInline;
16542
+ }
16543
+ provideParser(e = this.block) {
16544
+ return e ? T.parse : T.parseInline;
16545
+ }
16546
+ };
16547
+ E = new F;
16548
+ k.options = k.setOptions = function(l) {
16549
+ return E.setOptions(l), k.defaults = E.defaults, W(k.defaults), k;
16550
+ };
16551
+ k.getDefaults = I;
16552
+ k.defaults = P;
16553
+ k.use = yt;
16554
+ k.walkTokens = function(l, e) {
16555
+ return E.walkTokens(l, e);
16556
+ };
16557
+ k.parseInline = E.parseInline;
16558
+ k.Parser = T;
16559
+ k.parser = T.parse;
16560
+ k.Renderer = S;
16561
+ k.TextRenderer = z;
16562
+ k.Lexer = R;
16563
+ k.lexer = R.lex;
16564
+ k.Tokenizer = y;
16565
+ k.Hooks = _;
16566
+ k.parse = k;
16567
+ gn = k.options;
16568
+ fn = k.setOptions;
16569
+ mn = k.walkTokens;
16570
+ xn = k.parseInline;
16571
+ Rn = T.parse;
16572
+ Tn = R.lex;
16573
+ });
16574
+
16575
+ // packages/core/src/conversation-export-html.ts
16576
+ async function embedImages(items, loadImage) {
16577
+ const embedded = new Map;
16578
+ let used = 0;
16579
+ let skipped = 0;
16580
+ for (const item of items) {
16581
+ if (item.type !== "user" && item.type !== "tool")
16582
+ continue;
16583
+ for (const image of item.images ?? []) {
16584
+ let reason = null;
16585
+ if (loadImage === undefined)
16586
+ reason = "not embedded";
16587
+ else if (used + image.bytes > MAX_EMBEDDED_IMAGE_BYTES)
16588
+ reason = "over the embedding size limit";
16589
+ else {
16590
+ try {
16591
+ const file = await loadImage(image);
16592
+ if (!SAFE_IMAGE_TYPE.test(file.mimeType))
16593
+ reason = "unsupported image type";
16594
+ else if (used + file.bytes.length > MAX_EMBEDDED_IMAGE_BYTES)
16595
+ reason = "over the embedding size limit";
16596
+ else {
16597
+ used += file.bytes.length;
16598
+ const parts = [];
16599
+ for (let at = 0;at < file.bytes.length; at += BASE64_SLICE_BYTES)
16600
+ parts.push(encodeChunk(file.bytes.subarray(at, at + BASE64_SLICE_BYTES)));
16601
+ embedded.set(`${image.entryId}:${image.contentIndex}`, {
16602
+ src: `data:${file.mimeType.toLowerCase()};base64,${parts.join("")}`
16603
+ });
16604
+ }
16605
+ } catch {
16606
+ reason = "image unavailable";
16607
+ }
16608
+ }
16609
+ if (reason !== null) {
16610
+ embedded.set(`${image.entryId}:${image.contentIndex}`, { reason });
16611
+ skipped++;
16612
+ }
16613
+ }
16614
+ }
16615
+ return { embedded, skipped };
16616
+ }
16617
+ function renderImages(images, embedded) {
16618
+ return (images ?? []).map((image) => {
16619
+ const found = embedded.get(`${image.entryId}:${image.contentIndex}`);
16620
+ const label = escapeHtml(imageLabel(image));
16621
+ if (found !== undefined && "src" in found)
16622
+ return `<figure><img src="${found.src}" alt="${escapeHtml(image.name)}"><figcaption>${label}</figcaption></figure>`;
16623
+ const reason = found !== undefined && "reason" in found ? ` \u2014 ${found.reason}` : "";
16624
+ return `<p class="placeholder">[image: ${label}${reason}]</p>`;
16625
+ }).join("");
16626
+ }
16627
+ async function exportHtml(source, options) {
16628
+ const { embedded, skipped } = await embedImages(source.items, options.loadImage);
16629
+ const title = escapeHtml(oneLine(source.title) || FALLBACK_EXPORT_TITLE);
16630
+ const body = [];
16631
+ for (const item of source.items) {
16632
+ if (item.type === "system") {
16633
+ body.push(`<div class="system">${escapeHtml(oneLine(item.text))}</div>`);
16634
+ } else if (item.type === "user") {
16635
+ const text = userText2(item);
16636
+ body.push(`<section class="msg user"><div class="role">User</div>${text === "" ? "" : `<div class="text">${escapeHtml(text)}</div>`}${renderImages(item.images, embedded)}</section>`);
16637
+ } else if (item.type === "assistant") {
16638
+ const thinking = options.includeThinking && item.thinking.trim() !== "" ? `<details class="thinking"><summary>Thinking</summary><div class="text">${escapeHtml(item.thinking.trim())}</div></details>` : "";
16639
+ const text = item.text.trim() === "" ? "" : `<div class="md">${markdown2.parse(item.text.trim(), { async: false })}</div>`;
16640
+ const note = item.note === null ? "" : `<div class="note">${escapeHtml(oneLine(item.note))}</div>`;
16641
+ body.push(`<section class="msg assistant"><div class="role">Assistant</div>${thinking}${text}${note}</section>`);
16642
+ } else {
16643
+ const status = toolStatus(item);
16644
+ const output = toolOutput(item);
16645
+ const heading = [
16646
+ `<b>Tool</b> ${escapeHtml(item.name)}`,
16647
+ item.summary === "" ? "" : `<span class="args">${escapeHtml(oneLine(item.summary))}</span>`,
16648
+ status === "ok" ? "" : `<span class="status">[${status}]</span>`,
16649
+ item.durationMs === null ? "" : `<span class="duration">(${formatDuration(item.durationMs)})</span>`
16650
+ ].filter(Boolean).join(" ");
16651
+ body.push(`<details class="tool ${status}"><summary>${heading}</summary>${item.detail === null || item.detail === "" ? "" : `<pre>${escapeHtml(item.detail)}</pre>`}${output === "" ? "" : `<div class="label">${status === "running" ? "Output so far" : "Result"}</div><pre>${escapeHtml(output)}</pre>`}${item.note === null ? "" : `<div class="note">${escapeHtml(oneLine(item.note))}</div>`}${renderImages(item.images, embedded)}</details>`);
16652
+ }
16653
+ }
16654
+ const content = `<!doctype html>
16655
+ <html lang="en">
16656
+ <head>
16657
+ <meta charset="utf-8">
16658
+ <meta http-equiv="Content-Security-Policy" content="${CSP}">
16659
+ <meta name="viewport" content="width=device-width, initial-scale=1">
16660
+ <title>${title}</title>
16661
+ <style>${STYLE}</style>
16662
+ </head>
16663
+ <body>
16664
+ <main>
16665
+ <h1>${title}</h1>
16666
+ <p class="meta">${escapeHtml(oneLine(source.model))} \xB7 ${escapeHtml(oneLine(source.cwd))} \xB7 ${escapeHtml(source.exportedAt.toISOString())}</p>
16667
+ ${body.join(`
16668
+ `)}
16669
+ </main>
16670
+ </body>
16671
+ </html>
16672
+ `;
16673
+ return { content, skippedImages: skipped };
16674
+ }
16675
+ var MAX_EMBEDDED_IMAGE_BYTES, SAFE_IMAGE_TYPE, SAFE_LINK, BASE64_SLICE_BYTES, CSP = "default-src 'none'; img-src data:; style-src 'unsafe-inline'", STYLE = `
16676
+ :root{color-scheme:light dark;--bg:#fff;--fg:#1f2328;--muted:#656d76;--border:#d0d7de;--user:#eef4ff;--code:#f6f8fa}
16677
+ @media(prefers-color-scheme:dark){:root{--bg:#0d1117;--fg:#e6edf3;--muted:#8b949e;--border:#30363d;--user:#16243a;--code:#161b22}}
16678
+ body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif}
16679
+ main{max-width:860px;margin:0 auto;padding:24px 16px 64px}
16680
+ h1{font-size:1.5rem;margin:0 0 4px}
16681
+ .meta{color:var(--muted);font-size:.85rem;margin:0 0 24px}
16682
+ .msg{margin:16px 0;padding:12px 16px;border:1px solid var(--border);border-radius:12px}
16683
+ .msg.user{background:var(--user)}
16684
+ .role{font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
16685
+ .text{white-space:pre-wrap;overflow-wrap:anywhere}
16686
+ .system{text-align:center;color:var(--muted);font-size:.85rem;margin:16px 0}
16687
+ .md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}
16688
+ .md pre,pre{background:var(--code);border-radius:8px;padding:10px 12px;overflow:auto;font-size:.85rem;white-space:pre-wrap;overflow-wrap:anywhere}
16689
+ .md code{background:var(--code);border-radius:4px;padding:1px 4px}.md pre code{background:none;padding:0}
16690
+ .md table{border-collapse:collapse}.md th,.md td{border:1px solid var(--border);padding:4px 8px}
16691
+ .md blockquote{margin:0;padding-left:12px;border-left:3px solid var(--border);color:var(--muted)}
16692
+ details{margin:8px 0}summary{cursor:pointer;color:var(--muted)}
16693
+ details.tool{border:1px solid var(--border);border-radius:8px;padding:6px 12px;margin:12px 0}
16694
+ details.tool.error>summary .status{color:#cf222e}
16695
+ .label{font-size:.75rem;color:var(--muted);margin-top:8px}
16696
+ .note{color:var(--muted);font-style:italic;margin-top:6px}
16697
+ figure{margin:8px 0}img{max-width:100%;height:auto;border-radius:8px}figcaption,.placeholder{color:var(--muted);font-size:.8rem}
16698
+ `, escapeHtml = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;"), markdown2;
16699
+ var init_conversation_export_html = __esm(() => {
16700
+ init_marked_esm();
16701
+ MAX_EMBEDDED_IMAGE_BYTES = 32 * 1024 * 1024;
16702
+ SAFE_IMAGE_TYPE = /^image\/(png|jpeg|webp|gif)$/i;
16703
+ SAFE_LINK = /^(https?:|mailto:)/i;
16704
+ BASE64_SLICE_BYTES = 3 * 16 * 1024;
16705
+ markdown2 = new F({
16706
+ gfm: true,
16707
+ breaks: true,
16708
+ renderer: {
16709
+ html: ({ text }) => escapeHtml(text),
16710
+ link({ href, title, tokens }) {
16711
+ const label = this.parser.parseInline(tokens);
16712
+ if (!SAFE_LINK.test(href.trim()))
16713
+ return label;
16714
+ const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
16715
+ return `<a href="${escapeHtml(href)}"${titleAttr} rel="noopener noreferrer" target="_blank">${label}</a>`;
16716
+ },
16717
+ image({ href, text }) {
16718
+ const label = `[image: ${escapeHtml(text || href)}]`;
16719
+ return SAFE_LINK.test(href.trim()) ? `<a href="${escapeHtml(href)}" rel="noopener noreferrer" target="_blank">${label}</a>` : label;
16720
+ }
16721
+ }
16722
+ });
16723
+ });
16724
+
16725
+ // packages/core/src/conversation-export.ts
16726
+ function copyText(item) {
16727
+ if (item.type === "user")
16728
+ return userText2(item);
16729
+ if (item.type === "assistant")
16730
+ return item.text.trim();
16731
+ return "";
16732
+ }
16733
+ function lastAnswerText(items) {
16734
+ for (let i = items.length - 1;i >= 0; i--) {
16735
+ const item = items[i];
16736
+ if (item.type !== "assistant")
16737
+ continue;
16738
+ const text = copyText(item);
16739
+ if (text !== "")
16740
+ return text;
16741
+ }
16742
+ return null;
16743
+ }
16744
+ function exportFormatFromPath(path) {
16745
+ const extension = /\.(md|html|json)$/i.exec(path)?.[1]?.toLowerCase();
16746
+ return extension === "md" ? "markdown" : extension === "html" ? "html" : extension === "json" ? "json" : null;
16747
+ }
16748
+ function exportFileName(title, format, at) {
16749
+ const cleaned = title.replace(/[\\/:*?"<>|\u0000-\u001f\u007f]/g, "-").replace(/\s+/g, " ").replace(/-{2,}/g, "-");
16750
+ const safe = Array.from(cleaned).slice(0, MAX_FILE_TITLE_CHARS).join("").replace(/^[\s.-]+|[\s.-]+$/g, "") || FALLBACK_EXPORT_TITLE;
16751
+ const pad = (value, width = 2) => String(value).padStart(width, "0");
16752
+ const stamp = `${pad(at.getFullYear(), 4)}${pad(at.getMonth() + 1)}${pad(at.getDate())}-${pad(at.getHours())}${pad(at.getMinutes())}${pad(at.getSeconds())}`;
16753
+ return `${safe}-${stamp}.${EXPORT_EXTENSIONS[format]}`;
16754
+ }
16755
+ async function exportConversation(format, source, options) {
16756
+ if (source.items.length === 0)
16757
+ throw new Error("Nothing to export yet - start a conversation first");
16758
+ const extension = EXPORT_EXTENSIONS[format];
16759
+ const mimeType = MIME_TYPES[format];
16760
+ if (format === "html") {
16761
+ const { content, skippedImages } = await exportHtml(source, options);
16762
+ return { content, mimeType, extension, skippedImages };
16763
+ }
16764
+ const content = format === "markdown" ? exportMarkdown(source, options) : exportJson(source, options);
16765
+ return { content, mimeType, extension, skippedImages: 0 };
16766
+ }
16767
+ function block(content) {
16768
+ let longest = 0;
16769
+ for (const run of content.matchAll(/`+/g))
16770
+ longest = Math.max(longest, run[0].length);
16771
+ const marks = "`".repeat(Math.max(3, longest + 1));
16772
+ return `${marks}
16773
+ ${content}
16774
+ ${marks}`;
16775
+ }
16776
+ function exportMarkdown(source, options) {
16777
+ const out = [
16778
+ `# ${oneLine(source.title) || FALLBACK_EXPORT_TITLE}`,
16779
+ "",
16780
+ `- **Model:** ${oneLine(source.model)}`,
16781
+ `- **Workspace:** ${oneLine(source.cwd)}`,
16782
+ `- **Exported:** ${source.exportedAt.toISOString()}`,
16783
+ ""
16784
+ ];
16785
+ let role = null;
16786
+ const heading = (next) => {
16787
+ if (role === next)
16788
+ return;
16789
+ role = next;
16790
+ out.push("---", "", next === "user" ? "## User" : "## Assistant", "");
16791
+ };
16792
+ for (const item of source.items) {
16793
+ if (item.type === "system") {
16794
+ role = null;
16795
+ out.push("---", "", `*${escapeInline(oneLine(item.text))}*`, "");
16796
+ } else if (item.type === "user") {
16797
+ heading("user");
16798
+ const text = userText2(item);
16799
+ if (text !== "")
16800
+ out.push(text, "");
16801
+ for (const image of item.images ?? [])
16802
+ out.push(`> [image] ${escapeInline(imageLabel(image))}`, "");
16803
+ } else if (item.type === "assistant") {
16804
+ heading("assistant");
16805
+ if (options.includeThinking && item.thinking.trim() !== "")
16806
+ out.push("<details>", "<summary>Thinking</summary>", "", item.thinking.trim(), "", "</details>", "");
16807
+ if (item.text.trim() !== "")
16808
+ out.push(item.text.trim(), "");
16809
+ if (item.note !== null)
16810
+ out.push(`> ${escapeInline(oneLine(item.note))}`, "");
16811
+ } else {
16812
+ heading("assistant");
16813
+ out.push(...markdownTool(item));
16814
+ }
16815
+ }
16816
+ return `${out.join(`
16817
+ `).trimEnd()}
16818
+ `;
16819
+ }
16820
+ function markdownTool(item) {
16821
+ const status = toolStatus(item);
16822
+ const label = [
16823
+ `Tool: ${item.name}`,
16824
+ item.summary === "" ? "" : `\xB7 ${oneLine(item.summary)}`,
16825
+ status === "ok" ? "" : `[${status}]`,
16826
+ item.durationMs === null ? "" : `(${formatDuration(item.durationMs)})`
16827
+ ].filter(Boolean).join(" ");
16828
+ const lines = ["<details>", `<summary>${escapeInline(label)}</summary>`, ""];
16829
+ if (item.detail !== null && item.detail !== "")
16830
+ lines.push(block(item.detail), "");
16831
+ const output = toolOutput(item);
16832
+ if (output !== "")
16833
+ lines.push(status === "running" ? "Output so far:" : "Result:", "", block(output), "");
16834
+ if (item.note !== null)
16835
+ lines.push(`> ${escapeInline(oneLine(item.note))}`, "");
16836
+ for (const image of item.images ?? [])
16837
+ lines.push(`> [image] ${escapeInline(imageLabel(image))}`, "");
16838
+ lines.push("</details>", "");
16839
+ return lines;
16840
+ }
16841
+ function exportJson(source, options) {
16842
+ const items = source.items.map((item) => {
16843
+ switch (item.type) {
16844
+ case "user":
16845
+ return {
16846
+ type: "user",
16847
+ text: userText2(item),
16848
+ images: (item.images ?? []).map(({ name, mimeType, width, height, bytes }) => ({
16849
+ name,
16850
+ mimeType,
16851
+ width,
16852
+ height,
16853
+ bytes
16854
+ }))
16855
+ };
16856
+ case "assistant":
16857
+ return {
16858
+ type: "assistant",
16859
+ text: item.text,
16860
+ ...options.includeThinking && item.thinking.trim() !== "" ? { thinking: item.thinking } : {},
16861
+ stopReason: item.stopReason,
16862
+ note: item.note
16863
+ };
16864
+ case "tool":
16865
+ return {
16866
+ type: "tool",
16867
+ name: item.name,
16868
+ summary: item.summary,
16869
+ detail: item.detail,
16870
+ status: item.phase,
16871
+ isError: item.isError,
16872
+ result: toolOutput(item),
16873
+ durationMs: item.durationMs,
16874
+ note: item.note,
16875
+ images: (item.images ?? []).map(({ name, mimeType, width, height, bytes }) => ({
16876
+ name,
16877
+ mimeType,
16878
+ width,
16879
+ height,
16880
+ bytes
16881
+ }))
16882
+ };
16883
+ default:
16884
+ return { type: "system", text: item.text };
16885
+ }
16886
+ });
16887
+ return `${JSON.stringify({
16888
+ format: "neko-conversation",
16889
+ version: 1,
16890
+ session: {
16891
+ id: source.sessionId,
16892
+ title: source.title,
16893
+ model: source.model,
16894
+ cwd: source.cwd,
16895
+ exportedAt: source.exportedAt.toISOString()
16896
+ },
16897
+ items
16898
+ }, null, 2)}
16899
+ `;
16900
+ }
16901
+ var EXPORT_EXTENSIONS, MIME_TYPES, MAX_FILE_TITLE_CHARS = 60, escapeInline = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
16902
+ var init_conversation_export = __esm(() => {
16903
+ init_conversation_export_html();
16904
+ init_conversation_export_html();
16905
+ EXPORT_EXTENSIONS = { markdown: "md", html: "html", json: "json" };
16906
+ MIME_TYPES = {
16907
+ markdown: "text/markdown;charset=utf-8",
16908
+ html: "text/html;charset=utf-8",
16909
+ json: "application/json;charset=utf-8"
16910
+ };
16911
+ });
16912
+
16913
+ // packages/tui/src/clipboard-write.ts
16914
+ import { spawn as spawn2 } from "child_process";
16915
+ import { getNativeClipboard } from "@earendil-works/pi-tui";
16916
+ function runClipboardCommand(command, args, input) {
16917
+ const { promise, resolve } = Promise.withResolvers();
16918
+ const child = spawn2(command, args, { stdio: ["pipe", "ignore", "ignore"], windowsHide: true });
16919
+ const timer = setTimeout(() => {
16920
+ child.kill("SIGKILL");
16921
+ resolve(false);
16922
+ }, COMMAND_TIMEOUT_MS);
16923
+ child.on("error", () => {
16924
+ clearTimeout(timer);
16925
+ resolve(false);
16926
+ });
16927
+ child.on("close", (code) => {
16928
+ clearTimeout(timer);
16929
+ resolve(code === 0);
16930
+ });
16931
+ child.stdin.on("error", () => {});
16932
+ child.stdin.end(input);
16933
+ return promise;
16934
+ }
16935
+ async function copyToClipboard(text, deps) {
16936
+ const platform = deps.platform ?? process.platform;
16937
+ const env = deps.env ?? process.env;
16938
+ const native = "native" in deps ? deps.native : getNativeClipboard();
16939
+ const run = deps.run ?? runClipboardCommand;
16940
+ let method = null;
16941
+ if (platform !== "linux" && native?.setText !== undefined) {
16942
+ try {
16943
+ await native.setText(text);
16944
+ method = "native";
16945
+ } catch {}
16946
+ }
16947
+ if (method === null) {
16948
+ const tools = [];
16949
+ if (platform === "darwin")
16950
+ tools.push(["pbcopy", []]);
16951
+ else if (platform === "win32")
16952
+ tools.push([
16953
+ "powershell.exe",
16954
+ [
16955
+ "-NoProfile",
16956
+ "-NonInteractive",
16957
+ "-Command",
16958
+ "$t=[IO.StreamReader]::new([Console]::OpenStandardInput(),[Text.UTF8Encoding]::new($false)).ReadToEnd();Set-Clipboard -Value $t"
16959
+ ]
16960
+ ]);
16961
+ else {
16962
+ if (env.TERMUX_VERSION)
16963
+ tools.push(["termux-clipboard-set", []]);
16964
+ if (env.WAYLAND_DISPLAY)
16965
+ tools.push(["wl-copy", []]);
16966
+ if (env.DISPLAY)
16967
+ tools.push(["xclip", ["-selection", "clipboard"]], ["xsel", ["--clipboard", "--input"]]);
16968
+ }
16969
+ for (const [command, args] of tools) {
16970
+ if (await run(command, args, text)) {
16971
+ method = "command";
16972
+ break;
16973
+ }
16974
+ }
16975
+ }
16976
+ const remote = Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.MOSH_CONNECTION);
16977
+ let osc52Sent = false;
16978
+ if (method === null || remote) {
16979
+ const encoded = Buffer.from(text).toString("base64");
16980
+ if (encoded.length <= OSC52_MAX_BASE64_LENGTH) {
16981
+ deps.osc52(`\x1B]52;c;${encoded}\x07`);
16982
+ osc52Sent = true;
16983
+ }
16984
+ }
16985
+ if (method === null && !osc52Sent)
16986
+ throw new Error(`Clipboard unavailable: no clipboard tool worked and ${text.length} chars is too large for OSC 52. Use /export to save it to a file.`);
16987
+ return { method: method ?? "osc52", osc52Sent };
16988
+ }
16989
+ async function copyLastAnswer(items, deps) {
16990
+ const text = lastAnswerText(items);
16991
+ if (text === null)
16992
+ return { level: "warning", message: "Nothing to copy yet: no assistant answer in this session." };
16993
+ try {
16994
+ const result = await copyToClipboard(text, deps);
16995
+ return {
16996
+ level: "info",
16997
+ message: result.osc52Sent ? `Sent to terminal clipboard via OSC 52 (${text.length} chars); your terminal must allow it` : `Copied last response (${text.length} chars)`
16998
+ };
16999
+ } catch (error) {
17000
+ return { level: "error", message: error instanceof Error ? error.message : String(error) };
17001
+ }
17002
+ }
17003
+ var OSC52_MAX_BASE64_LENGTH = 1e5, COMMAND_TIMEOUT_MS = 3000;
17004
+ var init_clipboard_write = __esm(() => {
17005
+ init_conversation_export();
17006
+ });
17007
+
17008
+ // packages/tui/src/components/assistant-message.ts
17009
+ import { Container, Markdown, MouseRegion, Spacer, Text as Text2 } from "@earendil-works/pi-tui";
17010
+ var THINKING_COLLAPSED_LABEL = "\u2727 Thinking\u2026 (click to expand)", AssistantMessageComponent;
17011
+ var init_assistant_message = __esm(() => {
17012
+ init_theme();
17013
+ AssistantMessageComponent = class AssistantMessageComponent extends Container {
17014
+ #renderTheme = getActiveThemeMode();
17015
+ #lastContent;
17016
+ #isStreaming = false;
17017
+ #thinkingExpanded = false;
17018
+ #renderWidth = 80;
17019
+ constructor(content, isStreaming = false) {
17020
+ super();
17021
+ if (content !== undefined)
17022
+ this.updateContent(content, isStreaming);
17023
+ }
17024
+ render(width) {
17025
+ if (this.#renderTheme !== getActiveThemeMode()) {
17026
+ this.#renderTheme = getActiveThemeMode();
17027
+ this.#rebuild();
17028
+ }
17029
+ this.#renderWidth = width;
17030
+ return super.render(width);
17031
+ }
17032
+ #highlightWidth() {
17033
+ return Math.max(10, this.#renderWidth - 4);
14600
17034
  }
14601
17035
  updateContent(content, isStreaming = this.#isStreaming) {
14602
17036
  this.#lastContent = content;
@@ -14685,10 +17119,28 @@ var init_editor = __esm(() => {
14685
17119
  this.#completionEpoch++;
14686
17120
  this.resetEscapeSequence();
14687
17121
  super.setText(text);
17122
+ this.#flushHistory();
17123
+ }
17124
+ setHistory(entries) {
17125
+ if (this["historyIndex"] > -1) {
17126
+ this.#pendingHistory = entries;
17127
+ return;
17128
+ }
17129
+ this.#pendingHistory = undefined;
17130
+ const current = this["history"];
17131
+ if (current.length === entries.length && current.every((entry, i) => entry === entries[i]))
17132
+ return;
17133
+ this["history"] = [...entries];
17134
+ }
17135
+ #pendingHistory;
17136
+ #flushHistory() {
17137
+ if (this.#pendingHistory !== undefined && this["historyIndex"] === -1)
17138
+ this.setHistory(this.#pendingHistory);
14688
17139
  }
14689
17140
  handleInput(data) {
14690
17141
  if (isKeyRelease(data))
14691
17142
  return;
17143
+ this.#flushHistory();
14692
17144
  this.#completionEpoch++;
14693
17145
  if (matchesKey3(data, "escape") && this.isShowingAutocomplete()) {
14694
17146
  this.resetEscapeSequence();
@@ -14780,6 +17232,8 @@ var init_labels = __esm(() => {
14780
17232
  { name: "help", description: "Show keyboard shortcuts and commands" },
14781
17233
  { name: "version", description: "View versions and check for updates" },
14782
17234
  { name: "compact", description: "Compact context (/compact optional instructions)" },
17235
+ { name: "copy", description: "Copy the last assistant response" },
17236
+ { name: "export", description: "Export session (/export path.md|.html|.json [--thinking])" },
14783
17237
  { name: "exit", description: "Exit neko TUI" },
14784
17238
  { name: "new", description: "Create a new session" },
14785
17239
  { name: "sessions", description: "Switch or create a session" },
@@ -15068,7 +17522,14 @@ var init_history_image = __esm(() => {
15068
17522
  });
15069
17523
 
15070
17524
  // packages/tui/src/components/login.ts
15071
- import { Input, isKeyRelease as isKeyRelease2, matchesKey as matchesKey4, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
17525
+ import {
17526
+ CURSOR_MARKER as CURSOR_MARKER2,
17527
+ Input,
17528
+ isKeyRelease as isKeyRelease2,
17529
+ matchesKey as matchesKey4,
17530
+ renderFakeCursor,
17531
+ truncateToWidth as truncateToWidth3
17532
+ } from "@earendil-works/pi-tui";
15072
17533
 
15073
17534
  class LoginPanel {
15074
17535
  height = 10;
@@ -15079,22 +17540,28 @@ class LoginPanel {
15079
17540
  #provider = "deepseek";
15080
17541
  #message = "";
15081
17542
  #busy = false;
17543
+ #confirmingRemove = false;
15082
17544
  #generation = 0;
17545
+ #closeTimer;
15083
17546
  #unsubscribe;
15084
17547
  #changed;
15085
17548
  #closed;
15086
17549
  #save;
15087
17550
  #remove;
17551
+ #autoCloseMs;
15088
17552
  constructor(options) {
15089
17553
  this.#changed = options.changed;
15090
17554
  this.#closed = options.closed;
15091
17555
  this.#save = options.save;
15092
17556
  this.#remove = options.remove;
17557
+ this.#autoCloseMs = options.autoCloseMs ?? AUTO_CLOSE_MS;
15093
17558
  }
15094
17559
  show(state, subscribe) {
15095
17560
  this.#generation++;
17561
+ this.#clearCloseTimer();
15096
17562
  this.open = true;
15097
17563
  this.#busy = false;
17564
+ this.#confirmingRemove = false;
15098
17565
  this.#message = "";
15099
17566
  this.#input = new Input;
15100
17567
  this.#provider = "deepseek";
@@ -15110,7 +17577,9 @@ class LoginPanel {
15110
17577
  }
15111
17578
  close() {
15112
17579
  this.#generation++;
17580
+ this.#clearCloseTimer();
15113
17581
  this.open = false;
17582
+ this.#confirmingRemove = false;
15114
17583
  this.#input = new Input;
15115
17584
  this.#unsubscribe?.();
15116
17585
  this.#unsubscribe = undefined;
@@ -15127,11 +17596,20 @@ class LoginPanel {
15127
17596
  }
15128
17597
  if (this.#busy)
15129
17598
  return;
17599
+ const confirming = this.#confirmingRemove;
17600
+ this.#confirmingRemove = false;
17601
+ if (confirming && !matchesKey4(data, "ctrl+r"))
17602
+ this.#message = "";
15130
17603
  if (matchesKey4(data, "enter"))
15131
17604
  this.#apply(false);
15132
- else if (matchesKey4(data, "ctrl+r") && this.#status?.savedConfigured)
15133
- this.#apply(true);
15134
- else if (matchesKey4(data, "left") || matchesKey4(data, "right") || matchesKey4(data, "tab"))
17605
+ else if (matchesKey4(data, "ctrl+r") && this.#status?.savedConfigured) {
17606
+ if (confirming)
17607
+ this.#apply(true);
17608
+ else {
17609
+ this.#confirmingRemove = true;
17610
+ this.#message = "Press Ctrl+R again to remove the saved key.";
17611
+ }
17612
+ } else if (matchesKey4(data, "tab") || (matchesKey4(data, "left") || matchesKey4(data, "right")) && this.#input.getValue() === "")
15135
17613
  this.#switch();
15136
17614
  else {
15137
17615
  this.#input.handleInput(data);
@@ -15146,20 +17624,39 @@ class LoginPanel {
15146
17624
  #switch() {
15147
17625
  const index = CREDENTIAL_PROVIDERS.findIndex((spec) => spec.id === this.#provider);
15148
17626
  this.#provider = CREDENTIAL_PROVIDERS[(index + 1) % CREDENTIAL_PROVIDERS.length].id;
15149
- this.#input = new Input;
15150
17627
  this.#message = "";
15151
17628
  }
17629
+ #clearCloseTimer() {
17630
+ if (this.#closeTimer !== undefined)
17631
+ clearTimeout(this.#closeTimer);
17632
+ this.#closeTimer = undefined;
17633
+ }
17634
+ #scheduleAutoClose(generation) {
17635
+ this.#clearCloseTimer();
17636
+ this.#closeTimer = setTimeout(() => {
17637
+ this.#closeTimer = undefined;
17638
+ if (generation === this.#generation && this.open)
17639
+ this.close();
17640
+ }, this.#autoCloseMs);
17641
+ }
15152
17642
  async#apply(remove) {
15153
- const key = this.#input.getValue();
15154
- if (!remove && !key.trim()) {
15155
- this.#message = "Enter an API key.";
15156
- this.#changed();
15157
- return;
17643
+ const key = this.#input.getValue().trim();
17644
+ if (!remove) {
17645
+ if (!key) {
17646
+ this.#message = "Enter an API key.";
17647
+ this.#changed();
17648
+ return;
17649
+ }
17650
+ if (Array.from(key).some((char) => char.charCodeAt(0) < 32)) {
17651
+ this.#message = "Key contains a newline or control character. Remove it and try again.";
17652
+ this.#changed();
17653
+ return;
17654
+ }
15158
17655
  }
15159
17656
  const generation = this.#generation;
15160
17657
  const provider = this.#provider;
15161
17658
  this.#busy = true;
15162
- this.#message = "Saving...";
17659
+ this.#message = remove ? "Removing..." : "Saving...";
15163
17660
  this.#changed();
15164
17661
  try {
15165
17662
  if (remove)
@@ -15169,10 +17666,11 @@ class LoginPanel {
15169
17666
  if (generation === this.#generation) {
15170
17667
  this.#input = new Input;
15171
17668
  this.#message = remove ? "Saved key removed." : "Saved. Used by the next request.";
17669
+ this.#scheduleAutoClose(generation);
15172
17670
  }
15173
- } catch {
17671
+ } catch (error) {
15174
17672
  if (generation === this.#generation)
15175
- this.#message = "Could not update API key. Try again.";
17673
+ this.#message = error instanceof Error ? error.message : "Could not update API key. Try again.";
15176
17674
  } finally {
15177
17675
  if (generation === this.#generation) {
15178
17676
  this.#busy = false;
@@ -15185,20 +17683,25 @@ class LoginPanel {
15185
17683
  const spec = credentialProvider(this.#provider);
15186
17684
  const tabs = CREDENTIAL_PROVIDERS.map((item) => item.id === this.#provider ? `[${item.id}]` : ` ${item.id} `).join(" ");
15187
17685
  const source = state?.source === "keyless" ? "keyless (rate-limited)" : state?.source ?? "Loading...";
17686
+ const keyLength = this.#input.getValue().length;
17687
+ const marker = this.focused ? CURSOR_MARKER2 : "";
17688
+ const keyRow = keyLength > 0 ? `API Key: ********${marker}${this.focused ? renderFakeCursor(" ") : ""} (${keyLength} characters)` : `API Key: ${marker}${this.focused ? `${renderFakeCursor("(")}paste key)` : "(paste key)"}`;
15188
17689
  const rows = [
15189
17690
  `Provider: ${tabs}`,
15190
17691
  `Environment: ${state ? state.environmentConfigured ? "Configured" : "Not configured" : "Loading..."}`,
15191
17692
  `Active source: ${source}`,
15192
- `API Key: ${"*".repeat(Math.min(this.#input.getValue().length, Math.max(1, width - 15))) || "(paste key)"}`,
17693
+ keyRow,
15193
17694
  this.#message || (spec.keyless ? "Without a key, Tavily runs in rate-limited keyless mode." : "Saved key overrides environment."),
15194
17695
  spec.signupUrl
15195
17696
  ];
15196
- const help = this.#busy ? shortcutHints(["Esc", "Close"]) : state?.savedConfigured ? shortcutHints(["Enter", "Save"], ["Ctrl+R", "Remove"], ["Left/Right", "Switch"], ["Esc", "Cancel"]) : shortcutHints(["Enter", "Save"], ["Left/Right", "Switch"], ["Esc", "Cancel"]);
17697
+ const switchHint = keyLength > 0 ? ["Tab", "Switch"] : ["Left/Right/Tab", "Switch"];
17698
+ const help = this.#busy ? shortcutHints(["Esc", "Close"]) : this.#confirmingRemove ? shortcutHints(["Ctrl+R", "Confirm remove"], ["Esc", "Cancel"]) : state?.savedConfigured ? shortcutHints(["Enter", "Save"], ["Ctrl+R", "Remove"], switchHint, ["Esc", "Cancel"]) : shortcutHints(["Enter", "Save"], switchHint, ["Esc", "Cancel"]);
15197
17699
  while (rows.length < this.height - 1)
15198
17700
  rows.push("");
15199
17701
  return [...rows.slice(0, Math.max(0, this.height - 1)), help].map((line) => theme.panel(` ${truncateToWidth3(line, Math.max(1, width - 2), "...", true)} `));
15200
17702
  }
15201
17703
  }
17704
+ var AUTO_CLOSE_MS = 800;
15202
17705
  var init_login = __esm(() => {
15203
17706
  init_src();
15204
17707
  init_theme();
@@ -15535,6 +18038,17 @@ class ThemedTruncatedText {
15535
18038
  return new TruncatedText(this.#content(), this.#paddingX, this.#paddingY).render(width);
15536
18039
  }
15537
18040
  }
18041
+
18042
+ class ThemedWrappedText {
18043
+ #content;
18044
+ constructor(content) {
18045
+ this.#content = content;
18046
+ }
18047
+ invalidate() {}
18048
+ render(width) {
18049
+ return new Text4(this.#content(), 1, 0).render(width);
18050
+ }
18051
+ }
15538
18052
  var ThemedText;
15539
18053
  var init_themed_text = __esm(() => {
15540
18054
  ThemedText = class ThemedText extends Text4 {
@@ -15556,6 +18070,103 @@ var init_themed_text = __esm(() => {
15556
18070
  };
15557
18071
  });
15558
18072
 
18073
+ // packages/tui/src/components/diff-view.ts
18074
+ function parseDiffLine(line, format = "edit") {
18075
+ if (format === "write") {
18076
+ return line.startsWith("+ ") ? { prefix: "+", lineNum: "", content: line.slice(2) } : null;
18077
+ }
18078
+ const match = /^([+\- ])( *\d+) (.*)$/.exec(line);
18079
+ if (match === null)
18080
+ return null;
18081
+ const first = match[1];
18082
+ const prefix = first === "+" || first === "-" ? first : " ";
18083
+ return { prefix, lineNum: match[2] ?? "", content: match[3] ?? "" };
18084
+ }
18085
+ function parseDiffLines(diff, format = "edit") {
18086
+ return diff.split(`
18087
+ `).map((line) => {
18088
+ const parsed = parseDiffLine(line, format);
18089
+ if (parsed === null)
18090
+ return { kind: "raw", text: line };
18091
+ const kind = parsed.prefix === "+" ? "add" : parsed.prefix === "-" ? "del" : "context";
18092
+ return { kind, lineNum: parsed.lineNum, content: parsed.content };
18093
+ });
18094
+ }
18095
+ function diffTargetPath(name, summary, detail) {
18096
+ if (name === "edit") {
18097
+ const path = summary.replace(/ \(\d+ edits?\)$/, "");
18098
+ return path === "" ? undefined : path;
18099
+ }
18100
+ if (name === "write") {
18101
+ if (detail !== null) {
18102
+ const first = detail.split(`
18103
+ `, 1)[0] ?? "";
18104
+ if (first !== "")
18105
+ return first;
18106
+ }
18107
+ return summary === "" ? undefined : summary;
18108
+ }
18109
+ return;
18110
+ }
18111
+ function highlightDocuments(parsed, lang) {
18112
+ const newContents = [];
18113
+ const oldContents = [];
18114
+ for (const line of parsed) {
18115
+ if (line.kind === "raw")
18116
+ continue;
18117
+ if (line.kind !== "del")
18118
+ newContents.push(line.content);
18119
+ if (line.kind !== "add")
18120
+ oldContents.push(line.content);
18121
+ }
18122
+ const newLines = highlightCodeToSegments(newContents.join(`
18123
+ `), lang);
18124
+ const oldLines = highlightCodeToSegments(oldContents.join(`
18125
+ `), lang);
18126
+ if (newLines === null || oldLines === null)
18127
+ return null;
18128
+ return { newLines, oldLines };
18129
+ }
18130
+ function styleSegments(segments) {
18131
+ return segments.map((s) => s.slot === "plain" ? theme.codeFg(s.text) : theme[s.slot](s.text)).join("");
18132
+ }
18133
+ function paintChangedLine(kind, gutter, content) {
18134
+ const bg = diffBackgroundAnsi(kind === "add" ? "added" : "removed");
18135
+ return `${bg}${theme.dim(gutter)}${bg}${content}\x1B[49m`;
18136
+ }
18137
+ function highlightDiffLines(diff, lang, format = "edit") {
18138
+ const parsed = parseDiffLines(diff, format);
18139
+ const documents = highlightDocuments(parsed, lang);
18140
+ if (documents === null)
18141
+ return highlightCodeToAnsiLines(diff, "diff");
18142
+ const rendered = [];
18143
+ let newIndex = 0;
18144
+ let oldIndex = 0;
18145
+ for (const line of parsed) {
18146
+ if (line.kind === "raw") {
18147
+ rendered.push(theme.dim(line.text));
18148
+ continue;
18149
+ }
18150
+ const sign = line.kind === "add" ? "+" : line.kind === "del" ? "-" : " ";
18151
+ const gutter = `${sign}${line.lineNum} `;
18152
+ if (line.kind === "del") {
18153
+ rendered.push(paintChangedLine("del", gutter, styleSegments(documents.oldLines[oldIndex++] ?? [])));
18154
+ continue;
18155
+ }
18156
+ if (line.kind === "add") {
18157
+ rendered.push(paintChangedLine("add", gutter, styleSegments(documents.newLines[newIndex++] ?? [])));
18158
+ continue;
18159
+ }
18160
+ rendered.push(`${theme.dim(gutter)}${styleSegments(documents.newLines[newIndex++] ?? [])}`);
18161
+ oldIndex++;
18162
+ }
18163
+ return rendered;
18164
+ }
18165
+ var init_diff_view = __esm(() => {
18166
+ init_slots();
18167
+ init_syntax();
18168
+ });
18169
+
15559
18170
  // packages/tui/src/components/tool-execution.ts
15560
18171
  import { Box, Container as Container3, MouseRegion as MouseRegion2, Text as Text5 } from "@earendil-works/pi-tui";
15561
18172
  function currentToolFrame() {
@@ -15586,15 +18197,23 @@ class ToolSpinnerTicker {
15586
18197
  this.#timer = undefined;
15587
18198
  }
15588
18199
  }
18200
+ function diffCountSuffix(state) {
18201
+ if (state.diffAdded === null)
18202
+ return "";
18203
+ const removed = state.diffRemoved !== null && state.diffRemoved > 0 ? `/-${state.diffRemoved}` : "";
18204
+ return ` (+${state.diffAdded}${removed})`;
18205
+ }
15589
18206
  function clipLines(text, maxLines) {
15590
18207
  const lines = text.split(`
15591
18208
  `);
15592
18209
  return { visible: lines.slice(0, maxLines).join(`
15593
18210
  `), rest: Math.max(0, lines.length - maxLines) };
15594
18211
  }
15595
- var SPINNER_FRAMES, spinnerFrame = 0, ToolExecutionComponent;
18212
+ var SPINNER_FRAMES, spinnerFrame = 0, DIFF_MAX_LINES = 50, ToolExecutionComponent;
15596
18213
  var init_tool_execution = __esm(() => {
15597
18214
  init_theme();
18215
+ init_syntax();
18216
+ init_diff_view();
15598
18217
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
15599
18218
  ToolExecutionComponent = class ToolExecutionComponent extends Container3 {
15600
18219
  #renderTheme = getActiveThemeMode();
@@ -15648,16 +18267,38 @@ var init_tool_execution = __esm(() => {
15648
18267
  }
15649
18268
  rebuild() {
15650
18269
  const state = this.#state;
15651
- const parts = [`${theme.toolTitle(`\u26A1 ${state.name}`)}`, theme.dim(state.summary), this.#statusLine(state.phase)];
18270
+ const parts = [
18271
+ `${theme.toolTitle(`\u26A1 ${state.name}`)}`,
18272
+ theme.dim(state.summary + diffCountSuffix(state)),
18273
+ this.#statusLine(state.phase)
18274
+ ];
15652
18275
  let text = parts.join(" ");
15653
- if (this.#expanded && state.detail !== null) {
15654
- const detail = clipLines(state.detail, 20);
15655
- text += `
18276
+ if (this.#expanded) {
18277
+ const detail = state.detail;
18278
+ if (detail !== null && !(state.diff !== null && state.name === "write" && !state.diffTruncated)) {
18279
+ const clipped = clipLines(detail, 20);
18280
+ text += `
15656
18281
  ${theme.dim("---- Arguments ----")}
15657
- ${detail.visible}`;
15658
- if (detail.rest > 0)
18282
+ ${clipped.visible}`;
18283
+ if (clipped.rest > 0)
18284
+ text += `
18285
+ ${theme.dim(`... ${clipped.rest} more lines`)}`;
18286
+ }
18287
+ if (state.diff !== null) {
18288
+ const clipped = clipLines(state.diff, DIFF_MAX_LINES);
18289
+ const lang = languageForPath(diffTargetPath(state.name, state.summary, state.detail));
18290
+ const format = state.name === "write" ? "write" : "edit";
15659
18291
  text += `
15660
- ${theme.dim(`... ${detail.rest} more lines`)}`;
18292
+ ${theme.dim("---- Diff ----")}
18293
+ ${highlightDiffLines(clipped.visible, lang, format).join(`
18294
+ `)}`;
18295
+ if (state.diffTruncated)
18296
+ text += `
18297
+ ${theme.dim("\u2026 Diff preview truncated (line or byte limit)")}`;
18298
+ if (clipped.rest > 0)
18299
+ text += `
18300
+ ${theme.dim(`\u2026 +${clipped.rest} more lines`)}`;
18301
+ }
15661
18302
  }
15662
18303
  const body = state.phase === "running" ? state.output : state.result;
15663
18304
  if (!this.#expanded && body !== null && body !== "") {
@@ -15844,6 +18485,90 @@ function followTerminalTheme(ui, apply) {
15844
18485
  }
15845
18486
  var THEME_QUERY_TIMEOUT_MS = 100;
15846
18487
 
18488
+ // packages/tui/src/export-command.ts
18489
+ import { open as open2, unlink as unlink2 } from "fs/promises";
18490
+ import { homedir as homedir6 } from "os";
18491
+ import { resolve as resolve4 } from "path";
18492
+ function parseExportArgs(input) {
18493
+ const words = [];
18494
+ let word = "";
18495
+ let quote = "";
18496
+ let quoted = false;
18497
+ let active = false;
18498
+ for (const c of input) {
18499
+ if (quote !== "") {
18500
+ if (c === quote)
18501
+ quote = "";
18502
+ else
18503
+ word += c;
18504
+ } else if (c === '"' || c === "'") {
18505
+ quote = c;
18506
+ quoted = true;
18507
+ active = true;
18508
+ } else if (/\s/.test(c)) {
18509
+ if (active)
18510
+ words.push({ text: word, quoted });
18511
+ word = "";
18512
+ quoted = false;
18513
+ active = false;
18514
+ } else {
18515
+ word += c;
18516
+ active = true;
18517
+ }
18518
+ }
18519
+ if (quote !== "")
18520
+ throw new Error(`Unterminated quote. ${EXPORT_USAGE}`);
18521
+ if (active)
18522
+ words.push({ text: word, quoted });
18523
+ let includeThinking = false;
18524
+ let path;
18525
+ for (const { text, quoted: wasQuoted } of words) {
18526
+ if (!wasQuoted && text === "--thinking")
18527
+ includeThinking = true;
18528
+ else if (!wasQuoted && text.startsWith("--"))
18529
+ throw new Error(`Unknown option ${text}. ${EXPORT_USAGE}`);
18530
+ else if (path === undefined && text !== "")
18531
+ path = text;
18532
+ else
18533
+ throw new Error(EXPORT_USAGE);
18534
+ }
18535
+ return { path, includeThinking };
18536
+ }
18537
+ async function exportToFile(input) {
18538
+ const format = input.path === undefined ? "markdown" : exportFormatFromPath(input.path);
18539
+ if (format === null)
18540
+ throw new Error("Unsupported export format: use a .md, .html or .json path");
18541
+ const target = resolve4(input.cwd, input.path === undefined ? exportFileName(input.source.title, format, input.source.exportedAt) : input.path.startsWith("~/") ? homedir6() + input.path.slice(1) : input.path);
18542
+ let handle;
18543
+ try {
18544
+ handle = await open2(target, "wx");
18545
+ } catch (error) {
18546
+ const code = error.code;
18547
+ if (code === "EEXIST")
18548
+ throw new Error(`File already exists: ${target}. Choose another path.`);
18549
+ if (code === "ENOENT")
18550
+ throw new Error(`Directory does not exist: ${target}`);
18551
+ throw error;
18552
+ }
18553
+ try {
18554
+ const result = await exportConversation(format, input.source, {
18555
+ includeThinking: input.includeThinking,
18556
+ ...input.loadImage ? { loadImage: input.loadImage } : {}
18557
+ });
18558
+ await handle.writeFile(result.content, "utf8");
18559
+ return { path: target, skippedImages: result.skippedImages };
18560
+ } catch (error) {
18561
+ await unlink2(target).catch(() => {});
18562
+ throw error;
18563
+ } finally {
18564
+ await handle.close().catch(() => {});
18565
+ }
18566
+ }
18567
+ var EXPORT_USAGE = "Usage: /export [path.md|path.html|path.json] [--thinking]";
18568
+ var init_export_command = __esm(() => {
18569
+ init_conversation_export();
18570
+ });
18571
+
15847
18572
  // packages/core/src/file-mentions.ts
15848
18573
  function mentionAt(text, cursor) {
15849
18574
  const before = text.slice(0, cursor);
@@ -15908,7 +18633,7 @@ function fileCompletion(commands, root, notice, search) {
15908
18633
  var init_file_completion = () => {};
15909
18634
 
15910
18635
  // packages/gateway/src/unix-link.ts
15911
- import { lstat as lstat2, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
18636
+ import { lstat as lstat2, mkdir as mkdir4, unlink as unlink3 } from "fs/promises";
15912
18637
  import { connect, createServer } from "net";
15913
18638
  import { dirname as dirname7 } from "path";
15914
18639
  import { webSocketLink as webSocketLink2 } from "@earendil-works/chord";
@@ -16051,7 +18776,7 @@ function createUnixListener(options) {
16051
18776
  });
16052
18777
  if (active)
16053
18778
  throw new Error("Gateway already running at this socket");
16054
- await unlink2(options.path);
18779
+ await unlink3(options.path);
16055
18780
  } catch (error) {
16056
18781
  if (error.code !== "ENOENT")
16057
18782
  throw error;
@@ -16081,7 +18806,7 @@ function createUnixListener(options) {
16081
18806
  await new Promise((resolve) => previous.close(() => resolve()));
16082
18807
  if (bound) {
16083
18808
  bound = false;
16084
- await unlink2(options.path).catch(() => {});
18809
+ await unlink3(options.path).catch(() => {});
16085
18810
  }
16086
18811
  }
16087
18812
  };
@@ -16311,6 +19036,7 @@ import {
16311
19036
  opaque,
16312
19037
  resource
16313
19038
  } from "@earendil-works/chord";
19039
+ import { UsageDoc as UsageDoc3 } from "@earendil-works/pi-durable";
16314
19040
  import { Type as Type5 } from "typebox";
16315
19041
  async function openReader(runtime, childId, parentId, context, through, historyUsage) {
16316
19042
  const child = await runtime.harness.conversation(childId, context);
@@ -16373,7 +19099,7 @@ async function openReader(runtime, childId, parentId, context, through, historyU
16373
19099
  return () => {};
16374
19100
  const stop = native.subscribe(() => fn(null));
16375
19101
  const stopApprovals = runtime.harness.subscribeCommits(({ changes }) => {
16376
- if (changes.some((c) => c.type === "document" && c.record.kind === "app.neko.approvals" && c.conversationId === parentId))
19102
+ if (changes.some((c) => c.type === "document" && ["app.neko.approvals", "app.neko.delegations", "app.neko.subagents"].includes(c.record.kind) && c.conversationId === parentId))
16377
19103
  fn(null);
16378
19104
  });
16379
19105
  return () => {
@@ -16384,7 +19110,12 @@ async function openReader(runtime, childId, parentId, context, through, historyU
16384
19110
  async refresh() {
16385
19111
  const page = await pageAt();
16386
19112
  const approvals = through === undefined ? (await runtime.approvals.list(parentId, context)).filter((a) => a.childId === childId) : [];
16387
- revision = remember({ ...project(page.entries), approvals, historyBefore: page.next });
19113
+ const currentUsage = await runtime.harness.snapshot(UsageDoc3, childId, context);
19114
+ const rounds = (await listDelegations(runtime.harness, parentId, context)).filter((r) => r.childId === childId).map(({ usageStart, usageEnd: _end, ...round }) => ({
19115
+ ...round,
19116
+ roundUsage: round.roundUsage ?? (through === undefined && usageStart && currentUsage ? subagentUsageTotals(subtractSubagentUsage(currentUsage, usageStart)) : null)
19117
+ }));
19118
+ revision = remember({ ...project(page.entries), rounds, approvals, historyBefore: page.next });
16388
19119
  return revision;
16389
19120
  },
16390
19121
  async chunk(input) {
@@ -16436,7 +19167,7 @@ function installSubagentHost(host, runtime, context) {
16436
19167
  reader: resource({
16437
19168
  rev: 1,
16438
19169
  async open() {
16439
- const entry = (await listDelegations(runtime.harness, input.parentId, context)).find((d) => d.taskId === input.taskId);
19170
+ const entry = (await listDelegations(runtime.harness, input.parentId, context)).find((d) => input.agentId.startsWith("round-") ? d.taskId === Number(input.agentId.slice(6)) : (d.agentId ?? agentIdFor(d.childId)) === input.agentId);
16440
19171
  if (!entry)
16441
19172
  throw new ChordError("gone", "Subagent unavailable");
16442
19173
  const r = await openReader(runtime, entry.childId, input.parentId, context, entry.historyThrough, entry.historyUsage);
@@ -16466,13 +19197,13 @@ function installSubagentHost(host, runtime, context) {
16466
19197
  throw new ChordError("forbidden", "Attachment expired");
16467
19198
  if (!await runtime.getConversation(Number(input.sessionId)))
16468
19199
  throw new ChordError("gone", "Session unavailable");
16469
- const entry = (await listDelegations(runtime.harness, Number(input.sessionId), context)).find((d) => d.taskId === input.taskId);
19200
+ const entry = (await listDelegations(runtime.harness, Number(input.sessionId), context)).find((d) => input.agentId !== undefined ? (d.agentId ?? agentIdFor(d.childId)) === input.agentId : d.taskId === input.taskId);
16470
19201
  if (!entry)
16471
19202
  throw new ChordError("forbidden", "Subagent does not belong to this session");
16472
19203
  return entry;
16473
19204
  };
16474
19205
  const { target } = self.resources({ target: resource({ rev: 1, deps: [owner], open: check }) });
16475
- const shared = self.get(ChildProjection, { parentId: Number(input.sessionId), taskId: input.taskId }, { principal: hostPrincipal });
19206
+ const shared = self.get(ChildProjection, { parentId: Number(input.sessionId), agentId: input.agentId ?? `round-${input.taskId}` }, { principal: hostPrincipal });
16476
19207
  const { authorizedRevision } = self.state({ authorizedRevision: cell(Type5.Integer(), 0) });
16477
19208
  self.listen({
16478
19209
  authorizedView: listener(shared.revision, async (value, ctx) => {
@@ -16501,14 +19232,26 @@ function installSubagentHost(host, runtime, context) {
16501
19232
  const entry = await check();
16502
19233
  if (entry.historyThrough !== undefined)
16503
19234
  throw new ChordError("forbidden", "Fork history is read-only");
16504
- return runtime.approvals.decide(Number(input.sessionId), entry.taskId, id, version, decision, context);
19235
+ const approval = (await runtime.approvals.list(Number(input.sessionId), context)).find((a) => a.id === id && a.childId === entry.childId);
19236
+ if (!approval)
19237
+ throw new ChordError("forbidden", "Approval does not belong to this agent");
19238
+ return runtime.approvals.decide(Number(input.sessionId), approval.delegationTaskId, id, version, decision, context);
16505
19239
  },
16506
- stop: async () => {
19240
+ stop: async (request) => {
16507
19241
  const entry = await check();
16508
19242
  if (entry.historyThrough !== undefined)
16509
19243
  throw new ChordError("forbidden", "Fork history is read-only");
16510
- await runtime.harness.abortTask(entry.taskId, context);
16511
- await runtime.harness.waitForTask(entry.taskId, context);
19244
+ const taskId = request?.taskId ?? input.taskId;
19245
+ if (taskId === undefined)
19246
+ throw new ChordError("invalid", "Select the exact active round to stop");
19247
+ const valid = await runtime.harness.commit(async (tx) => {
19248
+ const identity = (await tx.doc(SubagentsDoc, Number(input.sessionId))).items.find((a) => a.childId === entry.childId);
19249
+ return identity?.activeRoundId === taskId;
19250
+ }, context);
19251
+ if (!valid)
19252
+ return null;
19253
+ await runtime.harness.abortTask(taskId, context);
19254
+ await runtime.harness.waitForTask(taskId, context);
16512
19255
  return null;
16513
19256
  }
16514
19257
  };
@@ -16522,7 +19265,7 @@ var init_subagent_host = __esm(() => {
16522
19265
  init_session_host();
16523
19266
  ChildProjection = local("neko.subagent-projection", {
16524
19267
  version: 1,
16525
- input: Type5.Object({ parentId: Type5.Integer(), taskId: Type5.Integer() }),
19268
+ input: Type5.Object({ parentId: Type5.Integer(), agentId: Type5.String() }),
16526
19269
  members: {
16527
19270
  revision: Type5.Integer(),
16528
19271
  reader: opaque("neko.subagent-reader@1")
@@ -16531,7 +19274,7 @@ var init_subagent_host = __esm(() => {
16531
19274
  });
16532
19275
 
16533
19276
  // packages/gateway/src/session-host.ts
16534
- import { open as open2 } from "fs/promises";
19277
+ import { open as open3 } from "fs/promises";
16535
19278
  import { basename as basename5, extname } from "path";
16536
19279
  import {
16537
19280
  ChordError as ChordError2,
@@ -16543,7 +19286,7 @@ import {
16543
19286
  resource as resource2
16544
19287
  } from "@earendil-works/chord";
16545
19288
  import { withAbortSignal as withAbortSignal3 } from "@earendil-works/chord/context";
16546
- import { UsageDoc as UsageDoc2 } from "@earendil-works/pi-durable";
19289
+ import { UsageDoc as UsageDoc4 } from "@earendil-works/pi-durable";
16547
19290
  import { Type as Type6 } from "typebox";
16548
19291
  async function openProjection(runtime, session, context) {
16549
19292
  const view = await session.viewState();
@@ -16560,7 +19303,7 @@ async function openProjection(runtime, session, context) {
16560
19303
  const stopMetadata = runtime.onDirectoryChange(() => changed?.(null));
16561
19304
  let childIds = new Set;
16562
19305
  const stopChildren = runtime.harness.subscribeCommits(({ changes }) => {
16563
- if (changes.some((change) => change.type === "document" && (["app.neko.delegations", "app.neko.approvals"].includes(change.record.kind) && change.conversationId === session.id || change.record.kind === "pi.usage" && change.conversationId !== undefined && childIds.has(change.conversationId))))
19306
+ if (changes.some((change) => change.type === "document" && (["app.neko.delegations", "app.neko.approvals", "app.neko.subagents"].includes(change.record.kind) && change.conversationId === session.id || change.record.kind === "pi.usage" && change.conversationId !== undefined && childIds.has(change.conversationId))))
16564
19307
  changed?.(null);
16565
19308
  });
16566
19309
  return {
@@ -16592,15 +19335,19 @@ async function openProjection(runtime, session, context) {
16592
19335
  entry.approvalCount = entry.historyThrough === undefined ? approvals.filter((a) => a.delegationTaskId === entry.taskId && a.status === "pending").length : 0;
16593
19336
  childIds = new Set(subagents.filter((entry) => entry.historyThrough === undefined).map((entry) => entry.childId));
16594
19337
  let aggregate = await session.usage();
16595
- for (const child of subagents.filter((entry) => entry.historyThrough === undefined))
16596
- aggregate = mergeUsage(aggregate, await runtime.harness.snapshot(UsageDoc2, child.childId, context) ?? {
19338
+ for (const child of new Map(subagents.filter((entry) => entry.historyThrough === undefined).map((r) => [r.childId, r])).values())
19339
+ aggregate = mergeUsage(aggregate, await runtime.harness.snapshot(UsageDoc4, child.childId, context) ?? {
16597
19340
  models: {},
16598
19341
  tools: {}
16599
19342
  });
16600
19343
  const u = totalUsage(aggregate);
16601
19344
  return {
16602
19345
  ...projected,
16603
- subagents: subagents.map((entry) => ({ ...entry, report: entry.report?.slice(0, 512) ?? null })),
19346
+ agents: summarizeSubagents(subagents, (await runtime.harness.snapshot(SubagentsDoc, session.id, context))?.items),
19347
+ subagents: subagents.map(({ usageStart: _start, usageEnd: _end, ...entry }) => ({
19348
+ ...entry,
19349
+ report: entry.report?.slice(0, 512) ?? null
19350
+ })),
16604
19351
  taskUsage: {
16605
19352
  input: u.input,
16606
19353
  output: u.output,
@@ -16708,7 +19455,7 @@ function serveSessions(host, runtime, context) {
16708
19455
  const model = runtime.listModels().find((m) => m.provider === agent.model?.provider && m.modelId === agent.model?.modelId);
16709
19456
  if (!model?.input.includes("image"))
16710
19457
  throw new Error("Current model does not support images");
16711
- const handle = await open2(target, "r");
19458
+ const handle = await open3(target, "r");
16712
19459
  let id;
16713
19460
  try {
16714
19461
  const info = await handle.stat();
@@ -16944,7 +19691,7 @@ var require2, _a, Worker, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, cl
16944
19691
  }
16945
19692
  }
16946
19693
  return { b, r };
16947
- }, _a, fl, revfl, _b, fd, revfd, rev, x, i, hMap = function(cd, mb, r) {
19694
+ }, _a, fl, revfl, _b, fd, revfd, rev, x2, i, hMap = function(cd, mb, r) {
16948
19695
  var s = cd.length;
16949
19696
  var i = 0;
16950
19697
  var l = new u16(mb);
@@ -17148,7 +19895,7 @@ var require2, _a, Worker, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, cl
17148
19895
  final = 1, st.m = lbt, st.d = dm, st.n = dbt;
17149
19896
  } while (!final);
17150
19897
  return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
17151
- }, et, b2 = function(d, b) {
19898
+ }, et2, b2 = function(d, b) {
17152
19899
  return d[b] | d[b + 1] << 8;
17153
19900
  }, b4 = function(d, b) {
17154
19901
  return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
@@ -17214,10 +19961,10 @@ var init_esm = __esm(() => {
17214
19961
  revfd = _b.r;
17215
19962
  rev = new u16(32768);
17216
19963
  for (i = 0;i < 32768; ++i) {
17217
- x = (i & 43690) >> 1 | (i & 21845) << 1;
17218
- x = (x & 52428) >> 2 | (x & 13107) << 2;
17219
- x = (x & 61680) >> 4 | (x & 3855) << 4;
17220
- rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
19964
+ x2 = (i & 43690) >> 1 | (i & 21845) << 1;
19965
+ x2 = (x2 & 52428) >> 2 | (x2 & 13107) << 2;
19966
+ x2 = (x2 & 61680) >> 4 | (x2 & 3855) << 4;
19967
+ rev[i] = ((x2 & 65280) >> 8 | (x2 & 255) << 8) >> 1;
17221
19968
  }
17222
19969
  flt = new u8(288);
17223
19970
  for (i = 0;i < 144; ++i)
@@ -17249,10 +19996,10 @@ var init_esm = __esm(() => {
17249
19996
  "stream finishing",
17250
19997
  "invalid zip data"
17251
19998
  ];
17252
- et = /* @__PURE__ */ new u8(0);
19999
+ et2 = /* @__PURE__ */ new u8(0);
17253
20000
  td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder;
17254
20001
  try {
17255
- td.decode(et, { stream: true });
20002
+ td.decode(et2, { stream: true });
17256
20003
  tds = 1;
17257
20004
  } catch (e) {}
17258
20005
  });
@@ -17260,10 +20007,10 @@ var init_esm = __esm(() => {
17260
20007
  // packages/gateway/src/skill-market.ts
17261
20008
  import { createHash, randomUUID as randomUUID2 } from "crypto";
17262
20009
  import { mkdir as mkdir5, readdir as readdir2, rename as rename3, rm as rm4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
17263
- import { homedir as homedir6 } from "os";
20010
+ import { homedir as homedir7 } from "os";
17264
20011
  import { dirname as dirname8, join as join9 } from "path";
17265
20012
  function createSkillMarketClient(options) {
17266
- const home = options.home ?? homedir6();
20013
+ const home = options.home ?? homedir7();
17267
20014
  const fetchImpl = options.fetchImpl ?? fetch;
17268
20015
  const now = options.now ?? Date.now;
17269
20016
  let base;
@@ -17991,7 +20738,7 @@ var init_src2 = __esm(() => {
17991
20738
  });
17992
20739
 
17993
20740
  // packages/tui/src/gateway.ts
17994
- import { spawn as spawn2 } from "child_process";
20741
+ import { spawn as spawn3 } from "child_process";
17995
20742
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
17996
20743
  import { basename as basename7, dirname as dirname11, join as join12 } from "path";
17997
20744
  async function connectToGateway(options = {}) {
@@ -18032,7 +20779,7 @@ async function connectToGateway(options = {}) {
18032
20779
  }
18033
20780
  async function spawnAndConnect(plan) {
18034
20781
  const command = gatewayCommand(gatewayArgs(plan));
18035
- const child = spawn2(command.binary, command.args, { stdio: ["ignore", "pipe", "pipe"] });
20782
+ const child = spawn3(command.binary, command.args, { stdio: ["ignore", "pipe", "pipe"] });
18036
20783
  const outputTail = captureOutput(child);
18037
20784
  let spawnError;
18038
20785
  child.on("error", (error) => {
@@ -18206,6 +20953,7 @@ function helpLines(platform = process.platform) {
18206
20953
  ["Alt+Enter", "Queue while running"],
18207
20954
  ["Esc", "Stop a running task or blocking compaction"],
18208
20955
  ["Esc Esc", "Clear draft when idle (press twice quickly)"],
20956
+ ["Up / Down", "Browse previous inputs in this session"],
18209
20957
  ["Ctrl+C", "Stop while running; exit when idle"],
18210
20958
  ["Ctrl+S", "Open sessions"],
18211
20959
  [platform === "win32" ? "Alt+V" : "Ctrl+V", "Paste clipboard image or text"],
@@ -18229,8 +20977,8 @@ function helpLines(platform = process.platform) {
18229
20977
  ["Enter", "Confirm selected approval option"],
18230
20978
  ["PgUp / PgDn", "Scroll full approval command"],
18231
20979
  ["[/]", "Select another pending approval"],
18232
- ["S", "Stop active task (Enter confirms)"],
18233
- ["Click", "Open task card or status-bar Agents entry"]
20980
+ ["S", "Stop current round; keep agent context (Enter confirms)"],
20981
+ ["Click", "Open round card or status-bar Agents entry"]
18234
20982
  ]);
18235
20983
  section("Skills", [
18236
20984
  ["U", "Install for user"],
@@ -18314,8 +21062,8 @@ var init_help_ui = __esm(() => {
18314
21062
  // packages/tui/src/image-input.ts
18315
21063
  import { execFile as execFile2 } from "child_process";
18316
21064
  import { readFile as readFile8, stat as stat5 } from "fs/promises";
18317
- import { homedir as homedir7 } from "os";
18318
- import { basename as basename8, resolve as resolve4 } from "path";
21065
+ import { homedir as homedir8 } from "os";
21066
+ import { basename as basename8, resolve as resolve5 } from "path";
18319
21067
  import { promisify as promisify2 } from "util";
18320
21068
  import { isKeyRelease as isKeyRelease4, isKeyRepeat as isKeyRepeat2, matchesKey as matchesKey7 } from "@earendil-works/pi-tui";
18321
21069
  function imagePaths(text, platform = process.platform) {
@@ -18349,7 +21097,7 @@ function imagePaths(text, platform = process.platform) {
18349
21097
  return paths.length && paths.every((p) => /\.(png|jpe?g|webp)$/i.test(p)) ? paths : null;
18350
21098
  }
18351
21099
  async function readImageFile(path, cwd) {
18352
- const full = resolve4(cwd, path.startsWith("~/") ? homedir7() + path.slice(1) : path);
21100
+ const full = resolve5(cwd, path.startsWith("~/") ? homedir8() + path.slice(1) : path);
18353
21101
  const info = await stat5(full);
18354
21102
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
18355
21103
  throw new Error("Image must be a file no larger than 20 MiB");
@@ -18707,7 +21455,7 @@ import { matchesKey as matchesKey9 } from "@earendil-works/pi-tui";
18707
21455
 
18708
21456
  class SubagentDetail {
18709
21457
  get help() {
18710
- const canStop = this.entry?.historyThrough === undefined && (this.entry?.status === "running" || this.entry?.status === "queued");
21458
+ const canStop = this.identity ? this.identity.activeRoundId !== null && this.identity.historyThrough === undefined : this.entry?.historyThrough === undefined && (this.entry?.status === "running" || this.entry?.status === "queued");
18711
21459
  return shortcutHints(["\u2190", "Main"], ["\u2191\u2193/Wheel", "Scroll"], ["E", "Expand"], ...canStop ? [["S", "Stop"]] : []);
18712
21460
  }
18713
21461
  #view;
@@ -18729,6 +21477,9 @@ class SubagentDetail {
18729
21477
  #panel;
18730
21478
  #active = false;
18731
21479
  entry;
21480
+ identity;
21481
+ #targetRound;
21482
+ #locating = false;
18732
21483
  #epoch = 0;
18733
21484
  constructor(client, taskId, panel, changed) {
18734
21485
  this.#service = client.subagent(taskId);
@@ -18755,10 +21506,35 @@ class SubagentDetail {
18755
21506
  if (this.#before === undefined)
18756
21507
  this.#before = view.historyBefore;
18757
21508
  this.#entries = new Map(mergeTranscriptItems([...this.#entries.values()], view.items).map((item) => [item.key, item]));
21509
+ this.#loadTarget();
18758
21510
  this.#changed();
18759
21511
  });
18760
21512
  return this;
18761
21513
  }
21514
+ locate(taskId) {
21515
+ this.#targetRound = taskId;
21516
+ this.#loadTarget();
21517
+ this.#changed();
21518
+ }
21519
+ async#loadTarget() {
21520
+ if (this.#locating || !this.#active || this.#targetRound === undefined)
21521
+ return;
21522
+ const entryId = this.#view?.rounds?.find((r) => r.taskId === this.#targetRound)?.inputEntryId;
21523
+ if (entryId === undefined)
21524
+ return;
21525
+ this.#locating = true;
21526
+ try {
21527
+ while (this.#active && !this.#entries.has(`e${entryId}`) && this.#before != null && !this.#loading) {
21528
+ const before = this.#before;
21529
+ await this.history();
21530
+ if (before === this.#before)
21531
+ break;
21532
+ }
21533
+ } finally {
21534
+ this.#locating = false;
21535
+ this.#changed();
21536
+ }
21537
+ }
18762
21538
  close() {
18763
21539
  this.#epoch++;
18764
21540
  this.#active = false;
@@ -18811,14 +21587,15 @@ class SubagentDetail {
18811
21587
  if (component instanceof AssistantMessageComponent)
18812
21588
  component.setThinkingExpanded(this.#expanded);
18813
21589
  }
18814
- } else if (data.toLowerCase() === "s" && this.entry?.historyThrough === undefined && (this.entry?.status === "running" || this.entry?.status === "queued"))
21590
+ } else if (data.toLowerCase() === "s" && this.entry?.historyThrough === undefined && (this.identity?.activeRoundId != null || !this.identity && (this.entry?.status === "running" || this.entry?.status === "queued"))) {
21591
+ const stopTaskId = this.identity?.activeRoundId ?? this.entry?.taskId;
18815
21592
  this.#panel.showDetail({
18816
21593
  help: shortcutHints(["Enter", "Stop"], ["Esc", "Back"]),
18817
21594
  lines: () => [theme.warning("Stop this task? Existing changes will be kept.")],
18818
21595
  handleKey: (key) => {
18819
21596
  if (!matchesKey9(key, "enter"))
18820
21597
  return false;
18821
- this.#service.stop(null).catch((e) => {
21598
+ this.#service.stop(stopTaskId === undefined ? null : { taskId: stopTaskId }).catch((e) => {
18822
21599
  this.#error = String(e);
18823
21600
  }).finally(() => {
18824
21601
  this.#panel.popDetail();
@@ -18827,7 +21604,7 @@ class SubagentDetail {
18827
21604
  return true;
18828
21605
  }
18829
21606
  });
18830
- else
21607
+ } else
18831
21608
  return false;
18832
21609
  return true;
18833
21610
  }
@@ -18851,14 +21628,25 @@ class SubagentDetail {
18851
21628
  this.#pageSize = Math.max(1, height - 1);
18852
21629
  const entry = this.entry;
18853
21630
  const rows = entry ? [
18854
- theme.accent(`${entry.role} \xB7 ${entry.title}`),
18855
- theme.dim(`${entry.historyThrough !== undefined ? "Snapshot \xB7 " : ""}${labels2[entry.status]} \xB7 ${Math.max(0, Math.round(((entry.endedAt ?? Date.now()) - entry.createdAt) / 1000))}s`),
21631
+ theme.accent(`${entry.role} \xB7 ${this.identity?.title ?? entry.title}`),
21632
+ theme.dim(`${entry.historyThrough !== undefined ? "Snapshot \xB7 " : ""}${this.identity ? this.identity.status === "idle" ? "Idle" : this.identity.status === "queued" ? "Queued" : "Running" : labels2[entry.status]} \xB7 ${Math.max(0, Math.round(((entry.endedAt ?? Date.now()) - entry.createdAt) / 1000))}s`),
18856
21633
  ""
18857
21634
  ] : [];
18858
21635
  if (this.#view)
18859
- rows.push(theme.dim(`In ${this.#view.usage.input} \xB7 Out ${this.#view.usage.output} \xB7 $${this.#view.usage.cost.toFixed(4)}`), "");
21636
+ rows.push(theme.dim(`Total In ${this.#view.usage.input} \xB7 Out ${this.#view.usage.output} \xB7 $${this.#view.usage.cost.toFixed(4)}`), "");
18860
21637
  const imageRows = [];
21638
+ let targetOffset;
18861
21639
  for (const item of [...this.#entries.values()].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))) {
21640
+ if (item.type === "user") {
21641
+ const round = this.#view?.rounds?.find((r) => r.inputEntryId === item.id || r.taskId === item.subagentRoundId);
21642
+ if (round?.taskId === this.#targetRound)
21643
+ targetOffset = rows.length;
21644
+ if (round) {
21645
+ rows.push(theme.accent(`Task ${round.round ?? ""} \xB7 ${round.title} \xB7 ${labels2[round.status]}`));
21646
+ const u = round.roundUsage;
21647
+ rows.push(theme.dim(u ? `Round In ${u.input} \xB7 Out ${u.output} \xB7 Cache ${u.cacheRead}/${u.cacheWrite} \xB7 $${u.cost.toFixed(4)}` : "Round usage not recorded"));
21648
+ }
21649
+ }
18862
21650
  rows.push(...this.#component(item).render(width));
18863
21651
  if (item.type === "tool") {
18864
21652
  for (const approval of this.#view?.approvals ?? [])
@@ -18904,6 +21692,11 @@ class SubagentDetail {
18904
21692
  this.#offset += Math.max(0, this.#maxOffset - this.#prependOffset);
18905
21693
  this.#prependOffset = undefined;
18906
21694
  }
21695
+ if (targetOffset !== undefined) {
21696
+ this.#offset = targetOffset;
21697
+ this.#follow = false;
21698
+ this.#targetRound = undefined;
21699
+ }
18907
21700
  this.#offset = this.#follow ? this.#maxOffset : Math.min(this.#offset, this.#maxOffset);
18908
21701
  for (const image of imageRows) {
18909
21702
  image.image.setVisible(this.#active && image.start < this.#offset + height && image.end > this.#offset);
@@ -18912,11 +21705,11 @@ class SubagentDetail {
18912
21705
  return rows.slice(this.#offset, this.#offset + height);
18913
21706
  }
18914
21707
  }
18915
- var labels2, subagentItems = (entries) => entries.map((d) => ({
18916
- value: String(d.taskId),
21708
+ var labels2, subagentItems = (entries, summaries = summarizeSubagents(entries)) => summaries.map((d) => ({
21709
+ value: d.agentId,
18917
21710
  label: `${d.role} \xB7 ${d.title}`,
18918
- description: `${d.historyThrough !== undefined ? "Snapshot \xB7 " : ""}${labels2[d.status]}`,
18919
- rightLabel: d.approvalCount ? `Approval ${d.approvalCount}` : labels2[d.status]
21711
+ description: `${d.historyThrough !== undefined ? "Snapshot \xB7 " : ""}${d.taskTitle} \xB7 ${d.roundCount} rounds`,
21712
+ rightLabel: d.approvalCount ? `Approval ${d.approvalCount}` : d.status === "idle" ? "Idle" : d.status === "queued" ? "Queued" : "Running"
18920
21713
  }));
18921
21714
  var init_subagents_ui = __esm(() => {
18922
21715
  init_src();
@@ -19052,20 +21845,6 @@ var init_version_ui = __esm(() => {
19052
21845
  init_application_version();
19053
21846
  });
19054
21847
 
19055
- // packages/core/src/session-display.ts
19056
- function relativeTime(at, now, language = "en") {
19057
- const minutes = Math.floor(Math.max(0, now - at) / 60000);
19058
- if (minutes < 1)
19059
- return language === "zh" ? "\u521A\u521A" : "now";
19060
- const hours = Math.floor(minutes / 60), days = Math.floor(hours / 24);
19061
- if (days >= 30)
19062
- return new Date(at).toLocaleDateString(language === "zh" ? "zh-CN" : "en-GB");
19063
- const value = days || hours || minutes;
19064
- const unit = days ? ["\u5929", "d"] : hours ? ["\u5C0F\u65F6", "h"] : ["\u5206\u949F", "m"];
19065
- return language === "zh" ? `${value} ${unit[0]}\u524D` : `${value}${unit[1]}`;
19066
- }
19067
- var sessionName = (session, empty = "New session") => session.title || session.firstPromptPreview || empty, messageTime = (session) => session.lastMessageAt ?? session.createdAt, byMessage = (a, b) => messageTime(b) - messageTime(a) || Number(b.id) - Number(a.id), byVisited = (a, b) => b.lastUsedAt - a.lastUsedAt || Number(b.id) - Number(a.id);
19068
-
19069
21848
  // packages/tui/src/workspace.ts
19070
21849
  function workspaceSessions(sessions, cwd) {
19071
21850
  return sessions.filter((s) => s.workspaceRoot === cwd).sort(byMessage);
@@ -19117,6 +21896,7 @@ class NekoTuiApp {
19117
21896
  #noticeTimer;
19118
21897
  #compactionTimer;
19119
21898
  #editor;
21899
+ #historyItems;
19120
21900
  #footer = new StatusFooter;
19121
21901
  #welcome = new WelcomeScreen;
19122
21902
  #welcomeAttached = false;
@@ -19455,6 +22235,14 @@ class NekoTuiApp {
19455
22235
  this.#manage(() => this.archiveSession());
19456
22236
  return;
19457
22237
  }
22238
+ if (trimmed === "/copy") {
22239
+ this.#copyLastAnswer();
22240
+ return;
22241
+ }
22242
+ if (trimmed === "/export" || trimmed.startsWith("/export ")) {
22243
+ this.#exportSession(trimmed.slice("/export".length));
22244
+ return;
22245
+ }
19458
22246
  const chat = this.#chat;
19459
22247
  if (chat === undefined)
19460
22248
  return;
@@ -19555,6 +22343,44 @@ class NekoTuiApp {
19555
22343
  this.#ui.requestRender();
19556
22344
  }
19557
22345
  }
22346
+ async#copyLastAnswer() {
22347
+ const outcome = await copyLastAnswer(this.#lastView?.items ?? [], {
22348
+ osc52: (sequence) => this.#terminal.write(sequence)
22349
+ });
22350
+ this.addNotice(outcome.message, outcome.level);
22351
+ }
22352
+ async#exportSession(argumentText) {
22353
+ const view = this.#lastView;
22354
+ const chat = this.#chat;
22355
+ const sessionId = this.#sessionId;
22356
+ if (view === undefined || chat === undefined) {
22357
+ this.addNotice("No session is open.", "warning");
22358
+ return;
22359
+ }
22360
+ let args;
22361
+ try {
22362
+ args = parseExportArgs(argumentText);
22363
+ } catch (error) {
22364
+ this.addNotice(messageOf(error), "warning", { wrap: true });
22365
+ return;
22366
+ }
22367
+ const summary = this.#gateway.client.directory.state.value?.sessions.find((s) => s.id === sessionId);
22368
+ const title = summary ? sessionName(summary, "") : "";
22369
+ this.addNotice("Exporting...");
22370
+ try {
22371
+ const { path, skippedImages } = await exportToFile({
22372
+ ...args,
22373
+ cwd: this.#options.cwd ?? process.cwd(),
22374
+ source: { sessionId, title, model: view.model, cwd: view.cwd, exportedAt: new Date, items: view.items },
22375
+ loadImage: (image) => downloadImage((offset) => chat.getImage({ entryId: image.entryId, contentIndex: image.contentIndex, offset }))
22376
+ });
22377
+ const other = sessionId !== this.#sessionId ? ` (session ${title || sessionId})` : "";
22378
+ const skipped = skippedImages > 0 ? `; ${skippedImages} image(s) not embedded` : "";
22379
+ this.addNotice(`Exported${other} to ${path}${skipped}`, "info", { wrap: true, ttlMs: 20000 });
22380
+ } catch (error) {
22381
+ this.addNotice(`Export failed: ${messageOf(error)}`, "error", { wrap: true, ttlMs: 20000 });
22382
+ }
22383
+ }
19558
22384
  abort() {
19559
22385
  const chat = this.#chat;
19560
22386
  if (chat === undefined)
@@ -19585,6 +22411,8 @@ class NekoTuiApp {
19585
22411
  this.#sessionId = id;
19586
22412
  this.#refreshSkills(handle);
19587
22413
  this.#editor.setText(this.#texts.get(id) ?? "");
22414
+ this.#editor.setHistory([]);
22415
+ this.#historyItems = undefined;
19588
22416
  this.#resetTranscript();
19589
22417
  const view = handle.view;
19590
22418
  if (view.value !== undefined)
@@ -19753,19 +22581,25 @@ class NekoTuiApp {
19753
22581
  this.#agentsPanel = true;
19754
22582
  this.#panel.pointerSelection = true;
19755
22583
  this.#panel.agentNavigation = true;
19756
- this.#panel.show("Agents", async () => subagentItems(this.#lastView?.subagents ?? []), async (value) => this.#agentDetail(Number(value)));
22584
+ this.#panel.show("Agents", async () => subagentItems(this.#lastView?.subagents ?? [], this.#lastView?.agents), async (value) => this.#agentDetail(value));
19757
22585
  this.#ui.setFocus(this.#panel);
19758
22586
  if (taskId !== undefined)
19759
22587
  this.#panel.showDetail(this.#agentDetail(taskId));
19760
22588
  }
19761
- #agentDetail(taskId) {
19762
- let detail = this.#agentDetails.get(taskId);
22589
+ #agentDetail(target) {
22590
+ const round = typeof target === "number" ? this.#lastView?.subagents?.find((r) => r.taskId === target) : undefined;
22591
+ const id = typeof target === "string" ? target : round?.agentId ?? `agent-${round?.childId}`;
22592
+ let detail = this.#agentDetails.get(id);
19763
22593
  if (!detail) {
19764
- detail = new SubagentDetail(this.#gateway.client, taskId, this.#panel, () => this.#ui.requestRender());
19765
- this.#agentDetails.set(taskId, detail);
22594
+ detail = new SubagentDetail(this.#gateway.client, id, this.#panel, () => this.#ui.requestRender());
22595
+ this.#agentDetails.set(id, detail);
19766
22596
  }
19767
- detail.entry = this.#lastView?.subagents?.find((entry) => entry.taskId === taskId);
19768
- return detail.open();
22597
+ detail.identity = this.#lastView?.agents?.find((a) => a.agentId === id);
22598
+ detail.entry = this.#lastView?.subagents?.find((r) => r.taskId === (detail.identity?.activeRoundId ?? detail.identity?.lastRoundId));
22599
+ detail.open();
22600
+ if (typeof target === "number")
22601
+ detail.locate(target);
22602
+ return detail;
19769
22603
  }
19770
22604
  #closeOverlay() {
19771
22605
  if (this.#panel.open)
@@ -19774,14 +22608,15 @@ class NekoTuiApp {
19774
22608
  this.#login.close();
19775
22609
  this.#ui.setFocus(this.#editor);
19776
22610
  }
19777
- addNotice(text, level = "info") {
22611
+ addNotice(text, level = "info", options = {}) {
19778
22612
  clearTimeout(this.#noticeTimer);
19779
22613
  this.#noticeTimer = setTimeout(() => {
19780
22614
  this.#notices.clear();
19781
22615
  this.#ui.requestRender();
19782
- }, 5000);
22616
+ }, options.ttlMs ?? 5000);
19783
22617
  this.#notices.clear();
19784
- this.#notices.addChild(new ThemedTruncatedText(() => formatNotice(text, level), 1, 0));
22618
+ const content = () => formatNotice(text, level);
22619
+ this.#notices.addChild(options.wrap ? new ThemedWrappedText(content) : new ThemedTruncatedText(content, 1, 0));
19785
22620
  this.#ui.requestRender();
19786
22621
  }
19787
22622
  scheduleApply() {
@@ -19805,16 +22640,22 @@ class NekoTuiApp {
19805
22640
  }
19806
22641
  applyView(view) {
19807
22642
  this.#lastView = view;
22643
+ if (this.#historyItems !== view.items) {
22644
+ this.#historyItems = view.items;
22645
+ this.#editor.setHistory(promptHistory(view.items));
22646
+ }
19808
22647
  if (this.#chat)
19809
22648
  this.#approval.update(view.subagents ?? []);
19810
22649
  else
19811
22650
  this.#approval.close();
19812
22651
  if (this.#agentsPanel)
19813
- this.#panel.update(subagentItems(view.subagents ?? []));
19814
- for (const entry of view.subagents ?? []) {
19815
- const detail = this.#agentDetails.get(entry.taskId);
19816
- if (detail)
19817
- detail.entry = entry;
22652
+ this.#panel.update(subagentItems(view.subagents ?? [], view.agents));
22653
+ for (const identity of view.agents ?? []) {
22654
+ const detail = this.#agentDetails.get(identity.agentId);
22655
+ if (detail) {
22656
+ detail.identity = identity;
22657
+ detail.entry = view.subagents?.find((r) => r.taskId === (identity.activeRoundId ?? identity.lastRoundId));
22658
+ }
19818
22659
  }
19819
22660
  const imageModel = `${this.#sessionId}:${view.agent.model?.provider}/${view.agent.model?.modelId}`;
19820
22661
  if (this.#imageWarningModel !== imageModel && view.items.some((i) => (i.type === "user" || i.type === "tool") && i.images?.length)) {
@@ -19932,7 +22773,11 @@ class NekoTuiApp {
19932
22773
  summary: item.summary,
19933
22774
  detail: item.detail,
19934
22775
  phase: item.phase,
19935
- output: item.output
22776
+ output: item.output,
22777
+ diff: item.diff,
22778
+ diffAdded: item.diffAdded,
22779
+ diffRemoved: item.diffRemoved,
22780
+ diffTruncated: item.diffTruncated
19936
22781
  });
19937
22782
  component.sync(item);
19938
22783
  this.#items.set(item.key, { kind: "tool", component });
@@ -20014,7 +22859,7 @@ class NekoTuiApp {
20014
22859
  #syncFooter(view) {
20015
22860
  const usage = view.taskUsage ?? view.usage;
20016
22861
  this.#footer.setStats({
20017
- subagentCount: view.subagents?.length ?? 0,
22862
+ subagentCount: view.agents?.length ?? 0,
20018
22863
  approvalCount: view.subagents?.reduce((n, d) => n + (d.approvalCount ?? 0), 0) ?? 0,
20019
22864
  model: view.model,
20020
22865
  thinkingLevel: view.agent.thinkingLevel,
@@ -20085,7 +22930,11 @@ function streamingCardState(call) {
20085
22930
  output: call.output,
20086
22931
  result: null,
20087
22932
  durationMs: null,
20088
- note: null
22933
+ note: null,
22934
+ diff: call.diff,
22935
+ diffAdded: call.diffAdded,
22936
+ diffRemoved: call.diffRemoved,
22937
+ diffTruncated: call.diffTruncated
20089
22938
  };
20090
22939
  }
20091
22940
  function messageOf(error) {
@@ -20131,7 +22980,9 @@ var EXIT_COMMANDS, SESSIONS_COMMANDS, NEW_SESSION_VALUE = "__new__";
20131
22980
  var init_tui = __esm(() => {
20132
22981
  init_src();
20133
22982
  init_application_updates();
22983
+ init_input_history();
20134
22984
  init_approval_dock();
22985
+ init_clipboard_write();
20135
22986
  init_assistant_message();
20136
22987
  init_editor();
20137
22988
  init_footer();
@@ -20143,6 +22994,7 @@ var init_tui = __esm(() => {
20143
22994
  init_tool_execution();
20144
22995
  init_user_message();
20145
22996
  init_welcome();
22997
+ init_export_command();
20146
22998
  init_file_completion();
20147
22999
  init_gateway2();
20148
23000
  init_help_ui();
@@ -20268,7 +23120,7 @@ function openBrowser(url) {
20268
23120
  var init_open_browser = () => {};
20269
23121
 
20270
23122
  // packages/web/src/gateway-spawn.ts
20271
- import { spawn as spawn3 } from "child_process";
23123
+ import { spawn as spawn4 } from "child_process";
20272
23124
  import { randomUUID as randomUUID5 } from "crypto";
20273
23125
  import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
20274
23126
  import { dirname as dirname12, join as join13 } from "path";
@@ -20370,7 +23222,7 @@ function spawnGatewayProcess(options, socketPath, serverId) {
20370
23222
  ...options.model !== undefined ? ["--model", `${options.model.provider}/${options.model.modelId}`] : []
20371
23223
  ];
20372
23224
  const command = gatewayCommand(args);
20373
- return spawn3(command.binary, command.args, { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
23225
+ return spawn4(command.binary, command.args, { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
20374
23226
  }
20375
23227
  function pipeChildLogs(child, onLog) {
20376
23228
  if (onLog === undefined)
@@ -20460,10 +23312,10 @@ var init_gateway_spawn = __esm(() => {
20460
23312
  import { createReadStream } from "fs";
20461
23313
  import { stat as stat6 } from "fs/promises";
20462
23314
  import { createServer as createServer2 } from "http";
20463
- import { dirname as dirname13, extname as extname2, resolve as resolve5, sep as sep2 } from "path";
23315
+ import { dirname as dirname13, extname as extname2, resolve as resolve6, sep as sep2 } from "path";
20464
23316
  import { fileURLToPath as fileURLToPath5 } from "url";
20465
23317
  async function startWebServer(options = {}) {
20466
- const staticDir = resolve5(options.staticDir ?? DEFAULT_STATIC_DIR);
23318
+ const staticDir = resolve6(options.staticDir ?? DEFAULT_STATIC_DIR);
20467
23319
  const workspaceRoot = normalizeWorkspace(options.gateway?.cwd ?? process.cwd());
20468
23320
  const gateway = await ensureNekoGateway({
20469
23321
  ...options.gateway ?? {},
@@ -20542,7 +23394,7 @@ async function handleHttp(request, response, info) {
20542
23394
  }
20543
23395
  async function serveStatic(pathname, method, response, staticDir) {
20544
23396
  const cleanPath = decodeURIComponent(pathname);
20545
- const candidate = resolve5(staticDir, `.${cleanPath}`);
23397
+ const candidate = resolve6(staticDir, `.${cleanPath}`);
20546
23398
  if (candidate !== staticDir && !candidate.startsWith(staticDir + sep2)) {
20547
23399
  response.writeHead(403);
20548
23400
  response.end();
@@ -20553,13 +23405,13 @@ async function serveStatic(pathname, method, response, staticDir) {
20553
23405
  return;
20554
23406
  });
20555
23407
  if (stats?.isDirectory()) {
20556
- file = resolve5(file, "index.html");
23408
+ file = resolve6(file, "index.html");
20557
23409
  stats = await stat6(file).catch(() => {
20558
23410
  return;
20559
23411
  });
20560
23412
  }
20561
23413
  if (stats === undefined && extname2(candidate) === "") {
20562
- file = resolve5(staticDir, "index.html");
23414
+ file = resolve6(staticDir, "index.html");
20563
23415
  stats = await stat6(file).catch(() => {
20564
23416
  return;
20565
23417
  });