@openscout/scout 0.2.69 → 0.2.70
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/client/assets/{addon-fit-DtSNdpy5.js → addon-fit-BNV7JPhj.js} +1 -1
- package/dist/client/assets/{addon-webgl-YYNKPQUg.js → addon-webgl-B-_Y5L3F.js} +1 -1
- package/dist/client/assets/index-BJri_z5a.css +1 -0
- package/dist/client/assets/index-Ccjo5BZz.js +199 -0
- package/dist/client/assets/{xterm-fBtFsYpq.js → xterm-Cx1oABPt.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/main.mjs +13252 -12118
- package/dist/openscout-terminal-relay.mjs +17 -94
- package/dist/pair-supervisor.mjs +600 -19
- package/dist/runtime/broker-daemon.mjs +1075 -153
- package/dist/scout-control-plane-web.mjs +988 -187
- package/dist/scout-web-server.mjs +988 -187
- package/package.json +1 -1
- package/dist/client/assets/index-5-XOQrKH.js +0 -186
- package/dist/client/assets/index-BnA2HAbt.css +0 -1
package/dist/pair-supervisor.mjs
CHANGED
|
@@ -715,6 +715,59 @@ function renderToolResultContent(content) {
|
|
|
715
715
|
}
|
|
716
716
|
return stringifyUnknown(content);
|
|
717
717
|
}
|
|
718
|
+
function renderContentPartsText(content) {
|
|
719
|
+
if (typeof content === "string") {
|
|
720
|
+
return content;
|
|
721
|
+
}
|
|
722
|
+
if (!Array.isArray(content)) {
|
|
723
|
+
return "";
|
|
724
|
+
}
|
|
725
|
+
return content.map((entry) => {
|
|
726
|
+
if (typeof entry === "string") {
|
|
727
|
+
return entry;
|
|
728
|
+
}
|
|
729
|
+
if (!isRecord(entry)) {
|
|
730
|
+
return "";
|
|
731
|
+
}
|
|
732
|
+
if (typeof entry.text === "string") {
|
|
733
|
+
return entry.text;
|
|
734
|
+
}
|
|
735
|
+
if (typeof entry.content === "string") {
|
|
736
|
+
return entry.content;
|
|
737
|
+
}
|
|
738
|
+
return "";
|
|
739
|
+
}).filter(Boolean).join(`
|
|
740
|
+
`);
|
|
741
|
+
}
|
|
742
|
+
function extractReasoningText(item) {
|
|
743
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
744
|
+
const content = Array.isArray(item.content) ? item.content : [];
|
|
745
|
+
const summaryText = summary.map((entry) => {
|
|
746
|
+
if (typeof entry === "string") {
|
|
747
|
+
return entry;
|
|
748
|
+
}
|
|
749
|
+
const record = entry;
|
|
750
|
+
if (typeof record.text === "string") {
|
|
751
|
+
return record.text;
|
|
752
|
+
}
|
|
753
|
+
if (typeof record.summary === "string") {
|
|
754
|
+
return record.summary;
|
|
755
|
+
}
|
|
756
|
+
return "";
|
|
757
|
+
}).filter(Boolean).join(`
|
|
758
|
+
`);
|
|
759
|
+
const contentText = content.map((entry) => {
|
|
760
|
+
if (typeof entry === "string") {
|
|
761
|
+
return entry;
|
|
762
|
+
}
|
|
763
|
+
const record = entry;
|
|
764
|
+
return typeof record.text === "string" ? record.text : "";
|
|
765
|
+
}).filter(Boolean).join(`
|
|
766
|
+
`);
|
|
767
|
+
return [summaryText, contentText].filter(Boolean).join(`
|
|
768
|
+
|
|
769
|
+
`).trim();
|
|
770
|
+
}
|
|
718
771
|
function extractQuestionOptions(firstQuestion) {
|
|
719
772
|
const options = Array.isArray(firstQuestion.options) ? firstQuestion.options : [];
|
|
720
773
|
return options.map((option) => {
|
|
@@ -1424,20 +1477,520 @@ class ClaudeCodeHistoryParser {
|
|
|
1424
1477
|
});
|
|
1425
1478
|
}
|
|
1426
1479
|
}
|
|
1480
|
+
|
|
1481
|
+
class CodexHistoryParser {
|
|
1482
|
+
session;
|
|
1483
|
+
baseTimestampMs;
|
|
1484
|
+
events = [];
|
|
1485
|
+
currentTurn = null;
|
|
1486
|
+
turnCounter = 0;
|
|
1487
|
+
blockIndex = 0;
|
|
1488
|
+
blockById = new Map;
|
|
1489
|
+
toolBlockMap = new Map;
|
|
1490
|
+
assistantMessageTextThisTurn = new Set;
|
|
1491
|
+
inputTokens = 0;
|
|
1492
|
+
outputTokens = 0;
|
|
1493
|
+
reasoningOutputTokens = 0;
|
|
1494
|
+
cachedInputTokens = 0;
|
|
1495
|
+
tokenEventCount = 0;
|
|
1496
|
+
constructor(session, baseTimestampMs) {
|
|
1497
|
+
this.session = session;
|
|
1498
|
+
this.baseTimestampMs = baseTimestampMs;
|
|
1499
|
+
}
|
|
1500
|
+
parse(content) {
|
|
1501
|
+
const lines = content.split(/\r?\n/u);
|
|
1502
|
+
let parsedLineCount = 0;
|
|
1503
|
+
let skippedLineCount = 0;
|
|
1504
|
+
let lineCount = 0;
|
|
1505
|
+
let lastCapturedAt = this.baseTimestampMs;
|
|
1506
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
1507
|
+
const rawLine = lines[index];
|
|
1508
|
+
const trimmed = rawLine.trim();
|
|
1509
|
+
if (!trimmed) {
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
lineCount += 1;
|
|
1513
|
+
let record;
|
|
1514
|
+
try {
|
|
1515
|
+
const parsed = JSON.parse(trimmed);
|
|
1516
|
+
if (!isRecord(parsed)) {
|
|
1517
|
+
skippedLineCount += 1;
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
record = parsed;
|
|
1521
|
+
} catch {
|
|
1522
|
+
skippedLineCount += 1;
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
const capturedAt = extractRecordTimestamp(record) ?? this.baseTimestampMs + index;
|
|
1526
|
+
lastCapturedAt = capturedAt;
|
|
1527
|
+
if (this.handleRecord(record, capturedAt)) {
|
|
1528
|
+
parsedLineCount += 1;
|
|
1529
|
+
} else {
|
|
1530
|
+
skippedLineCount += 1;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (this.persistUsageMetadata()) {
|
|
1534
|
+
this.emitSessionUpdate(lastCapturedAt);
|
|
1535
|
+
}
|
|
1536
|
+
return {
|
|
1537
|
+
events: this.events,
|
|
1538
|
+
lineCount,
|
|
1539
|
+
parsedLineCount,
|
|
1540
|
+
skippedLineCount
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
handleRecord(record, capturedAt) {
|
|
1544
|
+
const type = maybeString(record.type);
|
|
1545
|
+
if (!type) {
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
switch (type) {
|
|
1549
|
+
case "session_meta":
|
|
1550
|
+
this.handleSessionMeta(record, capturedAt);
|
|
1551
|
+
return true;
|
|
1552
|
+
case "turn_context":
|
|
1553
|
+
this.handleTurnContext(record, capturedAt);
|
|
1554
|
+
return true;
|
|
1555
|
+
case "event_msg":
|
|
1556
|
+
this.handleEventMessage(record, capturedAt);
|
|
1557
|
+
return true;
|
|
1558
|
+
case "response_item":
|
|
1559
|
+
this.handleResponseItem(record, capturedAt);
|
|
1560
|
+
return true;
|
|
1561
|
+
case "compacted":
|
|
1562
|
+
return true;
|
|
1563
|
+
default:
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
handleSessionMeta(record, capturedAt) {
|
|
1568
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
1569
|
+
let changed = false;
|
|
1570
|
+
const providerMeta = this.ensureProviderMeta();
|
|
1571
|
+
const externalSessionId = maybeString(payload.id);
|
|
1572
|
+
if (externalSessionId && providerMeta.externalSessionId !== externalSessionId) {
|
|
1573
|
+
providerMeta.externalSessionId = externalSessionId;
|
|
1574
|
+
changed = true;
|
|
1575
|
+
}
|
|
1576
|
+
const cwd = maybeString(payload.cwd);
|
|
1577
|
+
if (cwd && this.session.cwd !== cwd) {
|
|
1578
|
+
this.session.cwd = cwd;
|
|
1579
|
+
if (!this.session.name || /^\d+$/u.test(this.session.name)) {
|
|
1580
|
+
this.session.name = basename(cwd) || "Codex Session";
|
|
1581
|
+
}
|
|
1582
|
+
changed = true;
|
|
1583
|
+
}
|
|
1584
|
+
const git = isRecord(payload.git) ? payload.git : null;
|
|
1585
|
+
const runtime = this.ensureObserveMetaRecord("observeRuntime");
|
|
1586
|
+
changed = this.assignObserveString(runtime, "originator", payload.originator) || changed;
|
|
1587
|
+
changed = this.assignObserveString(runtime, "cliVersion", payload.cli_version) || changed;
|
|
1588
|
+
changed = this.assignObserveString(runtime, "source", payload.source) || changed;
|
|
1589
|
+
changed = this.assignObserveString(runtime, "threadSource", payload.thread_source) || changed;
|
|
1590
|
+
changed = this.assignObserveString(runtime, "modelProvider", payload.model_provider) || changed;
|
|
1591
|
+
changed = this.assignObserveString(runtime, "gitBranch", git?.branch) || changed;
|
|
1592
|
+
changed = this.assignObserveString(runtime, "gitCommitHash", git?.commit_hash) || changed;
|
|
1593
|
+
changed = this.assignObserveString(runtime, "repositoryUrl", git?.repository_url) || changed;
|
|
1594
|
+
if (changed) {
|
|
1595
|
+
this.emitSessionUpdate(capturedAt);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
handleTurnContext(record, capturedAt) {
|
|
1599
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
1600
|
+
let changed = false;
|
|
1601
|
+
const cwd = maybeString(payload.cwd);
|
|
1602
|
+
if (cwd && this.session.cwd !== cwd) {
|
|
1603
|
+
this.session.cwd = cwd;
|
|
1604
|
+
changed = true;
|
|
1605
|
+
}
|
|
1606
|
+
const model = maybeString(payload.model);
|
|
1607
|
+
if (model && this.session.model !== model) {
|
|
1608
|
+
this.session.model = model;
|
|
1609
|
+
changed = true;
|
|
1610
|
+
}
|
|
1611
|
+
const runtime = this.ensureObserveMetaRecord("observeRuntime");
|
|
1612
|
+
changed = this.assignObserveString(runtime, "approvalPolicy", payload.approval_policy) || changed;
|
|
1613
|
+
changed = this.assignObserveString(runtime, "currentDate", payload.current_date) || changed;
|
|
1614
|
+
changed = this.assignObserveString(runtime, "timezone", payload.timezone) || changed;
|
|
1615
|
+
changed = this.assignObserveString(runtime, "effort", payload.effort) || changed;
|
|
1616
|
+
changed = this.assignObserveString(runtime, "personality", payload.personality) || changed;
|
|
1617
|
+
if (isRecord(payload.sandbox_policy) && runtime.sandboxPolicy !== payload.sandbox_policy) {
|
|
1618
|
+
runtime.sandboxPolicy = payload.sandbox_policy;
|
|
1619
|
+
changed = true;
|
|
1620
|
+
}
|
|
1621
|
+
if (changed) {
|
|
1622
|
+
this.emitSessionUpdate(capturedAt);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
handleEventMessage(record, capturedAt) {
|
|
1626
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
1627
|
+
const eventType = maybeString(payload.type);
|
|
1628
|
+
if (!eventType) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
switch (eventType) {
|
|
1632
|
+
case "task_started": {
|
|
1633
|
+
const startedAt = normalizeTimestamp(payload.started_at) ?? capturedAt;
|
|
1634
|
+
this.startTurn(startedAt, maybeString(payload.turn_id));
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
case "user_message":
|
|
1638
|
+
this.ensureTurn(capturedAt);
|
|
1639
|
+
break;
|
|
1640
|
+
case "agent_message": {
|
|
1641
|
+
const message = maybeString(payload.message);
|
|
1642
|
+
if (message) {
|
|
1643
|
+
this.appendCompletedText(capturedAt, message);
|
|
1644
|
+
this.assistantMessageTextThisTurn.add(message);
|
|
1645
|
+
}
|
|
1646
|
+
break;
|
|
1647
|
+
}
|
|
1648
|
+
case "patch_apply_end":
|
|
1649
|
+
this.handleToolOutput(capturedAt, maybeString(payload.call_id), [maybeString(payload.stdout), maybeString(payload.stderr)].filter(Boolean).join(`
|
|
1650
|
+
`), payload.success === false);
|
|
1651
|
+
break;
|
|
1652
|
+
case "token_count":
|
|
1653
|
+
this.captureTokenUsage(payload.info);
|
|
1654
|
+
break;
|
|
1655
|
+
case "task_complete":
|
|
1656
|
+
this.endTurn("completed", normalizeTimestamp(payload.completed_at) ?? capturedAt);
|
|
1657
|
+
break;
|
|
1658
|
+
case "context_compacted":
|
|
1659
|
+
case "web_search_end":
|
|
1660
|
+
break;
|
|
1661
|
+
default:
|
|
1662
|
+
break;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
handleResponseItem(record, capturedAt) {
|
|
1666
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
1667
|
+
const itemType = maybeString(payload.type);
|
|
1668
|
+
if (!itemType) {
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
switch (itemType) {
|
|
1672
|
+
case "message":
|
|
1673
|
+
this.handleResponseMessage(payload, capturedAt);
|
|
1674
|
+
break;
|
|
1675
|
+
case "reasoning":
|
|
1676
|
+
this.handleReasoning(payload, capturedAt);
|
|
1677
|
+
break;
|
|
1678
|
+
case "function_call":
|
|
1679
|
+
case "custom_tool_call":
|
|
1680
|
+
this.handleToolCall(payload, capturedAt);
|
|
1681
|
+
break;
|
|
1682
|
+
case "function_call_output":
|
|
1683
|
+
case "custom_tool_call_output":
|
|
1684
|
+
this.handleToolOutput(capturedAt, maybeString(payload.call_id), renderToolResultContent(payload.output), false);
|
|
1685
|
+
break;
|
|
1686
|
+
case "web_search_call":
|
|
1687
|
+
this.handleWebSearchCall(payload, capturedAt);
|
|
1688
|
+
break;
|
|
1689
|
+
default:
|
|
1690
|
+
break;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
handleResponseMessage(payload, capturedAt) {
|
|
1694
|
+
const role = maybeString(payload.role);
|
|
1695
|
+
if (role !== "assistant") {
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
const text = renderContentPartsText(payload.content).trim();
|
|
1699
|
+
if (!text || this.assistantMessageTextThisTurn.has(text)) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
this.appendCompletedText(capturedAt, text);
|
|
1703
|
+
this.assistantMessageTextThisTurn.add(text);
|
|
1704
|
+
}
|
|
1705
|
+
handleReasoning(payload, capturedAt) {
|
|
1706
|
+
const text = extractReasoningText(payload);
|
|
1707
|
+
if (!text) {
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
const turn = this.ensureTurn(capturedAt);
|
|
1711
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
1712
|
+
type: "reasoning",
|
|
1713
|
+
text,
|
|
1714
|
+
status: "completed"
|
|
1715
|
+
});
|
|
1716
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
1717
|
+
}
|
|
1718
|
+
handleToolCall(payload, capturedAt) {
|
|
1719
|
+
const turn = this.ensureTurn(capturedAt);
|
|
1720
|
+
const toolName = maybeString(payload.name) ?? "unknown";
|
|
1721
|
+
const toolCallId = maybeString(payload.call_id) ?? `${turn.id}:tool:${this.blockIndex}`;
|
|
1722
|
+
const input = this.parseToolInput(payload.arguments ?? payload.input);
|
|
1723
|
+
const action = this.buildAction(toolName, toolCallId, input);
|
|
1724
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
1725
|
+
id: `${turn.id}:action:${toolCallId}`,
|
|
1726
|
+
type: "action",
|
|
1727
|
+
action,
|
|
1728
|
+
status: "streaming"
|
|
1729
|
+
});
|
|
1730
|
+
this.toolBlockMap.set(toolCallId, block.id);
|
|
1731
|
+
}
|
|
1732
|
+
handleWebSearchCall(payload, capturedAt) {
|
|
1733
|
+
const turn = this.ensureTurn(capturedAt);
|
|
1734
|
+
const toolCallId = `${turn.id}:web-search:${this.blockIndex}`;
|
|
1735
|
+
const action = {
|
|
1736
|
+
kind: "tool_call",
|
|
1737
|
+
toolName: "web_search",
|
|
1738
|
+
toolCallId,
|
|
1739
|
+
input: payload.action,
|
|
1740
|
+
result: payload.status,
|
|
1741
|
+
status: maybeString(payload.status) === "failed" ? "failed" : "completed",
|
|
1742
|
+
output: ""
|
|
1743
|
+
};
|
|
1744
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
1745
|
+
id: `${turn.id}:action:${toolCallId}`,
|
|
1746
|
+
type: "action",
|
|
1747
|
+
action,
|
|
1748
|
+
status: "completed"
|
|
1749
|
+
});
|
|
1750
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
1751
|
+
}
|
|
1752
|
+
parseToolInput(value) {
|
|
1753
|
+
if (typeof value !== "string") {
|
|
1754
|
+
return value;
|
|
1755
|
+
}
|
|
1756
|
+
const trimmed = value.trim();
|
|
1757
|
+
if (!trimmed) {
|
|
1758
|
+
return "";
|
|
1759
|
+
}
|
|
1760
|
+
try {
|
|
1761
|
+
return JSON.parse(trimmed);
|
|
1762
|
+
} catch {
|
|
1763
|
+
return value;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
buildAction(toolName, toolCallId, input) {
|
|
1767
|
+
const inputRecord = isRecord(input) ? input : {};
|
|
1768
|
+
if (toolName === "exec_command") {
|
|
1769
|
+
return {
|
|
1770
|
+
kind: "command",
|
|
1771
|
+
command: maybeString(inputRecord.cmd) ?? "",
|
|
1772
|
+
status: "running",
|
|
1773
|
+
output: ""
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
return {
|
|
1777
|
+
kind: "tool_call",
|
|
1778
|
+
toolName,
|
|
1779
|
+
toolCallId,
|
|
1780
|
+
input,
|
|
1781
|
+
status: "running",
|
|
1782
|
+
output: ""
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
handleToolOutput(capturedAt, toolCallId, output, isError) {
|
|
1786
|
+
if (!toolCallId) {
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
const blockId = this.toolBlockMap.get(toolCallId);
|
|
1790
|
+
const turn = this.currentTurn;
|
|
1791
|
+
if (!blockId || !turn) {
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
if (output) {
|
|
1795
|
+
this.emitEvent(capturedAt, {
|
|
1796
|
+
event: "block:action:output",
|
|
1797
|
+
sessionId: this.session.id,
|
|
1798
|
+
turnId: turn.id,
|
|
1799
|
+
blockId,
|
|
1800
|
+
output
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
const exitCode = this.extractExitCode(output);
|
|
1804
|
+
const status = isError || exitCode != null && exitCode !== 0 ? "failed" : "completed";
|
|
1805
|
+
this.emitEvent(capturedAt, {
|
|
1806
|
+
event: "block:action:status",
|
|
1807
|
+
sessionId: this.session.id,
|
|
1808
|
+
turnId: turn.id,
|
|
1809
|
+
blockId,
|
|
1810
|
+
status,
|
|
1811
|
+
...exitCode != null ? { meta: { exitCode } } : {}
|
|
1812
|
+
});
|
|
1813
|
+
const block = this.blockById.get(blockId);
|
|
1814
|
+
if (block) {
|
|
1815
|
+
this.emitBlockEnd(capturedAt, turn, block, status === "failed" ? "failed" : "completed");
|
|
1816
|
+
}
|
|
1817
|
+
this.toolBlockMap.delete(toolCallId);
|
|
1818
|
+
}
|
|
1819
|
+
extractExitCode(output) {
|
|
1820
|
+
const match = output.match(/(?:exit code|exited with code):\s*(-?\d+)/iu);
|
|
1821
|
+
if (!match) {
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
const exitCode = Number(match[1]);
|
|
1825
|
+
return Number.isFinite(exitCode) ? exitCode : null;
|
|
1826
|
+
}
|
|
1827
|
+
captureTokenUsage(info) {
|
|
1828
|
+
if (!isRecord(info)) {
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
const total = isRecord(info.total_token_usage) ? info.total_token_usage : {};
|
|
1832
|
+
this.inputTokens = maybeNumber(total.input_tokens) ?? this.inputTokens;
|
|
1833
|
+
this.outputTokens = maybeNumber(total.output_tokens) ?? this.outputTokens;
|
|
1834
|
+
this.reasoningOutputTokens = maybeNumber(total.reasoning_output_tokens) ?? this.reasoningOutputTokens;
|
|
1835
|
+
this.cachedInputTokens = maybeNumber(total.cached_input_tokens) ?? this.cachedInputTokens;
|
|
1836
|
+
this.tokenEventCount += 1;
|
|
1837
|
+
}
|
|
1838
|
+
persistUsageMetadata() {
|
|
1839
|
+
if (this.tokenEventCount === 0) {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
const usage = this.ensureObserveMetaRecord("observeUsage");
|
|
1843
|
+
let changed = false;
|
|
1844
|
+
const assignNumber = (key, value) => {
|
|
1845
|
+
if (value > 0 && usage[key] !== value) {
|
|
1846
|
+
usage[key] = value;
|
|
1847
|
+
changed = true;
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
assignNumber("inputTokens", this.inputTokens);
|
|
1851
|
+
assignNumber("outputTokens", this.outputTokens);
|
|
1852
|
+
assignNumber("reasoningOutputTokens", this.reasoningOutputTokens);
|
|
1853
|
+
assignNumber("cacheReadInputTokens", this.cachedInputTokens);
|
|
1854
|
+
assignNumber("tokenEvents", this.tokenEventCount);
|
|
1855
|
+
return changed;
|
|
1856
|
+
}
|
|
1857
|
+
startTurn(capturedAt, turnId) {
|
|
1858
|
+
if (this.currentTurn) {
|
|
1859
|
+
this.endTurn("stopped", capturedAt);
|
|
1860
|
+
}
|
|
1861
|
+
this.turnCounter += 1;
|
|
1862
|
+
this.blockIndex = 0;
|
|
1863
|
+
this.blockById.clear();
|
|
1864
|
+
this.toolBlockMap.clear();
|
|
1865
|
+
this.assistantMessageTextThisTurn.clear();
|
|
1866
|
+
this.session.status = "active";
|
|
1867
|
+
this.emitSessionUpdate(capturedAt);
|
|
1868
|
+
const turn = {
|
|
1869
|
+
id: turnId || `history-turn-${this.turnCounter}`,
|
|
1870
|
+
sessionId: this.session.id,
|
|
1871
|
+
status: "started",
|
|
1872
|
+
startedAt: new Date(capturedAt).toISOString(),
|
|
1873
|
+
blocks: []
|
|
1874
|
+
};
|
|
1875
|
+
this.currentTurn = turn;
|
|
1876
|
+
this.emitEvent(capturedAt, {
|
|
1877
|
+
event: "turn:start",
|
|
1878
|
+
sessionId: this.session.id,
|
|
1879
|
+
turn
|
|
1880
|
+
});
|
|
1881
|
+
return turn;
|
|
1882
|
+
}
|
|
1883
|
+
ensureTurn(capturedAt) {
|
|
1884
|
+
return this.currentTurn ?? this.startTurn(capturedAt);
|
|
1885
|
+
}
|
|
1886
|
+
endTurn(status, capturedAt) {
|
|
1887
|
+
const turn = this.currentTurn;
|
|
1888
|
+
if (!turn) {
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
turn.status = status;
|
|
1892
|
+
turn.endedAt = new Date(capturedAt).toISOString();
|
|
1893
|
+
this.emitEvent(capturedAt, {
|
|
1894
|
+
event: "turn:end",
|
|
1895
|
+
sessionId: this.session.id,
|
|
1896
|
+
turnId: turn.id,
|
|
1897
|
+
status
|
|
1898
|
+
});
|
|
1899
|
+
this.currentTurn = null;
|
|
1900
|
+
this.blockById.clear();
|
|
1901
|
+
this.toolBlockMap.clear();
|
|
1902
|
+
this.assistantMessageTextThisTurn.clear();
|
|
1903
|
+
this.session.status = "idle";
|
|
1904
|
+
this.emitSessionUpdate(capturedAt);
|
|
1905
|
+
}
|
|
1906
|
+
appendCompletedText(capturedAt, text) {
|
|
1907
|
+
const turn = this.ensureTurn(capturedAt);
|
|
1908
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
1909
|
+
type: "text",
|
|
1910
|
+
text,
|
|
1911
|
+
status: "completed"
|
|
1912
|
+
});
|
|
1913
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
1914
|
+
}
|
|
1915
|
+
startBlock(turn, capturedAt, partial) {
|
|
1916
|
+
const block = {
|
|
1917
|
+
...partial,
|
|
1918
|
+
id: partial.id || `${turn.id}:block:${this.blockIndex}`,
|
|
1919
|
+
turnId: turn.id,
|
|
1920
|
+
index: this.blockIndex
|
|
1921
|
+
};
|
|
1922
|
+
this.blockIndex += 1;
|
|
1923
|
+
turn.blocks.push(block);
|
|
1924
|
+
this.blockById.set(block.id, block);
|
|
1925
|
+
this.emitEvent(capturedAt, {
|
|
1926
|
+
event: "block:start",
|
|
1927
|
+
sessionId: this.session.id,
|
|
1928
|
+
turnId: turn.id,
|
|
1929
|
+
block
|
|
1930
|
+
});
|
|
1931
|
+
return block;
|
|
1932
|
+
}
|
|
1933
|
+
emitBlockEnd(capturedAt, turn, block, status) {
|
|
1934
|
+
block.status = status;
|
|
1935
|
+
this.emitEvent(capturedAt, {
|
|
1936
|
+
event: "block:end",
|
|
1937
|
+
sessionId: this.session.id,
|
|
1938
|
+
turnId: turn.id,
|
|
1939
|
+
blockId: block.id,
|
|
1940
|
+
status
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
ensureProviderMeta() {
|
|
1944
|
+
const providerMeta = isRecord(this.session.providerMeta) ? this.session.providerMeta : {};
|
|
1945
|
+
this.session.providerMeta = providerMeta;
|
|
1946
|
+
return providerMeta;
|
|
1947
|
+
}
|
|
1948
|
+
ensureObserveMetaRecord(key) {
|
|
1949
|
+
const providerMeta = this.ensureProviderMeta();
|
|
1950
|
+
const existing = providerMeta[key];
|
|
1951
|
+
if (isRecord(existing)) {
|
|
1952
|
+
return existing;
|
|
1953
|
+
}
|
|
1954
|
+
const next = {};
|
|
1955
|
+
providerMeta[key] = next;
|
|
1956
|
+
return next;
|
|
1957
|
+
}
|
|
1958
|
+
assignObserveString(target, key, value) {
|
|
1959
|
+
const next = maybeString(value);
|
|
1960
|
+
if (!next || target[key] === next) {
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1963
|
+
target[key] = next;
|
|
1964
|
+
return true;
|
|
1965
|
+
}
|
|
1966
|
+
emitSessionUpdate(capturedAt) {
|
|
1967
|
+
this.emitEvent(capturedAt, {
|
|
1968
|
+
event: "session:update",
|
|
1969
|
+
session: { ...this.session }
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
emitEvent(capturedAt, event) {
|
|
1973
|
+
this.events.push({
|
|
1974
|
+
capturedAt,
|
|
1975
|
+
event: structuredClone(event)
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1427
1979
|
function inferHistorySessionAdapterType(path2, adapterType) {
|
|
1428
1980
|
return inferHistoryAdapterType(path2, adapterType);
|
|
1429
1981
|
}
|
|
1430
1982
|
function supportsHistorySessionSnapshotForPath(path2, adapterType) {
|
|
1431
|
-
|
|
1983
|
+
const inferred = inferHistoryAdapterType(path2, adapterType);
|
|
1984
|
+
return inferred === "claude-code" || inferred === "codex";
|
|
1432
1985
|
}
|
|
1433
1986
|
function createHistorySessionSnapshot(input) {
|
|
1434
1987
|
const adapterType = inferHistoryAdapterType(input.path, input.adapterType);
|
|
1435
|
-
if (adapterType !== "claude-code") {
|
|
1988
|
+
if (adapterType !== "claude-code" && adapterType !== "codex") {
|
|
1436
1989
|
throw new Error(`History snapshot is not supported for adapter type "${adapterType}".`);
|
|
1437
1990
|
}
|
|
1438
1991
|
const session = buildBaseHistorySession(input, adapterType);
|
|
1439
1992
|
const baseTimestampMs = normalizeTimestamp(input.baseTimestampMs) ?? Date.now();
|
|
1440
|
-
const parser = new ClaudeCodeHistoryParser(session, baseTimestampMs);
|
|
1993
|
+
const parser = adapterType === "codex" ? new CodexHistoryParser(session, baseTimestampMs) : new ClaudeCodeHistoryParser(session, baseTimestampMs);
|
|
1441
1994
|
const replay = parser.parse(input.content);
|
|
1442
1995
|
const tracker = new StateTracker;
|
|
1443
1996
|
tracker.createSession(session.id, session);
|
|
@@ -3334,7 +3887,7 @@ function stringifyValue(value) {
|
|
|
3334
3887
|
return String(value);
|
|
3335
3888
|
}
|
|
3336
3889
|
}
|
|
3337
|
-
function
|
|
3890
|
+
function extractReasoningText2(item) {
|
|
3338
3891
|
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
3339
3892
|
const content = Array.isArray(item.content) ? item.content : [];
|
|
3340
3893
|
const summaryText = summary.map((entry) => {
|
|
@@ -3810,7 +4363,7 @@ var init_codex = __esm(() => {
|
|
|
3810
4363
|
this.ensureTextBlock(turnState, itemId, typeof item.text === "string" ? item.text : "");
|
|
3811
4364
|
return;
|
|
3812
4365
|
case "reasoning": {
|
|
3813
|
-
const text =
|
|
4366
|
+
const text = extractReasoningText2(item);
|
|
3814
4367
|
if (text) {
|
|
3815
4368
|
this.ensureReasoningBlock(turnState, itemId, text);
|
|
3816
4369
|
}
|
|
@@ -3889,7 +4442,7 @@ var init_codex = __esm(() => {
|
|
|
3889
4442
|
return;
|
|
3890
4443
|
}
|
|
3891
4444
|
case "reasoning": {
|
|
3892
|
-
const finalText =
|
|
4445
|
+
const finalText = extractReasoningText2(item);
|
|
3893
4446
|
if (!finalText && !turnState.blocksByItemId.has(itemId)) {
|
|
3894
4447
|
return;
|
|
3895
4448
|
}
|
|
@@ -7813,6 +8366,25 @@ function buildScoutReturnAddress(input) {
|
|
|
7813
8366
|
}
|
|
7814
8367
|
return next;
|
|
7815
8368
|
}
|
|
8369
|
+
// ../protocol/src/channel-identity.ts
|
|
8370
|
+
function mintChannelId(randomUuid) {
|
|
8371
|
+
return `${CHANNEL_ID_PREFIX}${randomUuid().toLowerCase()}`;
|
|
8372
|
+
}
|
|
8373
|
+
function directChannelNaturalKey(participantIds) {
|
|
8374
|
+
return `direct:${stableIdentityParts(participantIds).join(",")}`;
|
|
8375
|
+
}
|
|
8376
|
+
function channelNaturalKeyFromMetadata(metadata) {
|
|
8377
|
+
const value = metadata?.[CHANNEL_NATURAL_KEY_METADATA];
|
|
8378
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8379
|
+
}
|
|
8380
|
+
function stableIdentityParts(values) {
|
|
8381
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean))).sort().map(encodeIdentityPart);
|
|
8382
|
+
}
|
|
8383
|
+
function encodeIdentityPart(value) {
|
|
8384
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
8385
|
+
}
|
|
8386
|
+
var CHANNEL_ID_PREFIX = "c.", CHANNEL_NATURAL_KEY_METADATA = "naturalKey";
|
|
8387
|
+
|
|
7816
8388
|
// ../protocol/src/collaboration.ts
|
|
7817
8389
|
function isQuestionTerminalState(state) {
|
|
7818
8390
|
return state === "closed" || state === "declined";
|
|
@@ -14922,6 +15494,7 @@ var init_local_agents = __esm(() => {
|
|
|
14922
15494
|
"mcp__scout__messages_inbox",
|
|
14923
15495
|
"mcp__scout__messages_channel",
|
|
14924
15496
|
"mcp__scout__broker_feed",
|
|
15497
|
+
"mcp__scout__tail_events",
|
|
14925
15498
|
"mcp__scout__agents_search",
|
|
14926
15499
|
"mcp__scout__agents_resolve",
|
|
14927
15500
|
"mcp__scout__messages_reply",
|
|
@@ -19499,6 +20072,7 @@ function createScoutAgentService(deps) {
|
|
|
19499
20072
|
}
|
|
19500
20073
|
|
|
19501
20074
|
// ../web/server/core/broker/service.ts
|
|
20075
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
19502
20076
|
init_src2();
|
|
19503
20077
|
init_setup();
|
|
19504
20078
|
init_broker_api();
|
|
@@ -19571,6 +20145,8 @@ var scoutBrokerPaths = {
|
|
|
19571
20145
|
node: "/v1/node",
|
|
19572
20146
|
snapshot: "/v1/snapshot",
|
|
19573
20147
|
topologySnapshot: "/v1/topology/snapshot",
|
|
20148
|
+
tailDiscover: "/v1/tail/discover",
|
|
20149
|
+
tailRecent: "/v1/tail/recent",
|
|
19574
20150
|
messages: "/v1/messages",
|
|
19575
20151
|
eventsStream: "/v1/events/stream",
|
|
19576
20152
|
actors: "/v1/actors",
|
|
@@ -19589,7 +20165,6 @@ var scoutBrokerPaths = {
|
|
|
19589
20165
|
};
|
|
19590
20166
|
|
|
19591
20167
|
// ../web/server/core/broker/service.ts
|
|
19592
|
-
var BROKER_SHARED_CHANNEL_ID = "channel.shared";
|
|
19593
20168
|
var OPERATOR_ID = "operator";
|
|
19594
20169
|
var DEFAULT_BROKER_HOST2 = "127.0.0.1";
|
|
19595
20170
|
var DEFAULT_BROKER_PORT2 = 65535;
|
|
@@ -19916,21 +20491,15 @@ async function ensureTargetRelayAgentRegistered(baseUrl, snapshot, nodeId, agent
|
|
|
19916
20491
|
});
|
|
19917
20492
|
return Boolean(binding);
|
|
19918
20493
|
}
|
|
19919
|
-
function
|
|
19920
|
-
|
|
19921
|
-
return `dm.${sourceId}.${targetId}`;
|
|
19922
|
-
}
|
|
19923
|
-
if (sourceId === OPERATOR_ID || targetId === OPERATOR_ID) {
|
|
19924
|
-
const peerId = sourceId === OPERATOR_ID ? targetId : sourceId;
|
|
19925
|
-
return `dm.${OPERATOR_ID}.${peerId}`;
|
|
19926
|
-
}
|
|
19927
|
-
return `dm.${[sourceId, targetId].sort().join(".")}`;
|
|
20494
|
+
function findConversationByIdentity(snapshot, naturalKey) {
|
|
20495
|
+
return Object.values(snapshot.conversations).find((conversation) => channelNaturalKeyFromMetadata(conversation.metadata) === naturalKey);
|
|
19928
20496
|
}
|
|
19929
20497
|
async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId, sourceId, targetId) {
|
|
19930
|
-
const conversationId = targetId === SCOUT_AGENT_ID && sourceId === OPERATOR_ID ? BROKER_SHARED_CHANNEL_ID : directConversationIdForActors(sourceId, targetId);
|
|
19931
20498
|
const participantIds = [...new Set([sourceId, targetId])].sort();
|
|
20499
|
+
const naturalKey = directChannelNaturalKey(participantIds);
|
|
20500
|
+
const existing = findConversationByIdentity(snapshot, naturalKey);
|
|
20501
|
+
const conversationId = existing?.id ?? mintChannelId(randomUUID3);
|
|
19932
20502
|
const nextShareMode = resolveConversationShareMode(snapshot, nodeId, participantIds, "local");
|
|
19933
|
-
const existing = snapshot.conversations[conversationId];
|
|
19934
20503
|
const alreadyMatches = existing && existing.kind === "direct" && existing.shareMode === nextShareMode && existing.visibility === "private" && existing.participantIds.join("\x00") === participantIds.join("\x00");
|
|
19935
20504
|
if (alreadyMatches) {
|
|
19936
20505
|
const preferredTargetId = targetId === OPERATOR_ID ? sourceId : targetId;
|
|
@@ -19952,6 +20521,7 @@ async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId,
|
|
|
19952
20521
|
participantIds,
|
|
19953
20522
|
metadata: {
|
|
19954
20523
|
surface: "scout",
|
|
20524
|
+
naturalKey,
|
|
19955
20525
|
...targetId === SCOUT_AGENT_ID && sourceId === OPERATOR_ID ? { role: "partner" } : {}
|
|
19956
20526
|
}
|
|
19957
20527
|
};
|
|
@@ -20222,6 +20792,11 @@ function transientBrokerWorkingStatusPredicate(alias) {
|
|
|
20222
20792
|
var HEARTRATE_WINDOW_MS = 7 * 24 * 60 * 60000;
|
|
20223
20793
|
// ../web/server/db/runs.ts
|
|
20224
20794
|
init_src2();
|
|
20795
|
+
|
|
20796
|
+
// ../web/server/db/sessions.ts
|
|
20797
|
+
init_src2();
|
|
20798
|
+
|
|
20799
|
+
// ../web/server/db/runs.ts
|
|
20225
20800
|
var ACTIVE_RUN_STATES = new Set([
|
|
20226
20801
|
"queued",
|
|
20227
20802
|
"waking",
|
|
@@ -20992,7 +21567,7 @@ async function ensureScoutbotRegistered(baseUrl, snapshot, nodeId, currentDirect
|
|
|
20992
21567
|
await postJson(baseUrl, "/v1/actors", actor);
|
|
20993
21568
|
}
|
|
20994
21569
|
const existingAgent = snapshot.agents?.[SCOUTBOT_AGENT_ID];
|
|
20995
|
-
if (!existingAgent || !
|
|
21570
|
+
if (!existingAgent || !hasCurrentScoutbotAgentRegistration(existingAgent, nodeId)) {
|
|
20996
21571
|
await postJson(baseUrl, "/v1/agents", agent);
|
|
20997
21572
|
}
|
|
20998
21573
|
const existingEndpoint = findScoutbotEndpoint(snapshot, nodeId);
|
|
@@ -21187,6 +21762,9 @@ function hasScoutbotLabels(agent) {
|
|
|
21187
21762
|
const labels = new Set(agent.labels ?? []);
|
|
21188
21763
|
return labels.has("assistant") && labels.has("scout") && labels.has("scoutbot");
|
|
21189
21764
|
}
|
|
21765
|
+
function hasCurrentScoutbotAgentRegistration(agent, nodeId) {
|
|
21766
|
+
return hasScoutbotLabels(agent) && hasCurrentScoutbotAgentConfig(agent) && agent.homeNodeId === nodeId && agent.authorityNodeId === nodeId && agent.advertiseScope === "local" && agent.wakePolicy === "keep_warm" && agent.metadata?.source === "scoutbot";
|
|
21767
|
+
}
|
|
21190
21768
|
function hasCurrentScoutbotAgentConfig(agent) {
|
|
21191
21769
|
const roleConfig = metadataObject(agent.metadata, "roleConfig");
|
|
21192
21770
|
return metadataString5(roleConfig, "systemPrompt") === SCOUTBOT_ROLE_CONFIG.systemPrompt;
|
|
@@ -38949,6 +39527,9 @@ init_src2();
|
|
|
38949
39527
|
// ../runtime/src/drizzle-migrate.ts
|
|
38950
39528
|
init_support_paths();
|
|
38951
39529
|
if (false) {}
|
|
39530
|
+
|
|
39531
|
+
// ../runtime/src/conversations/api.ts
|
|
39532
|
+
init_src2();
|
|
38952
39533
|
// ../runtime/src/tailscale.ts
|
|
38953
39534
|
import { execFile } from "child_process";
|
|
38954
39535
|
import { promisify } from "util";
|