@sjawhar/opencode-legion-envoy 12.2.0 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dispatch.js CHANGED
@@ -1243,6 +1243,667 @@ function consumeMarker(stores, argv, args, files, now) {
1243
1243
  };
1244
1244
  }
1245
1245
 
1246
+ // ../envoy-client/src/askgate-new-asks.ts
1247
+ var OPENING_CODE_POINTS = 60;
1248
+ function newAsks(toolName, input) {
1249
+ switch (toolName) {
1250
+ case "dispatch_ask":
1251
+ return [questionAsk(input)];
1252
+ case "dispatch_issue":
1253
+ return typeof input.spec === "string" ? askBlocks(input.spec) : [];
1254
+ case "dispatch_artifact":
1255
+ return typeof input.content === "string" ? askBlocks(input.content) : [];
1256
+ case "dispatch_doc_edit":
1257
+ return (Array.isArray(input.ops) ? input.ops : []).flatMap((op) => {
1258
+ if (typeof op !== "object" || op === null)
1259
+ return [];
1260
+ const { op: kind, markdown, type, block } = op;
1261
+ if (kind === "insert" && typeof markdown === "string") {
1262
+ const { before, after } = op;
1263
+ return scanAsks(markdown, before === "start" || after === "start");
1264
+ }
1265
+ if (kind === "retype" && type === "ask" && typeof block === "string") {
1266
+ return [{ kind: "retype", id: block, text: "", opening: "" }];
1267
+ }
1268
+ return [];
1269
+ });
1270
+ default:
1271
+ return [];
1272
+ }
1273
+ }
1274
+ function questionAsk(input) {
1275
+ const question = typeof input.question === "string" ? input.question.trim() : "";
1276
+ const options = (Array.isArray(input.options) ? input.options : []).flatMap((option) => {
1277
+ if (typeof option !== "object" || option === null)
1278
+ return [];
1279
+ const { label, description } = option;
1280
+ return [
1281
+ typeof description === "string" && description !== "" ? `- ${String(label ?? "")}: ${description}` : `- ${String(label ?? "")}`
1282
+ ];
1283
+ });
1284
+ return {
1285
+ kind: "ask",
1286
+ text: options.length === 0 ? question : `${question}
1287
+
1288
+ ${options.join(`
1289
+ `)}`,
1290
+ opening: openingOf(question)
1291
+ };
1292
+ }
1293
+ function openingOf(text) {
1294
+ return Array.from(text.replace(/\s+/g, " ").trim()).slice(0, OPENING_CODE_POINTS).join("");
1295
+ }
1296
+ var FRONT_MATTER_FENCE = /^---[ \t]*$/;
1297
+ function askBlocks(markdown) {
1298
+ return scanAsks(markdown, true);
1299
+ }
1300
+ function scanAsks(markdown, frontMatter) {
1301
+ let lines = markdown.replace(/\r\n?/g, `
1302
+ `).split(`
1303
+ `);
1304
+ let unclosedFrontMatter = false;
1305
+ if (frontMatter && FRONT_MATTER_FENCE.test(lines[0] ?? "")) {
1306
+ const close = lines.findIndex((line, index) => index > 0 && FRONT_MATTER_FENCE.test(line));
1307
+ if (close === -1)
1308
+ unclosedFrontMatter = true;
1309
+ else
1310
+ lines = lines.slice(close + 1);
1311
+ }
1312
+ const scan = {
1313
+ lines,
1314
+ index: 0,
1315
+ opened: [],
1316
+ drafts: [],
1317
+ emptyItemWithBlankLines: false,
1318
+ skipList: false,
1319
+ unclosedFrontMatter
1320
+ };
1321
+ const document = {
1322
+ kind: "document",
1323
+ parent: undefined,
1324
+ previous: undefined,
1325
+ children: 0,
1326
+ last: undefined
1327
+ };
1328
+ for (const [index, text] of lines.entries()) {
1329
+ scan.index = index;
1330
+ readLine(scan, document, new Line(text));
1331
+ }
1332
+ return scan.drafts.map(({ id, lines: body }) => {
1333
+ const text = body.join(`
1334
+ `).trim();
1335
+ return { kind: "block", ...id === undefined ? {} : { id }, text, opening: openingOf(text) };
1336
+ });
1337
+ }
1338
+
1339
+ class Line {
1340
+ text;
1341
+ #starts;
1342
+ #pos = 0;
1343
+ #column = 0;
1344
+ constructor(text) {
1345
+ this.text = text;
1346
+ const starts = [0];
1347
+ let column = 0;
1348
+ for (let index = 0;index < text.length; index++) {
1349
+ column += text[index] === "\t" ? 4 - column % 4 : 1;
1350
+ starts.push(column);
1351
+ }
1352
+ this.#starts = starts;
1353
+ }
1354
+ get column() {
1355
+ return this.#column;
1356
+ }
1357
+ columnAt(index) {
1358
+ return this.#starts[index];
1359
+ }
1360
+ #afterSpaces(index) {
1361
+ let end = index;
1362
+ while (end < this.text.length && (this.text[end] === " " || this.text[end] === "\t"))
1363
+ end++;
1364
+ return end;
1365
+ }
1366
+ spacesAt(index) {
1367
+ return this.columnAt(this.#afterSpaces(index)) - this.columnAt(index);
1368
+ }
1369
+ indent() {
1370
+ const at = this.#afterSpaces(this.#pos);
1371
+ return { width: this.columnAt(at) - this.#column, at };
1372
+ }
1373
+ blank() {
1374
+ return this.indent().at === this.text.length;
1375
+ }
1376
+ skip(columns) {
1377
+ this.#column += columns;
1378
+ while (this.#pos < this.text.length && this.columnAt(this.#pos + 1) <= this.#column)
1379
+ this.#pos++;
1380
+ }
1381
+ advanceTo(index) {
1382
+ this.#pos = index;
1383
+ this.#column = this.columnAt(index);
1384
+ }
1385
+ rest() {
1386
+ const start = this.columnAt(this.#pos);
1387
+ if (this.#pos < this.text.length && start < this.#column) {
1388
+ return " ".repeat(this.columnAt(this.#pos + 1) - this.#column) + this.text.slice(this.#pos + 1);
1389
+ }
1390
+ return this.text.slice(this.#pos);
1391
+ }
1392
+ }
1393
+ function readLine(scan, document, line) {
1394
+ if (scan.opened.length === 0) {
1395
+ if (!line.blank())
1396
+ openBlocks(scan, document, line);
1397
+ return;
1398
+ }
1399
+ const opened = [...scan.opened];
1400
+ const lastIndex = opened.length - 1;
1401
+ for (const [index, block] of opened.entries()) {
1402
+ if (block.kind !== "paragraph") {
1403
+ const continuation = continueBlock(scan, block, line);
1404
+ if (continuation === "children" && index === lastIndex) {
1405
+ openBlocks(scan, block, line);
1406
+ return;
1407
+ }
1408
+ if (continuation !== "close")
1409
+ continue;
1410
+ }
1411
+ if (openBlocks(scan, opened[index - 1] ?? document, line) !== "paragraph") {
1412
+ scan.opened.splice(index, lastIndex - index + 1);
1413
+ }
1414
+ return;
1415
+ }
1416
+ }
1417
+ function openBlocks(scan, parent, line) {
1418
+ const continuable = scan.opened.at(-1)?.kind === "paragraph";
1419
+ let result = "none";
1420
+ for (let into = parent;; ) {
1421
+ const { width, at } = line.indent();
1422
+ if (at === line.text.length)
1423
+ break;
1424
+ const char = line.text.charAt(at);
1425
+ const interrupting = continuable && result === "none";
1426
+ let opened;
1427
+ for (const parser of BLOCK_PARSERS) {
1428
+ if (parser.triggers !== undefined && !parser.triggers.includes(char))
1429
+ continue;
1430
+ if (interrupting && !parser.interrupts)
1431
+ continue;
1432
+ if (width > 3 && !parser.indented)
1433
+ continue;
1434
+ opened = parser.open(scan, into, line, width, at);
1435
+ if (opened !== undefined)
1436
+ break;
1437
+ }
1438
+ if (opened === undefined)
1439
+ break;
1440
+ into.children++;
1441
+ into.last = opened.block;
1442
+ scan.opened.push(opened.block);
1443
+ result = "opened";
1444
+ if (!opened.children)
1445
+ break;
1446
+ into = opened.block;
1447
+ }
1448
+ const last = scan.opened.at(-1);
1449
+ if (result === "none" && last?.kind === "paragraph" && !line.blank()) {
1450
+ last.rows.push(line.rest());
1451
+ last.lastLine = scan.index;
1452
+ line.advanceTo(line.text.length);
1453
+ return "paragraph";
1454
+ }
1455
+ return result;
1456
+ }
1457
+ function under(parent) {
1458
+ return { parent, previous: parent.last, children: 0, last: undefined };
1459
+ }
1460
+ function leafAt(parent, line) {
1461
+ line.advanceTo(line.text.length);
1462
+ return { block: { kind: "leaf", ...under(parent) }, children: false };
1463
+ }
1464
+ function frontMatterHolds(scan, parent) {
1465
+ return scan.unclosedFrontMatter && parent.kind === "document";
1466
+ }
1467
+ var BLOCK_PARSERS = [
1468
+ {
1469
+ triggers: "-=",
1470
+ interrupts: true,
1471
+ indented: false,
1472
+ open(scan, parent, line, _width, at) {
1473
+ const last = scan.opened.at(-1);
1474
+ if (last?.kind !== "paragraph" || last.parent !== parent)
1475
+ return;
1476
+ return setextUnderline(line.text.slice(at)) ? leafAt(parent, line) : undefined;
1477
+ }
1478
+ },
1479
+ {
1480
+ triggers: "-*_",
1481
+ interrupts: true,
1482
+ indented: false,
1483
+ open: (_scan, parent, line, _width, at) => thematicBreak(line.text.slice(at)) ? leafAt(parent, line) : undefined
1484
+ },
1485
+ {
1486
+ triggers: "-+*0123456789",
1487
+ interrupts: true,
1488
+ indented: false,
1489
+ open(scan, parent, line) {
1490
+ const last = scan.opened.at(-1);
1491
+ if (last?.kind === "list" || scan.skipList) {
1492
+ scan.skipList = false;
1493
+ return;
1494
+ }
1495
+ const marker = listMarker(line);
1496
+ if (marker === undefined || frontMatterHolds(scan, parent))
1497
+ return;
1498
+ if (marker.empty || marker.number !== undefined && marker.number !== "1") {
1499
+ if (last?.kind === "paragraph" && last.parent === parent)
1500
+ return;
1501
+ if (interruptsOpenBlock(scan, parent))
1502
+ return;
1503
+ }
1504
+ scan.emptyItemWithBlankLines = false;
1505
+ return { block: { kind: "list", marker: marker.char, ...under(parent) }, children: true };
1506
+ }
1507
+ },
1508
+ {
1509
+ triggers: "-+*0123456789",
1510
+ interrupts: true,
1511
+ indented: false,
1512
+ open(scan, parent, line) {
1513
+ if (parent.kind !== "list")
1514
+ return;
1515
+ const previous = parent.last?.kind === "item" ? parent.last.offset : 0;
1516
+ const marker = listMarker(line);
1517
+ if (marker === undefined || marker.indent - previous > 3)
1518
+ return;
1519
+ scan.emptyItemWithBlankLines = false;
1520
+ const after = line.spacesAt(marker.end);
1521
+ const spacing = marker.empty || after > 4 ? 1 : after;
1522
+ const offset = line.columnAt(marker.end) - line.column + spacing;
1523
+ const item = { kind: "item", offset, ...under(parent) };
1524
+ if (marker.empty)
1525
+ return { block: item, children: false };
1526
+ line.advanceTo(marker.end);
1527
+ line.skip(spacing);
1528
+ return { block: item, children: true };
1529
+ }
1530
+ },
1531
+ {
1532
+ interrupts: false,
1533
+ indented: true,
1534
+ open(scan, parent, line, width) {
1535
+ if (width < 4)
1536
+ return;
1537
+ line.skip(4);
1538
+ const previous = scan.lines[scan.index - 1];
1539
+ const block = {
1540
+ kind: "code",
1541
+ lastLine: scan.index,
1542
+ blankBefore: previous === undefined || /^[ \t]*$/.test(previous),
1543
+ ...under(parent)
1544
+ };
1545
+ return { block, children: false };
1546
+ }
1547
+ },
1548
+ {
1549
+ triggers: "#",
1550
+ interrupts: true,
1551
+ indented: false,
1552
+ open: (_scan, parent, line, _width, at) => /^#{1,6}(?:[ \t]|$)/.test(line.text.slice(at)) ? leafAt(parent, line) : undefined
1553
+ },
1554
+ {
1555
+ triggers: "`~",
1556
+ interrupts: true,
1557
+ indented: false,
1558
+ open(_scan, parent, line, _width, at) {
1559
+ const char = line.text.charAt(at);
1560
+ const length = runOf(line.text, at, char);
1561
+ if (length < 3)
1562
+ return;
1563
+ if (char === "`" && line.text.slice(at + length).includes("`"))
1564
+ return;
1565
+ line.advanceTo(line.text.length);
1566
+ return { block: { kind: "fence", char, length, ...under(parent) }, children: false };
1567
+ }
1568
+ },
1569
+ {
1570
+ triggers: ">",
1571
+ interrupts: true,
1572
+ indented: false,
1573
+ open: (scan, parent, line) => !frontMatterHolds(scan, parent) && quoteMarker(line) ? { block: { kind: "quote", ...under(parent) }, children: true } : undefined
1574
+ },
1575
+ {
1576
+ triggers: ":",
1577
+ interrupts: true,
1578
+ indented: false,
1579
+ open: (_scan, parent, line, _width, at) => refusedDirective(line.text.slice(at)) ? leafAt(parent, line) : undefined
1580
+ },
1581
+ {
1582
+ triggers: ":",
1583
+ interrupts: true,
1584
+ indented: false,
1585
+ open(scan, parent, line, width, at) {
1586
+ const opener = typedOpener(line.text.slice(at));
1587
+ if (opener === undefined)
1588
+ return;
1589
+ let draft;
1590
+ if (opener.name === "ask") {
1591
+ draft = { id: opener.id, lines: [] };
1592
+ scan.drafts.push(draft);
1593
+ }
1594
+ line.advanceTo(line.text.length);
1595
+ const block = {
1596
+ kind: "typed",
1597
+ fence: opener.colons,
1598
+ indent: width,
1599
+ draft,
1600
+ ...under(parent)
1601
+ };
1602
+ return { block, children: true };
1603
+ }
1604
+ },
1605
+ {
1606
+ triggers: "[",
1607
+ interrupts: true,
1608
+ indented: false,
1609
+ open(scan, parent, line, _width, at) {
1610
+ const definition = /^\[\^([^\]]*)\]:/.exec(line.text.slice(at));
1611
+ if (definition === null || /^\s*$/.test(definition[1] ?? ""))
1612
+ return;
1613
+ if (frontMatterHolds(scan, parent))
1614
+ return;
1615
+ const label = at + definition[0].length;
1616
+ const content = line.text.slice(label).search(/[^ \t]/);
1617
+ line.advanceTo(content < 0 ? label : label + content);
1618
+ return { block: { kind: "footnote", ...under(parent) }, children: true };
1619
+ }
1620
+ },
1621
+ {
1622
+ interrupts: false,
1623
+ indented: false,
1624
+ open: (scan, parent, line) => {
1625
+ const block = {
1626
+ kind: "paragraph",
1627
+ rows: [line.rest()],
1628
+ lastLine: scan.index,
1629
+ ...under(parent)
1630
+ };
1631
+ line.advanceTo(line.text.length);
1632
+ return { block, children: false };
1633
+ }
1634
+ }
1635
+ ];
1636
+ function interruptsOpenBlock(scan, parent) {
1637
+ let previous = parent.last;
1638
+ const opensContainers = previous === undefined;
1639
+ let node = parent;
1640
+ while (previous === undefined && node !== undefined && node.kind !== "document") {
1641
+ previous = node.previous;
1642
+ node = node.parent;
1643
+ }
1644
+ if (previous?.kind === "paragraph") {
1645
+ return opensContainers && !previous.rows.slice(1).some((row) => TABLE_DELIMITER_ROW.test(row)) && scan.index - previous.lastLine <= 1;
1646
+ }
1647
+ if (previous?.kind !== "code")
1648
+ return false;
1649
+ if (previous.previous?.kind === "list")
1650
+ return false;
1651
+ if (previous.previous?.kind === "quote" && !previous.blankBefore)
1652
+ return false;
1653
+ return scan.lines.slice(previous.lastLine + 1, scan.index).every((row) => /^[ \t>]*$/.test(row));
1654
+ }
1655
+ var TABLE_DELIMITER_ROW = /^(?=.*\|)[ \t]*\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
1656
+ function continueBlock(scan, block, line) {
1657
+ switch (block.kind) {
1658
+ case "quote":
1659
+ return quoteMarker(line) ? "children" : "close";
1660
+ case "list":
1661
+ return continueList(scan, block, line);
1662
+ case "item":
1663
+ return continueItem(scan, block, line);
1664
+ case "footnote": {
1665
+ if (line.blank())
1666
+ return "children";
1667
+ const { width } = line.indent();
1668
+ if (width < 4)
1669
+ return "close";
1670
+ line.skip(4);
1671
+ return "children";
1672
+ }
1673
+ case "typed": {
1674
+ const { width, at } = line.indent();
1675
+ if (width < 4 && at < line.text.length && fenceColons(line.text.slice(at)) >= block.fence) {
1676
+ line.advanceTo(line.text.length);
1677
+ return "close";
1678
+ }
1679
+ if (at < line.text.length)
1680
+ line.skip(Math.min(width, block.indent));
1681
+ block.draft?.lines.push(line.rest());
1682
+ return "children";
1683
+ }
1684
+ case "fence": {
1685
+ const { width, at } = line.indent();
1686
+ if (width < 4) {
1687
+ const length = runOf(line.text, at, block.char);
1688
+ if (length >= block.length && /^[ \t]*$/.test(line.text.slice(at + length))) {
1689
+ line.advanceTo(line.text.length);
1690
+ return "close";
1691
+ }
1692
+ }
1693
+ line.advanceTo(line.text.length);
1694
+ return "continue";
1695
+ }
1696
+ case "code": {
1697
+ if (line.blank())
1698
+ return "continue";
1699
+ if (line.indent().width < 4)
1700
+ return "close";
1701
+ block.lastLine = scan.index;
1702
+ line.advanceTo(line.text.length);
1703
+ return "continue";
1704
+ }
1705
+ default:
1706
+ return block.kind === "document" ? "children" : "close";
1707
+ }
1708
+ }
1709
+ function continueList(scan, list, line) {
1710
+ const last = list.last;
1711
+ const lastIsEmpty = last === undefined || last.children === 0;
1712
+ if (line.blank()) {
1713
+ if (lastIsEmpty)
1714
+ scan.emptyItemWithBlankLines = true;
1715
+ return "children";
1716
+ }
1717
+ const offset = last?.kind === "item" ? last.offset : 0;
1718
+ const { width, at } = line.indent();
1719
+ if (width < offset || lastIsEmpty) {
1720
+ if (width < 4) {
1721
+ const marker = listMarker(line);
1722
+ if (marker !== undefined && marker.indent - offset < 4) {
1723
+ if (marker.char !== list.marker)
1724
+ return "close";
1725
+ const fromMarker = line.text.slice(at);
1726
+ const heading = scan.opened.at(-1)?.kind === "paragraph" && setextUnderline(fromMarker);
1727
+ if (thematicBreak(fromMarker) && !heading)
1728
+ return "close";
1729
+ return "children";
1730
+ }
1731
+ }
1732
+ if (!lastIsEmpty)
1733
+ return "close";
1734
+ }
1735
+ if (lastIsEmpty && width < offset)
1736
+ return "close";
1737
+ if (scan.emptyItemWithBlankLines)
1738
+ return "close";
1739
+ return "children";
1740
+ }
1741
+ function continueItem(scan, item, line) {
1742
+ const { width } = line.indent();
1743
+ if (line.blank()) {
1744
+ line.skip(Math.min(width, item.offset));
1745
+ return "children";
1746
+ }
1747
+ const isEmpty = item.children === 0 && scan.emptyItemWithBlankLines;
1748
+ if ((isEmpty || width < item.offset) && width < 4) {
1749
+ if (listMarker(line) !== undefined) {
1750
+ scan.skipList = true;
1751
+ return "close";
1752
+ }
1753
+ if (!isEmpty)
1754
+ return "close";
1755
+ }
1756
+ line.skip(Math.min(width, item.offset));
1757
+ return "children";
1758
+ }
1759
+ function listMarker(line) {
1760
+ const { width, at } = line.indent();
1761
+ if (width > 3)
1762
+ return;
1763
+ const { text } = line;
1764
+ const first = text.charAt(at);
1765
+ let end = at;
1766
+ let number;
1767
+ if (first === "-" || first === "*" || first === "+") {
1768
+ end = at + 1;
1769
+ } else {
1770
+ while (end < text.length && end - at <= 9 && /[0-9]/.test(text.charAt(end)))
1771
+ end++;
1772
+ const delimiter = text.charAt(end);
1773
+ if (end === at || end - at > 9 || delimiter !== "." && delimiter !== ")")
1774
+ return;
1775
+ number = text.slice(at, end);
1776
+ end++;
1777
+ }
1778
+ const after = text.charAt(end);
1779
+ if (end < text.length && after !== " " && after !== "\t")
1780
+ return;
1781
+ return {
1782
+ indent: width,
1783
+ end,
1784
+ char: text.charAt(end - 1),
1785
+ number,
1786
+ empty: /^[ \t]*$/.test(text.slice(end))
1787
+ };
1788
+ }
1789
+ function quoteMarker(line) {
1790
+ const { width, at } = line.indent();
1791
+ if (width > 3 || line.text.charAt(at) !== ">")
1792
+ return false;
1793
+ line.advanceTo(at + 1);
1794
+ const next = line.text.charAt(at + 1);
1795
+ if (next === " " || next === "\t")
1796
+ line.skip(1);
1797
+ return true;
1798
+ }
1799
+ function thematicBreak(text) {
1800
+ const marks = text.replace(/[ \t]/g, "");
1801
+ return marks.length >= 3 && /^(?:\*+|-+|_+)$/.test(marks);
1802
+ }
1803
+ function setextUnderline(text) {
1804
+ return /^(?:=+|-+)[ \t]*$/.test(text);
1805
+ }
1806
+ function runOf(text, index, char) {
1807
+ let end = index;
1808
+ while (text.charAt(end) === char)
1809
+ end++;
1810
+ return end - index;
1811
+ }
1812
+ function fenceColons(text) {
1813
+ const trimmed = text.replace(/^[ \t]+|[ \t]+$/g, "");
1814
+ return /^:+$/.test(trimmed) ? trimmed.length : 0;
1815
+ }
1816
+ function nameChar(char) {
1817
+ return /^[A-Za-z0-9_-]$/.test(char);
1818
+ }
1819
+ function typedOpener(text) {
1820
+ const colons = runOf(text, 0, ":");
1821
+ if (colons < 3)
1822
+ return;
1823
+ const rest = text.slice(colons);
1824
+ let nameEnd = 0;
1825
+ while (nameEnd < rest.length && nameChar(rest.charAt(nameEnd)))
1826
+ nameEnd++;
1827
+ if (nameEnd === 0 || rest.charAt(nameEnd) !== "{" || !rest.endsWith("}"))
1828
+ return;
1829
+ const attributes = directiveAttributes(rest.slice(nameEnd + 1, -1));
1830
+ if (attributes === undefined)
1831
+ return;
1832
+ return { name: rest.slice(0, nameEnd), id: attributes.id, colons };
1833
+ }
1834
+ function refusedDirective(text) {
1835
+ if (text.startsWith(":::"))
1836
+ return text !== ":::" && typedOpener(text) === undefined;
1837
+ if (text.startsWith("::"))
1838
+ return text.length > 2 && nameChar(text.charAt(2));
1839
+ return text.length > 1 && nameChar(text.charAt(1)) && text.includes("{");
1840
+ }
1841
+ function attributeSpace(char) {
1842
+ return char === " " || char >= "\t" && char <= "\r";
1843
+ }
1844
+ var BLOCK_ID_ATTRIBUTE = "blockId";
1845
+ function directiveAttributes(value) {
1846
+ const named = new Set;
1847
+ let id;
1848
+ let offset = 0;
1849
+ const name = () => {
1850
+ const start = offset;
1851
+ while (offset < value.length && nameChar(value.charAt(offset)))
1852
+ offset++;
1853
+ return value.slice(start, offset);
1854
+ };
1855
+ while (offset < value.length) {
1856
+ while (offset < value.length && attributeSpace(value.charAt(offset)))
1857
+ offset++;
1858
+ if (offset === value.length)
1859
+ break;
1860
+ const sigil = value.charAt(offset);
1861
+ if (sigil === "#") {
1862
+ offset++;
1863
+ const read = name();
1864
+ if (read === "" || named.has(BLOCK_ID_ATTRIBUTE))
1865
+ return;
1866
+ named.add(BLOCK_ID_ATTRIBUTE);
1867
+ id = read;
1868
+ } else if (sigil === ".") {
1869
+ offset++;
1870
+ if (name() === "")
1871
+ return;
1872
+ named.add("class");
1873
+ } else {
1874
+ const key = name();
1875
+ if (key === "" || named.has(key))
1876
+ return;
1877
+ named.add(key);
1878
+ if (offset === value.length || value.charAt(offset) === " ")
1879
+ continue;
1880
+ if (value.charAt(offset) !== "=" || value.charAt(offset + 1) !== '"')
1881
+ return;
1882
+ offset += 2;
1883
+ const start = offset;
1884
+ let escaped = false;
1885
+ while (offset < value.length && (value.charAt(offset) !== '"' || escaped)) {
1886
+ escaped = value.charAt(offset) === "\\" && !escaped;
1887
+ offset++;
1888
+ }
1889
+ if (offset === value.length)
1890
+ return;
1891
+ const quoted = value.slice(start, offset);
1892
+ if (!goQuoted(quoted))
1893
+ return;
1894
+ if (key === BLOCK_ID_ATTRIBUTE)
1895
+ id = /[\\&]/.test(quoted) ? undefined : quoted;
1896
+ offset++;
1897
+ }
1898
+ if (offset < value.length && !attributeSpace(value.charAt(offset)))
1899
+ return;
1900
+ }
1901
+ return { id };
1902
+ }
1903
+ function goQuoted(body) {
1904
+ return /^(?:[^\\\n"]|\\(?:[abfnrtv\\"]|x[0-9A-Fa-f]{2}|[0-3][0-7]{2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}))*$/.test(body);
1905
+ }
1906
+
1246
1907
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/core/core.js
1247
1908
  var NEVER = Object.freeze({
1248
1909
  status: "aborted"
@@ -15892,8 +16553,8 @@ class DispatchClient {
15892
16553
  async listProjectArtifacts(project, unlinked = false) {
15893
16554
  return this.#json("GET", ["api", "v1", "projects", project, "artifacts"], undefined, unlinked ? { unlinked: "true" } : undefined);
15894
16555
  }
15895
- async projectArtifact(project, input) {
15896
- return this.#upload(["api", "v1", "projects", project, "artifacts"], input);
16556
+ async projectArtifact(project, input, key) {
16557
+ return this.#upload(["api", "v1", "projects", project, "artifacts"], input, key);
15897
16558
  }
15898
16559
  async getProjectArtifact(project, slug) {
15899
16560
  return this.#json("GET", ["api", "v1", "projects", project, "artifacts", slug]);
@@ -16145,8 +16806,8 @@ class DispatchClient {
16145
16806
  async messageBlocks(id, session) {
16146
16807
  return this.#json("GET", ["api", "v1", "messages", id, "blocks"], undefined, session === undefined ? undefined : { session });
16147
16808
  }
16148
- async docEdit(id, input) {
16149
- return this.#json("POST", ["api", "v1", "artifacts", id, "edits"], input);
16809
+ async docEdit(id, input, key) {
16810
+ return this.#json("POST", ["api", "v1", "artifacts", id, "edits"], input, undefined, undefined, key);
16150
16811
  }
16151
16812
  async nameArtifactVersion(id, input) {
16152
16813
  return this.#json("POST", ["api", "v1", "artifacts", id, "versions"], input);
@@ -16202,17 +16863,17 @@ class DispatchClient {
16202
16863
  throw error;
16203
16864
  }
16204
16865
  }
16205
- async#json(method, path, body, query, signal) {
16206
- return (await this.#jsonAnswer(method, path, body, query, signal)).payload;
16866
+ async#json(method, path, body, query, signal, key) {
16867
+ return (await this.#jsonAnswer(method, path, body, query, signal, key)).payload;
16207
16868
  }
16208
- async#jsonAnswer(method, path, body, query, signal) {
16869
+ async#jsonAnswer(method, path, body, query, signal, key) {
16209
16870
  const url = this.#url(path, query);
16210
- const response = await this.#send(method, url, body === undefined ? undefined : { content: JSON.stringify(body), type: "application/json" }, undefined, signal);
16871
+ const response = await this.#send(method, url, body === undefined ? undefined : { content: JSON.stringify(body), type: "application/json" }, undefined, signal, key);
16211
16872
  return { status: response.status, payload: await this.#response(method, url, response) };
16212
16873
  }
16213
- async#upload(path, input) {
16874
+ async#upload(path, input, key) {
16214
16875
  if ("content" in input)
16215
- return this.#json("POST", path, input);
16876
+ return this.#json("POST", path, input, undefined, undefined, key);
16216
16877
  const form = new FormData;
16217
16878
  form.set("name", input.name);
16218
16879
  if (input.summary !== undefined)
@@ -16220,17 +16881,17 @@ class DispatchClient {
16220
16881
  if (input.actor !== undefined)
16221
16882
  form.set("actor", JSON.stringify(input.actor));
16222
16883
  form.set("file", input.file, input.name);
16223
- return this.#form("POST", path, form);
16884
+ return this.#form("POST", path, form, key);
16224
16885
  }
16225
- async#form(method, path, form) {
16886
+ async#form(method, path, form, key) {
16226
16887
  const url = this.#url(path);
16227
16888
  const encoded = new Response(form);
16228
16889
  const type = encoded.headers.get("Content-Type") ?? "multipart/form-data";
16229
16890
  const content = new Uint8Array(await encoded.arrayBuffer());
16230
- const response = await this.#send(method, url, { content, type });
16891
+ const response = await this.#send(method, url, { content, type }, undefined, undefined, key);
16231
16892
  return this.#response(method, url, response);
16232
16893
  }
16233
- async#send(method, url, body, accept = "application/json", signal) {
16894
+ async#send(method, url, body, accept = "application/json", signal, callerKey) {
16234
16895
  const headers = {
16235
16896
  Accept: accept,
16236
16897
  Authorization: `Bearer ${this.token}`
@@ -16245,7 +16906,7 @@ class DispatchClient {
16245
16906
  ...body === undefined ? {} : { body: body.content }
16246
16907
  });
16247
16908
  }
16248
- const key = crypto.randomUUID();
16909
+ const key = callerKey ?? crypto.randomUUID();
16249
16910
  headers[IDEMPOTENCY_KEY_HEADER] = key;
16250
16911
  const { pausesMs, withinMs, silenceMs, pause = pauseFor } = this.writeRetry;
16251
16912
  const request = `${method} ${url}`;
@@ -17289,7 +17950,7 @@ var RELATION_PHRASES = {
17289
17950
  replied_to_your_message: "replied to your message"
17290
17951
  };
17291
17952
  function isNoAnswersRoute(error) {
17292
- return error instanceof DispatchServiceError && error.status === 404;
17953
+ return isNoRoute(error);
17293
17954
  }
17294
17955
  function renderSessionAnswerItem(item) {
17295
17956
  const relation = `${RELATION_PHRASES[item.relation]}${item.replaced === true ? " (replaces an earlier answer)" : ""}`;
@@ -17328,7 +17989,7 @@ function renderSessionAnswersList(items, options) {
17328
17989
 
17329
17990
  `);
17330
17991
  }
17331
- var STATE_KEY = Symbol.for("legion.pi-envoy.session-answers.v2");
17992
+ var STATE_KEY = Symbol.for("legion.pi-envoy.session-answers.v3");
17332
17993
  var processGlobal = globalThis;
17333
17994
  var state = processGlobal[STATE_KEY] ?? {
17334
17995
  sessions: new Map,
@@ -18142,6 +18803,9 @@ async function resolveOwnerArguments(tool, input, cwd, env, exec, dashboardUrls,
18142
18803
  const issue = `${repo}#${legionIssue}`;
18143
18804
  return { args: { ...args, issue }, ref, owner: { kind: "issue", issue } };
18144
18805
  }
18806
+
18807
+ class DocumentNotFoundError extends Error {
18808
+ }
18145
18809
  function artifactByReference(artifacts, artifactReference, owner, canonical) {
18146
18810
  const bySlug = artifacts.find((candidate) => candidate.slug === artifactReference);
18147
18811
  if (canonical && bySlug !== undefined)
@@ -18162,7 +18826,7 @@ function artifactByReference(artifacts, artifactReference, owner, canonical) {
18162
18826
  }
18163
18827
  const [artifact] = byName;
18164
18828
  if (artifact === undefined) {
18165
- throw new Error(documentReferenceProblem(artifactReference, artifacts, owner));
18829
+ throw new DocumentNotFoundError(documentReferenceProblem(artifactReference, artifacts, owner));
18166
18830
  }
18167
18831
  return artifact;
18168
18832
  }
@@ -18193,10 +18857,25 @@ async function resolveArtifact(client, owner, artifactReference, { canonical = f
18193
18857
  artifact = artifactByReference(issue.artifacts, artifactReference, "issue", canonical);
18194
18858
  }
18195
18859
  if (!artifact) {
18196
- throw new Error(documentReferenceProblem(artifactReference ?? "spec", issue.artifacts, "issue"));
18860
+ throw new DocumentNotFoundError(documentReferenceProblem(artifactReference ?? "spec", issue.artifacts, "issue"));
18197
18861
  }
18198
18862
  return { owner, issue, artifact };
18199
18863
  }
18864
+ async function documentTargets(client, resolution) {
18865
+ const refDocument = resolution.ref?.kind === "artifact" ? resolution.ref.id : resolution.ref?.artifact;
18866
+ const owner = resolution.owner?.kind === "issue" ? { kind: "issue", issue: await resolveExistingIssue(client, resolution.owner.issue) } : resolution.owner;
18867
+ return {
18868
+ owner,
18869
+ documentOwner: () => {
18870
+ if (owner === null)
18871
+ throw new Error("issue or project is required");
18872
+ return owner;
18873
+ },
18874
+ resolveDocument: (documentOwner, reference) => resolveArtifact(client, documentOwner, reference, {
18875
+ canonical: reference !== undefined && reference === refDocument
18876
+ })
18877
+ };
18878
+ }
18200
18879
  var documentHintLimit = 8;
18201
18880
  function documentReferenceProblem(reference, documents, owner, use) {
18202
18881
  const hints = documents.slice(0, documentHintLimit).map((document) => use === "id" ? `${document.id} (${document.slug}, ${document.name})` : `${document.slug} (${document.name})`);
@@ -18777,13 +19456,11 @@ ${text}`,
18777
19456
  }
18778
19457
  var BLOCK_OPENING_CHARS = 60;
18779
19458
  async function listDocumentBlocks(client, artifact) {
18780
- const before = await client.docRead(artifact.id);
18781
- const blocks = await client.artifactBlocks(artifact.id);
18782
- const after = await client.docRead(artifact.id);
18783
- if (before.token !== after.token || before.markdown !== after.markdown) {
19459
+ const live = await liveBlocks(client, artifact);
19460
+ if (live === undefined) {
18784
19461
  throw new Error(`${artifact.name} changed while dispatch doc-read --blocks read it, so the opening words could name the wrong text; nothing is listed. Run it again.`);
18785
19462
  }
18786
- const bytes = new TextEncoder().encode(before.markdown);
19463
+ const { bytes, blocks } = live;
18787
19464
  const decoder = new TextDecoder;
18788
19465
  const lines = [
18789
19466
  `Blocks of ${artifact.name}, in document order, each indented under the block holding it: id, type, (token; anchored comments and asks), opening words. A table lists its rows by the index delete_row takes and its cells by the row and the column delete_column takes.`
@@ -18814,7 +19491,15 @@ async function listDocumentBlocks(client, artifact) {
18814
19491
  holders.push(block);
18815
19492
  }
18816
19493
  return { text: lines.join(`
18817
- `), token: before.token };
19494
+ `), token: live.token };
19495
+ }
19496
+ async function liveBlocks(client, artifact) {
19497
+ const before = await client.docRead(artifact.id);
19498
+ const blocks = await client.artifactBlocks(artifact.id);
19499
+ const after = await client.docRead(artifact.id);
19500
+ if (before.token !== after.token || before.markdown !== after.markdown)
19501
+ return;
19502
+ return { bytes: new TextEncoder().encode(before.markdown), token: before.token, blocks };
18818
19503
  }
18819
19504
  async function refuseOpenDecisionBlocks(client, tool, resolved) {
18820
19505
  const artifact = resolved.artifact;
@@ -19024,14 +19709,7 @@ async function executeDispatchTool(input) {
19024
19709
  const args = checked.args;
19025
19710
  const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
19026
19711
  const client = dispatchClient();
19027
- const refDocument = ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : ownerArguments.ref?.artifact;
19028
- const resolveDocument = (documentOwner, reference) => resolveArtifact(client, documentOwner, reference, {
19029
- canonical: reference !== undefined && reference === refDocument
19030
- });
19031
- const owner = ownerArguments.owner?.kind === "issue" ? {
19032
- kind: "issue",
19033
- issue: await resolveExistingIssue(client, ownerArguments.owner.issue)
19034
- } : ownerArguments.owner;
19712
+ const { owner, documentOwner, resolveDocument } = await documentTargets(client, ownerArguments);
19035
19713
  const issueArgument = optionalString(input.args, "issue");
19036
19714
  const refText = optionalString(ownerArguments.args, "ref");
19037
19715
  if (input.tool !== "dispatch_ask" && owner?.kind === "issue" && issueArgument !== undefined && refText !== undefined && ownerArguments.ref?.owner.kind === "issue" && owner.issue !== ownerArguments.ref.owner.issue) {
@@ -19044,11 +19722,6 @@ async function executeDispatchTool(input) {
19044
19722
  throw new Error("issue is required");
19045
19723
  return owner.issue;
19046
19724
  };
19047
- const documentOwner = () => {
19048
- if (owner === null)
19049
- throw new Error("issue or project is required");
19050
- return owner;
19051
- };
19052
19725
  const postWithPictures = (text, limit, owner, upload) => textWithPictures(input.tool, text, pictures, limit, owner, (picture) => upload(picture).catch((error) => {
19053
19726
  if (!(error instanceof DispatchWriteLandedError))
19054
19727
  throw error;
@@ -21322,12 +21995,16 @@ async function runDispatchCli(argv, rawEnv, io) {
21322
21995
  uploadBytes = bytes;
21323
21996
  }
21324
21997
  const spent = consumeMarker(gate.stores, argv, parsed.args, files, Date.now());
21998
+ const asksCarried = spent.ok ? 0 : newAsks(parsed.tool, uploadBytes === undefined ? parsed.args : { ...parsed.args, content: new TextDecoder().decode(uploadBytes) }).length;
21325
21999
  if (!spent.ok && gate.mode === "block") {
21326
- return refuse(spent.changed === undefined ? `${notCalled} AskGate is on for this session, and ${spent.why}. ${LITERAL_FORM} Send the write again in that form, as a bash call of its own.` : `${notCalled} AskGate is on for this session, and ${spent.why}. ${SAME_MESSAGE} Send the call again in a later message.`, REFUSED, parsed.tool, { mode: "block", why: spent.why });
22000
+ return refuse(spent.changed === undefined ? `${notCalled} AskGate is on for this session, and ${spent.why}. ${LITERAL_FORM} Send the write again in that form, as a bash call of its own.` : `${notCalled} AskGate is on for this session, and ${spent.why}. ${SAME_MESSAGE} Send the call again in a later message.`, REFUSED, parsed.tool, { mode: "block", why: spent.why, asksCarried });
21327
22001
  }
21328
22002
  if (!spent.ok) {
21329
22003
  try {
21330
- appendResult(dir, { tool: parsed.tool, askgate: { mode: "warn", why: spent.why } });
22004
+ appendResult(dir, {
22005
+ tool: parsed.tool,
22006
+ askgate: { mode: "warn", why: spent.why, asksCarried }
22007
+ });
21331
22008
  } catch (error) {
21332
22009
  return refuse(`${notCalled} AskGate is on for this session in warn mode, and ${spent.why}. A write that posts unreviewed is first recorded in this session's ledger, and the ledger could not be written: ${messageFor(error)}.`, REFUSED, parsed.tool);
21333
22010
  }
@@ -15643,7 +15643,7 @@ function sourceSession(sessionID) {
15643
15643
  // src/server.ts
15644
15644
  import { tool } from "@opencode-ai/plugin/tool";
15645
15645
  // package.json
15646
- var version2 = "12.2.0";
15646
+ var version2 = "12.3.0";
15647
15647
 
15648
15648
  // src/log.ts
15649
15649
  import { appendFile, mkdir } from "fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "12.2.0",
3
+ "version": "12.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "dist/src/server.js",