@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
|
@@ -1503,6 +1503,31 @@ function buildScoutReturnAddress(input) {
|
|
|
1503
1503
|
}
|
|
1504
1504
|
return next;
|
|
1505
1505
|
}
|
|
1506
|
+
// packages/protocol/src/channel-identity.ts
|
|
1507
|
+
function mintChannelId(randomUuid) {
|
|
1508
|
+
return `${CHANNEL_ID_PREFIX}${randomUuid().toLowerCase()}`;
|
|
1509
|
+
}
|
|
1510
|
+
function directChannelNaturalKey(participantIds) {
|
|
1511
|
+
return `direct:${stableIdentityParts(participantIds).join(",")}`;
|
|
1512
|
+
}
|
|
1513
|
+
function namedChannelNaturalKey(channel) {
|
|
1514
|
+
return `channel:${encodeIdentityPart(channel.trim().toLowerCase() || "shared")}`;
|
|
1515
|
+
}
|
|
1516
|
+
function systemChannelNaturalKey(name) {
|
|
1517
|
+
return `system:${encodeIdentityPart(name.trim().toLowerCase() || "system")}`;
|
|
1518
|
+
}
|
|
1519
|
+
function channelNaturalKeyFromMetadata(metadata) {
|
|
1520
|
+
const value = metadata?.[CHANNEL_NATURAL_KEY_METADATA];
|
|
1521
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1522
|
+
}
|
|
1523
|
+
function stableIdentityParts(values) {
|
|
1524
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean))).sort().map(encodeIdentityPart);
|
|
1525
|
+
}
|
|
1526
|
+
function encodeIdentityPart(value) {
|
|
1527
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
1528
|
+
}
|
|
1529
|
+
var CHANNEL_ID_PREFIX = "c.", CHANNEL_NATURAL_KEY_METADATA = "naturalKey";
|
|
1530
|
+
|
|
1506
1531
|
// packages/protocol/src/collaboration.ts
|
|
1507
1532
|
function isQuestionTerminalState(state) {
|
|
1508
1533
|
return state === "closed" || state === "declined";
|
|
@@ -5742,6 +5767,59 @@ function renderToolResultContent(content) {
|
|
|
5742
5767
|
}
|
|
5743
5768
|
return stringifyUnknown(content);
|
|
5744
5769
|
}
|
|
5770
|
+
function renderContentPartsText(content) {
|
|
5771
|
+
if (typeof content === "string") {
|
|
5772
|
+
return content;
|
|
5773
|
+
}
|
|
5774
|
+
if (!Array.isArray(content)) {
|
|
5775
|
+
return "";
|
|
5776
|
+
}
|
|
5777
|
+
return content.map((entry) => {
|
|
5778
|
+
if (typeof entry === "string") {
|
|
5779
|
+
return entry;
|
|
5780
|
+
}
|
|
5781
|
+
if (!isRecord(entry)) {
|
|
5782
|
+
return "";
|
|
5783
|
+
}
|
|
5784
|
+
if (typeof entry.text === "string") {
|
|
5785
|
+
return entry.text;
|
|
5786
|
+
}
|
|
5787
|
+
if (typeof entry.content === "string") {
|
|
5788
|
+
return entry.content;
|
|
5789
|
+
}
|
|
5790
|
+
return "";
|
|
5791
|
+
}).filter(Boolean).join(`
|
|
5792
|
+
`);
|
|
5793
|
+
}
|
|
5794
|
+
function extractReasoningText(item) {
|
|
5795
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
5796
|
+
const content = Array.isArray(item.content) ? item.content : [];
|
|
5797
|
+
const summaryText = summary.map((entry) => {
|
|
5798
|
+
if (typeof entry === "string") {
|
|
5799
|
+
return entry;
|
|
5800
|
+
}
|
|
5801
|
+
const record = entry;
|
|
5802
|
+
if (typeof record.text === "string") {
|
|
5803
|
+
return record.text;
|
|
5804
|
+
}
|
|
5805
|
+
if (typeof record.summary === "string") {
|
|
5806
|
+
return record.summary;
|
|
5807
|
+
}
|
|
5808
|
+
return "";
|
|
5809
|
+
}).filter(Boolean).join(`
|
|
5810
|
+
`);
|
|
5811
|
+
const contentText = content.map((entry) => {
|
|
5812
|
+
if (typeof entry === "string") {
|
|
5813
|
+
return entry;
|
|
5814
|
+
}
|
|
5815
|
+
const record = entry;
|
|
5816
|
+
return typeof record.text === "string" ? record.text : "";
|
|
5817
|
+
}).filter(Boolean).join(`
|
|
5818
|
+
`);
|
|
5819
|
+
return [summaryText, contentText].filter(Boolean).join(`
|
|
5820
|
+
|
|
5821
|
+
`).trim();
|
|
5822
|
+
}
|
|
5745
5823
|
function extractQuestionOptions(firstQuestion) {
|
|
5746
5824
|
const options = Array.isArray(firstQuestion.options) ? firstQuestion.options : [];
|
|
5747
5825
|
return options.map((option) => {
|
|
@@ -6451,17 +6529,517 @@ class ClaudeCodeHistoryParser {
|
|
|
6451
6529
|
});
|
|
6452
6530
|
}
|
|
6453
6531
|
}
|
|
6532
|
+
|
|
6533
|
+
class CodexHistoryParser {
|
|
6534
|
+
session;
|
|
6535
|
+
baseTimestampMs;
|
|
6536
|
+
events = [];
|
|
6537
|
+
currentTurn = null;
|
|
6538
|
+
turnCounter = 0;
|
|
6539
|
+
blockIndex = 0;
|
|
6540
|
+
blockById = new Map;
|
|
6541
|
+
toolBlockMap = new Map;
|
|
6542
|
+
assistantMessageTextThisTurn = new Set;
|
|
6543
|
+
inputTokens = 0;
|
|
6544
|
+
outputTokens = 0;
|
|
6545
|
+
reasoningOutputTokens = 0;
|
|
6546
|
+
cachedInputTokens = 0;
|
|
6547
|
+
tokenEventCount = 0;
|
|
6548
|
+
constructor(session, baseTimestampMs) {
|
|
6549
|
+
this.session = session;
|
|
6550
|
+
this.baseTimestampMs = baseTimestampMs;
|
|
6551
|
+
}
|
|
6552
|
+
parse(content) {
|
|
6553
|
+
const lines = content.split(/\r?\n/u);
|
|
6554
|
+
let parsedLineCount = 0;
|
|
6555
|
+
let skippedLineCount = 0;
|
|
6556
|
+
let lineCount = 0;
|
|
6557
|
+
let lastCapturedAt = this.baseTimestampMs;
|
|
6558
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
6559
|
+
const rawLine = lines[index];
|
|
6560
|
+
const trimmed = rawLine.trim();
|
|
6561
|
+
if (!trimmed) {
|
|
6562
|
+
continue;
|
|
6563
|
+
}
|
|
6564
|
+
lineCount += 1;
|
|
6565
|
+
let record;
|
|
6566
|
+
try {
|
|
6567
|
+
const parsed = JSON.parse(trimmed);
|
|
6568
|
+
if (!isRecord(parsed)) {
|
|
6569
|
+
skippedLineCount += 1;
|
|
6570
|
+
continue;
|
|
6571
|
+
}
|
|
6572
|
+
record = parsed;
|
|
6573
|
+
} catch {
|
|
6574
|
+
skippedLineCount += 1;
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
const capturedAt = extractRecordTimestamp(record) ?? this.baseTimestampMs + index;
|
|
6578
|
+
lastCapturedAt = capturedAt;
|
|
6579
|
+
if (this.handleRecord(record, capturedAt)) {
|
|
6580
|
+
parsedLineCount += 1;
|
|
6581
|
+
} else {
|
|
6582
|
+
skippedLineCount += 1;
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
if (this.persistUsageMetadata()) {
|
|
6586
|
+
this.emitSessionUpdate(lastCapturedAt);
|
|
6587
|
+
}
|
|
6588
|
+
return {
|
|
6589
|
+
events: this.events,
|
|
6590
|
+
lineCount,
|
|
6591
|
+
parsedLineCount,
|
|
6592
|
+
skippedLineCount
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
6595
|
+
handleRecord(record, capturedAt) {
|
|
6596
|
+
const type = maybeString(record.type);
|
|
6597
|
+
if (!type) {
|
|
6598
|
+
return false;
|
|
6599
|
+
}
|
|
6600
|
+
switch (type) {
|
|
6601
|
+
case "session_meta":
|
|
6602
|
+
this.handleSessionMeta(record, capturedAt);
|
|
6603
|
+
return true;
|
|
6604
|
+
case "turn_context":
|
|
6605
|
+
this.handleTurnContext(record, capturedAt);
|
|
6606
|
+
return true;
|
|
6607
|
+
case "event_msg":
|
|
6608
|
+
this.handleEventMessage(record, capturedAt);
|
|
6609
|
+
return true;
|
|
6610
|
+
case "response_item":
|
|
6611
|
+
this.handleResponseItem(record, capturedAt);
|
|
6612
|
+
return true;
|
|
6613
|
+
case "compacted":
|
|
6614
|
+
return true;
|
|
6615
|
+
default:
|
|
6616
|
+
return false;
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
handleSessionMeta(record, capturedAt) {
|
|
6620
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
6621
|
+
let changed = false;
|
|
6622
|
+
const providerMeta = this.ensureProviderMeta();
|
|
6623
|
+
const externalSessionId = maybeString(payload.id);
|
|
6624
|
+
if (externalSessionId && providerMeta.externalSessionId !== externalSessionId) {
|
|
6625
|
+
providerMeta.externalSessionId = externalSessionId;
|
|
6626
|
+
changed = true;
|
|
6627
|
+
}
|
|
6628
|
+
const cwd = maybeString(payload.cwd);
|
|
6629
|
+
if (cwd && this.session.cwd !== cwd) {
|
|
6630
|
+
this.session.cwd = cwd;
|
|
6631
|
+
if (!this.session.name || /^\d+$/u.test(this.session.name)) {
|
|
6632
|
+
this.session.name = basename2(cwd) || "Codex Session";
|
|
6633
|
+
}
|
|
6634
|
+
changed = true;
|
|
6635
|
+
}
|
|
6636
|
+
const git = isRecord(payload.git) ? payload.git : null;
|
|
6637
|
+
const runtime = this.ensureObserveMetaRecord("observeRuntime");
|
|
6638
|
+
changed = this.assignObserveString(runtime, "originator", payload.originator) || changed;
|
|
6639
|
+
changed = this.assignObserveString(runtime, "cliVersion", payload.cli_version) || changed;
|
|
6640
|
+
changed = this.assignObserveString(runtime, "source", payload.source) || changed;
|
|
6641
|
+
changed = this.assignObserveString(runtime, "threadSource", payload.thread_source) || changed;
|
|
6642
|
+
changed = this.assignObserveString(runtime, "modelProvider", payload.model_provider) || changed;
|
|
6643
|
+
changed = this.assignObserveString(runtime, "gitBranch", git?.branch) || changed;
|
|
6644
|
+
changed = this.assignObserveString(runtime, "gitCommitHash", git?.commit_hash) || changed;
|
|
6645
|
+
changed = this.assignObserveString(runtime, "repositoryUrl", git?.repository_url) || changed;
|
|
6646
|
+
if (changed) {
|
|
6647
|
+
this.emitSessionUpdate(capturedAt);
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
handleTurnContext(record, capturedAt) {
|
|
6651
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
6652
|
+
let changed = false;
|
|
6653
|
+
const cwd = maybeString(payload.cwd);
|
|
6654
|
+
if (cwd && this.session.cwd !== cwd) {
|
|
6655
|
+
this.session.cwd = cwd;
|
|
6656
|
+
changed = true;
|
|
6657
|
+
}
|
|
6658
|
+
const model = maybeString(payload.model);
|
|
6659
|
+
if (model && this.session.model !== model) {
|
|
6660
|
+
this.session.model = model;
|
|
6661
|
+
changed = true;
|
|
6662
|
+
}
|
|
6663
|
+
const runtime = this.ensureObserveMetaRecord("observeRuntime");
|
|
6664
|
+
changed = this.assignObserveString(runtime, "approvalPolicy", payload.approval_policy) || changed;
|
|
6665
|
+
changed = this.assignObserveString(runtime, "currentDate", payload.current_date) || changed;
|
|
6666
|
+
changed = this.assignObserveString(runtime, "timezone", payload.timezone) || changed;
|
|
6667
|
+
changed = this.assignObserveString(runtime, "effort", payload.effort) || changed;
|
|
6668
|
+
changed = this.assignObserveString(runtime, "personality", payload.personality) || changed;
|
|
6669
|
+
if (isRecord(payload.sandbox_policy) && runtime.sandboxPolicy !== payload.sandbox_policy) {
|
|
6670
|
+
runtime.sandboxPolicy = payload.sandbox_policy;
|
|
6671
|
+
changed = true;
|
|
6672
|
+
}
|
|
6673
|
+
if (changed) {
|
|
6674
|
+
this.emitSessionUpdate(capturedAt);
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
handleEventMessage(record, capturedAt) {
|
|
6678
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
6679
|
+
const eventType = maybeString(payload.type);
|
|
6680
|
+
if (!eventType) {
|
|
6681
|
+
return;
|
|
6682
|
+
}
|
|
6683
|
+
switch (eventType) {
|
|
6684
|
+
case "task_started": {
|
|
6685
|
+
const startedAt = normalizeTimestamp(payload.started_at) ?? capturedAt;
|
|
6686
|
+
this.startTurn(startedAt, maybeString(payload.turn_id));
|
|
6687
|
+
break;
|
|
6688
|
+
}
|
|
6689
|
+
case "user_message":
|
|
6690
|
+
this.ensureTurn(capturedAt);
|
|
6691
|
+
break;
|
|
6692
|
+
case "agent_message": {
|
|
6693
|
+
const message = maybeString(payload.message);
|
|
6694
|
+
if (message) {
|
|
6695
|
+
this.appendCompletedText(capturedAt, message);
|
|
6696
|
+
this.assistantMessageTextThisTurn.add(message);
|
|
6697
|
+
}
|
|
6698
|
+
break;
|
|
6699
|
+
}
|
|
6700
|
+
case "patch_apply_end":
|
|
6701
|
+
this.handleToolOutput(capturedAt, maybeString(payload.call_id), [maybeString(payload.stdout), maybeString(payload.stderr)].filter(Boolean).join(`
|
|
6702
|
+
`), payload.success === false);
|
|
6703
|
+
break;
|
|
6704
|
+
case "token_count":
|
|
6705
|
+
this.captureTokenUsage(payload.info);
|
|
6706
|
+
break;
|
|
6707
|
+
case "task_complete":
|
|
6708
|
+
this.endTurn("completed", normalizeTimestamp(payload.completed_at) ?? capturedAt);
|
|
6709
|
+
break;
|
|
6710
|
+
case "context_compacted":
|
|
6711
|
+
case "web_search_end":
|
|
6712
|
+
break;
|
|
6713
|
+
default:
|
|
6714
|
+
break;
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
handleResponseItem(record, capturedAt) {
|
|
6718
|
+
const payload = isRecord(record.payload) ? record.payload : {};
|
|
6719
|
+
const itemType = maybeString(payload.type);
|
|
6720
|
+
if (!itemType) {
|
|
6721
|
+
return;
|
|
6722
|
+
}
|
|
6723
|
+
switch (itemType) {
|
|
6724
|
+
case "message":
|
|
6725
|
+
this.handleResponseMessage(payload, capturedAt);
|
|
6726
|
+
break;
|
|
6727
|
+
case "reasoning":
|
|
6728
|
+
this.handleReasoning(payload, capturedAt);
|
|
6729
|
+
break;
|
|
6730
|
+
case "function_call":
|
|
6731
|
+
case "custom_tool_call":
|
|
6732
|
+
this.handleToolCall(payload, capturedAt);
|
|
6733
|
+
break;
|
|
6734
|
+
case "function_call_output":
|
|
6735
|
+
case "custom_tool_call_output":
|
|
6736
|
+
this.handleToolOutput(capturedAt, maybeString(payload.call_id), renderToolResultContent(payload.output), false);
|
|
6737
|
+
break;
|
|
6738
|
+
case "web_search_call":
|
|
6739
|
+
this.handleWebSearchCall(payload, capturedAt);
|
|
6740
|
+
break;
|
|
6741
|
+
default:
|
|
6742
|
+
break;
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
handleResponseMessage(payload, capturedAt) {
|
|
6746
|
+
const role = maybeString(payload.role);
|
|
6747
|
+
if (role !== "assistant") {
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
const text = renderContentPartsText(payload.content).trim();
|
|
6751
|
+
if (!text || this.assistantMessageTextThisTurn.has(text)) {
|
|
6752
|
+
return;
|
|
6753
|
+
}
|
|
6754
|
+
this.appendCompletedText(capturedAt, text);
|
|
6755
|
+
this.assistantMessageTextThisTurn.add(text);
|
|
6756
|
+
}
|
|
6757
|
+
handleReasoning(payload, capturedAt) {
|
|
6758
|
+
const text = extractReasoningText(payload);
|
|
6759
|
+
if (!text) {
|
|
6760
|
+
return;
|
|
6761
|
+
}
|
|
6762
|
+
const turn = this.ensureTurn(capturedAt);
|
|
6763
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
6764
|
+
type: "reasoning",
|
|
6765
|
+
text,
|
|
6766
|
+
status: "completed"
|
|
6767
|
+
});
|
|
6768
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
6769
|
+
}
|
|
6770
|
+
handleToolCall(payload, capturedAt) {
|
|
6771
|
+
const turn = this.ensureTurn(capturedAt);
|
|
6772
|
+
const toolName = maybeString(payload.name) ?? "unknown";
|
|
6773
|
+
const toolCallId = maybeString(payload.call_id) ?? `${turn.id}:tool:${this.blockIndex}`;
|
|
6774
|
+
const input = this.parseToolInput(payload.arguments ?? payload.input);
|
|
6775
|
+
const action = this.buildAction(toolName, toolCallId, input);
|
|
6776
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
6777
|
+
id: `${turn.id}:action:${toolCallId}`,
|
|
6778
|
+
type: "action",
|
|
6779
|
+
action,
|
|
6780
|
+
status: "streaming"
|
|
6781
|
+
});
|
|
6782
|
+
this.toolBlockMap.set(toolCallId, block.id);
|
|
6783
|
+
}
|
|
6784
|
+
handleWebSearchCall(payload, capturedAt) {
|
|
6785
|
+
const turn = this.ensureTurn(capturedAt);
|
|
6786
|
+
const toolCallId = `${turn.id}:web-search:${this.blockIndex}`;
|
|
6787
|
+
const action = {
|
|
6788
|
+
kind: "tool_call",
|
|
6789
|
+
toolName: "web_search",
|
|
6790
|
+
toolCallId,
|
|
6791
|
+
input: payload.action,
|
|
6792
|
+
result: payload.status,
|
|
6793
|
+
status: maybeString(payload.status) === "failed" ? "failed" : "completed",
|
|
6794
|
+
output: ""
|
|
6795
|
+
};
|
|
6796
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
6797
|
+
id: `${turn.id}:action:${toolCallId}`,
|
|
6798
|
+
type: "action",
|
|
6799
|
+
action,
|
|
6800
|
+
status: "completed"
|
|
6801
|
+
});
|
|
6802
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
6803
|
+
}
|
|
6804
|
+
parseToolInput(value) {
|
|
6805
|
+
if (typeof value !== "string") {
|
|
6806
|
+
return value;
|
|
6807
|
+
}
|
|
6808
|
+
const trimmed = value.trim();
|
|
6809
|
+
if (!trimmed) {
|
|
6810
|
+
return "";
|
|
6811
|
+
}
|
|
6812
|
+
try {
|
|
6813
|
+
return JSON.parse(trimmed);
|
|
6814
|
+
} catch {
|
|
6815
|
+
return value;
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
buildAction(toolName, toolCallId, input) {
|
|
6819
|
+
const inputRecord = isRecord(input) ? input : {};
|
|
6820
|
+
if (toolName === "exec_command") {
|
|
6821
|
+
return {
|
|
6822
|
+
kind: "command",
|
|
6823
|
+
command: maybeString(inputRecord.cmd) ?? "",
|
|
6824
|
+
status: "running",
|
|
6825
|
+
output: ""
|
|
6826
|
+
};
|
|
6827
|
+
}
|
|
6828
|
+
return {
|
|
6829
|
+
kind: "tool_call",
|
|
6830
|
+
toolName,
|
|
6831
|
+
toolCallId,
|
|
6832
|
+
input,
|
|
6833
|
+
status: "running",
|
|
6834
|
+
output: ""
|
|
6835
|
+
};
|
|
6836
|
+
}
|
|
6837
|
+
handleToolOutput(capturedAt, toolCallId, output, isError) {
|
|
6838
|
+
if (!toolCallId) {
|
|
6839
|
+
return;
|
|
6840
|
+
}
|
|
6841
|
+
const blockId = this.toolBlockMap.get(toolCallId);
|
|
6842
|
+
const turn = this.currentTurn;
|
|
6843
|
+
if (!blockId || !turn) {
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
6846
|
+
if (output) {
|
|
6847
|
+
this.emitEvent(capturedAt, {
|
|
6848
|
+
event: "block:action:output",
|
|
6849
|
+
sessionId: this.session.id,
|
|
6850
|
+
turnId: turn.id,
|
|
6851
|
+
blockId,
|
|
6852
|
+
output
|
|
6853
|
+
});
|
|
6854
|
+
}
|
|
6855
|
+
const exitCode = this.extractExitCode(output);
|
|
6856
|
+
const status = isError || exitCode != null && exitCode !== 0 ? "failed" : "completed";
|
|
6857
|
+
this.emitEvent(capturedAt, {
|
|
6858
|
+
event: "block:action:status",
|
|
6859
|
+
sessionId: this.session.id,
|
|
6860
|
+
turnId: turn.id,
|
|
6861
|
+
blockId,
|
|
6862
|
+
status,
|
|
6863
|
+
...exitCode != null ? { meta: { exitCode } } : {}
|
|
6864
|
+
});
|
|
6865
|
+
const block = this.blockById.get(blockId);
|
|
6866
|
+
if (block) {
|
|
6867
|
+
this.emitBlockEnd(capturedAt, turn, block, status === "failed" ? "failed" : "completed");
|
|
6868
|
+
}
|
|
6869
|
+
this.toolBlockMap.delete(toolCallId);
|
|
6870
|
+
}
|
|
6871
|
+
extractExitCode(output) {
|
|
6872
|
+
const match2 = output.match(/(?:exit code|exited with code):\s*(-?\d+)/iu);
|
|
6873
|
+
if (!match2) {
|
|
6874
|
+
return null;
|
|
6875
|
+
}
|
|
6876
|
+
const exitCode = Number(match2[1]);
|
|
6877
|
+
return Number.isFinite(exitCode) ? exitCode : null;
|
|
6878
|
+
}
|
|
6879
|
+
captureTokenUsage(info) {
|
|
6880
|
+
if (!isRecord(info)) {
|
|
6881
|
+
return;
|
|
6882
|
+
}
|
|
6883
|
+
const total = isRecord(info.total_token_usage) ? info.total_token_usage : {};
|
|
6884
|
+
this.inputTokens = maybeNumber(total.input_tokens) ?? this.inputTokens;
|
|
6885
|
+
this.outputTokens = maybeNumber(total.output_tokens) ?? this.outputTokens;
|
|
6886
|
+
this.reasoningOutputTokens = maybeNumber(total.reasoning_output_tokens) ?? this.reasoningOutputTokens;
|
|
6887
|
+
this.cachedInputTokens = maybeNumber(total.cached_input_tokens) ?? this.cachedInputTokens;
|
|
6888
|
+
this.tokenEventCount += 1;
|
|
6889
|
+
}
|
|
6890
|
+
persistUsageMetadata() {
|
|
6891
|
+
if (this.tokenEventCount === 0) {
|
|
6892
|
+
return false;
|
|
6893
|
+
}
|
|
6894
|
+
const usage = this.ensureObserveMetaRecord("observeUsage");
|
|
6895
|
+
let changed = false;
|
|
6896
|
+
const assignNumber = (key, value) => {
|
|
6897
|
+
if (value > 0 && usage[key] !== value) {
|
|
6898
|
+
usage[key] = value;
|
|
6899
|
+
changed = true;
|
|
6900
|
+
}
|
|
6901
|
+
};
|
|
6902
|
+
assignNumber("inputTokens", this.inputTokens);
|
|
6903
|
+
assignNumber("outputTokens", this.outputTokens);
|
|
6904
|
+
assignNumber("reasoningOutputTokens", this.reasoningOutputTokens);
|
|
6905
|
+
assignNumber("cacheReadInputTokens", this.cachedInputTokens);
|
|
6906
|
+
assignNumber("tokenEvents", this.tokenEventCount);
|
|
6907
|
+
return changed;
|
|
6908
|
+
}
|
|
6909
|
+
startTurn(capturedAt, turnId) {
|
|
6910
|
+
if (this.currentTurn) {
|
|
6911
|
+
this.endTurn("stopped", capturedAt);
|
|
6912
|
+
}
|
|
6913
|
+
this.turnCounter += 1;
|
|
6914
|
+
this.blockIndex = 0;
|
|
6915
|
+
this.blockById.clear();
|
|
6916
|
+
this.toolBlockMap.clear();
|
|
6917
|
+
this.assistantMessageTextThisTurn.clear();
|
|
6918
|
+
this.session.status = "active";
|
|
6919
|
+
this.emitSessionUpdate(capturedAt);
|
|
6920
|
+
const turn = {
|
|
6921
|
+
id: turnId || `history-turn-${this.turnCounter}`,
|
|
6922
|
+
sessionId: this.session.id,
|
|
6923
|
+
status: "started",
|
|
6924
|
+
startedAt: new Date(capturedAt).toISOString(),
|
|
6925
|
+
blocks: []
|
|
6926
|
+
};
|
|
6927
|
+
this.currentTurn = turn;
|
|
6928
|
+
this.emitEvent(capturedAt, {
|
|
6929
|
+
event: "turn:start",
|
|
6930
|
+
sessionId: this.session.id,
|
|
6931
|
+
turn
|
|
6932
|
+
});
|
|
6933
|
+
return turn;
|
|
6934
|
+
}
|
|
6935
|
+
ensureTurn(capturedAt) {
|
|
6936
|
+
return this.currentTurn ?? this.startTurn(capturedAt);
|
|
6937
|
+
}
|
|
6938
|
+
endTurn(status, capturedAt) {
|
|
6939
|
+
const turn = this.currentTurn;
|
|
6940
|
+
if (!turn) {
|
|
6941
|
+
return;
|
|
6942
|
+
}
|
|
6943
|
+
turn.status = status;
|
|
6944
|
+
turn.endedAt = new Date(capturedAt).toISOString();
|
|
6945
|
+
this.emitEvent(capturedAt, {
|
|
6946
|
+
event: "turn:end",
|
|
6947
|
+
sessionId: this.session.id,
|
|
6948
|
+
turnId: turn.id,
|
|
6949
|
+
status
|
|
6950
|
+
});
|
|
6951
|
+
this.currentTurn = null;
|
|
6952
|
+
this.blockById.clear();
|
|
6953
|
+
this.toolBlockMap.clear();
|
|
6954
|
+
this.assistantMessageTextThisTurn.clear();
|
|
6955
|
+
this.session.status = "idle";
|
|
6956
|
+
this.emitSessionUpdate(capturedAt);
|
|
6957
|
+
}
|
|
6958
|
+
appendCompletedText(capturedAt, text) {
|
|
6959
|
+
const turn = this.ensureTurn(capturedAt);
|
|
6960
|
+
const block = this.startBlock(turn, capturedAt, {
|
|
6961
|
+
type: "text",
|
|
6962
|
+
text,
|
|
6963
|
+
status: "completed"
|
|
6964
|
+
});
|
|
6965
|
+
this.emitBlockEnd(capturedAt, turn, block, "completed");
|
|
6966
|
+
}
|
|
6967
|
+
startBlock(turn, capturedAt, partial) {
|
|
6968
|
+
const block = {
|
|
6969
|
+
...partial,
|
|
6970
|
+
id: partial.id || `${turn.id}:block:${this.blockIndex}`,
|
|
6971
|
+
turnId: turn.id,
|
|
6972
|
+
index: this.blockIndex
|
|
6973
|
+
};
|
|
6974
|
+
this.blockIndex += 1;
|
|
6975
|
+
turn.blocks.push(block);
|
|
6976
|
+
this.blockById.set(block.id, block);
|
|
6977
|
+
this.emitEvent(capturedAt, {
|
|
6978
|
+
event: "block:start",
|
|
6979
|
+
sessionId: this.session.id,
|
|
6980
|
+
turnId: turn.id,
|
|
6981
|
+
block
|
|
6982
|
+
});
|
|
6983
|
+
return block;
|
|
6984
|
+
}
|
|
6985
|
+
emitBlockEnd(capturedAt, turn, block, status) {
|
|
6986
|
+
block.status = status;
|
|
6987
|
+
this.emitEvent(capturedAt, {
|
|
6988
|
+
event: "block:end",
|
|
6989
|
+
sessionId: this.session.id,
|
|
6990
|
+
turnId: turn.id,
|
|
6991
|
+
blockId: block.id,
|
|
6992
|
+
status
|
|
6993
|
+
});
|
|
6994
|
+
}
|
|
6995
|
+
ensureProviderMeta() {
|
|
6996
|
+
const providerMeta = isRecord(this.session.providerMeta) ? this.session.providerMeta : {};
|
|
6997
|
+
this.session.providerMeta = providerMeta;
|
|
6998
|
+
return providerMeta;
|
|
6999
|
+
}
|
|
7000
|
+
ensureObserveMetaRecord(key) {
|
|
7001
|
+
const providerMeta = this.ensureProviderMeta();
|
|
7002
|
+
const existing = providerMeta[key];
|
|
7003
|
+
if (isRecord(existing)) {
|
|
7004
|
+
return existing;
|
|
7005
|
+
}
|
|
7006
|
+
const next = {};
|
|
7007
|
+
providerMeta[key] = next;
|
|
7008
|
+
return next;
|
|
7009
|
+
}
|
|
7010
|
+
assignObserveString(target, key, value) {
|
|
7011
|
+
const next = maybeString(value);
|
|
7012
|
+
if (!next || target[key] === next) {
|
|
7013
|
+
return false;
|
|
7014
|
+
}
|
|
7015
|
+
target[key] = next;
|
|
7016
|
+
return true;
|
|
7017
|
+
}
|
|
7018
|
+
emitSessionUpdate(capturedAt) {
|
|
7019
|
+
this.emitEvent(capturedAt, {
|
|
7020
|
+
event: "session:update",
|
|
7021
|
+
session: { ...this.session }
|
|
7022
|
+
});
|
|
7023
|
+
}
|
|
7024
|
+
emitEvent(capturedAt, event) {
|
|
7025
|
+
this.events.push({
|
|
7026
|
+
capturedAt,
|
|
7027
|
+
event: structuredClone(event)
|
|
7028
|
+
});
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
6454
7031
|
function supportsHistorySessionSnapshotForPath(path, adapterType) {
|
|
6455
|
-
|
|
7032
|
+
const inferred = inferHistoryAdapterType(path, adapterType);
|
|
7033
|
+
return inferred === "claude-code" || inferred === "codex";
|
|
6456
7034
|
}
|
|
6457
7035
|
function createHistorySessionSnapshot(input) {
|
|
6458
7036
|
const adapterType = inferHistoryAdapterType(input.path, input.adapterType);
|
|
6459
|
-
if (adapterType !== "claude-code") {
|
|
7037
|
+
if (adapterType !== "claude-code" && adapterType !== "codex") {
|
|
6460
7038
|
throw new Error(`History snapshot is not supported for adapter type "${adapterType}".`);
|
|
6461
7039
|
}
|
|
6462
7040
|
const session = buildBaseHistorySession(input, adapterType);
|
|
6463
7041
|
const baseTimestampMs = normalizeTimestamp(input.baseTimestampMs) ?? Date.now();
|
|
6464
|
-
const parser = new ClaudeCodeHistoryParser(session, baseTimestampMs);
|
|
7042
|
+
const parser = adapterType === "codex" ? new CodexHistoryParser(session, baseTimestampMs) : new ClaudeCodeHistoryParser(session, baseTimestampMs);
|
|
6465
7043
|
const replay = parser.parse(input.content);
|
|
6466
7044
|
const tracker = new StateTracker;
|
|
6467
7045
|
tracker.createSession(session.id, session);
|
|
@@ -8181,7 +8759,7 @@ function stringifyValue(value) {
|
|
|
8181
8759
|
return String(value);
|
|
8182
8760
|
}
|
|
8183
8761
|
}
|
|
8184
|
-
function
|
|
8762
|
+
function extractReasoningText2(item) {
|
|
8185
8763
|
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
8186
8764
|
const content = Array.isArray(item.content) ? item.content : [];
|
|
8187
8765
|
const summaryText = summary.map((entry) => {
|
|
@@ -8657,7 +9235,7 @@ var init_codex = __esm(() => {
|
|
|
8657
9235
|
this.ensureTextBlock(turnState, itemId, typeof item.text === "string" ? item.text : "");
|
|
8658
9236
|
return;
|
|
8659
9237
|
case "reasoning": {
|
|
8660
|
-
const text =
|
|
9238
|
+
const text = extractReasoningText2(item);
|
|
8661
9239
|
if (text) {
|
|
8662
9240
|
this.ensureReasoningBlock(turnState, itemId, text);
|
|
8663
9241
|
}
|
|
@@ -8736,7 +9314,7 @@ var init_codex = __esm(() => {
|
|
|
8736
9314
|
return;
|
|
8737
9315
|
}
|
|
8738
9316
|
case "reasoning": {
|
|
8739
|
-
const finalText =
|
|
9317
|
+
const finalText = extractReasoningText2(item);
|
|
8740
9318
|
if (!finalText && !turnState.blocksByItemId.has(itemId)) {
|
|
8741
9319
|
return;
|
|
8742
9320
|
}
|
|
@@ -12481,7 +13059,7 @@ function isCodexThreadGlobalMessage(message) {
|
|
|
12481
13059
|
function sessionKey2(options) {
|
|
12482
13060
|
return `${options.agentName}:${options.sessionId}`;
|
|
12483
13061
|
}
|
|
12484
|
-
function
|
|
13062
|
+
function metadataString3(metadata, key) {
|
|
12485
13063
|
const value = metadata?.[key];
|
|
12486
13064
|
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
12487
13065
|
}
|
|
@@ -13909,9 +14487,9 @@ class CodexAppServerSession {
|
|
|
13909
14487
|
return;
|
|
13910
14488
|
}
|
|
13911
14489
|
const errorPayload = metadataRecord(params, "error");
|
|
13912
|
-
const message2 =
|
|
13913
|
-
const codexErrorInfo =
|
|
13914
|
-
const additionalDetails =
|
|
14490
|
+
const message2 = metadataString3(errorPayload, "message") ?? metadataString3(params, "message") ?? "Codex app-server reported an error.";
|
|
14491
|
+
const codexErrorInfo = metadataString3(errorPayload, "codexErrorInfo") ?? metadataString3(params, "codexErrorInfo");
|
|
14492
|
+
const additionalDetails = metadataString3(errorPayload, "additionalDetails") ?? metadataString3(params, "additionalDetails");
|
|
13915
14493
|
const detail = [codexErrorInfo, additionalDetails].filter((value) => Boolean(value && value.trim().length > 0)).join("; ");
|
|
13916
14494
|
const error = detail.length > 0 ? `${message2} (${detail})` : message2;
|
|
13917
14495
|
if (this.activeTurn) {
|
|
@@ -14109,8 +14687,8 @@ async function getCodexAppServerAgentSnapshot(options) {
|
|
|
14109
14687
|
readOptionalFile3(threadIdPath),
|
|
14110
14688
|
readOptionalJsonRecord(statePath)
|
|
14111
14689
|
]);
|
|
14112
|
-
const resolvedThreadId = options.threadId ?? persistedThreadId ??
|
|
14113
|
-
const persistedThreadPath =
|
|
14690
|
+
const resolvedThreadId = options.threadId ?? persistedThreadId ?? metadataString3(persistedState ?? undefined, "threadId") ?? undefined;
|
|
14691
|
+
const persistedThreadPath = metadataString3(persistedState ?? undefined, "threadPath");
|
|
14114
14692
|
if (persistedThreadPath) {
|
|
14115
14693
|
const rawRollout = await readOptionalFile3(persistedThreadPath);
|
|
14116
14694
|
if (rawRollout) {
|
|
@@ -14379,6 +14957,8 @@ __export(exports_local_agents, {
|
|
|
14379
14957
|
getLocalAgentConfig: () => getLocalAgentConfig,
|
|
14380
14958
|
ensureLocalSessionEndpointOnline: () => ensureLocalSessionEndpointOnline,
|
|
14381
14959
|
ensureLocalAgentBindingOnline: () => ensureLocalAgentBindingOnline,
|
|
14960
|
+
endpointStateAfterSuccessfulSessionWarmup: () => endpointStateAfterSuccessfulSessionWarmup,
|
|
14961
|
+
clearEndpointFailureMetadata: () => clearEndpointFailureMetadata,
|
|
14382
14962
|
classifyLocalAgentContextState: () => classifyLocalAgentContextState,
|
|
14383
14963
|
buildTmuxPasteBufferArgs: () => buildTmuxPasteBufferArgs,
|
|
14384
14964
|
buildTmuxLaunchShellCommand: () => buildTmuxLaunchShellCommand,
|
|
@@ -15525,6 +16105,13 @@ async function ensureLocalSessionEndpointOnline(endpoint) {
|
|
|
15525
16105
|
}
|
|
15526
16106
|
return {};
|
|
15527
16107
|
}
|
|
16108
|
+
function clearEndpointFailureMetadata(metadata) {
|
|
16109
|
+
const { lastError: _lastError, lastFailedAt: _lastFailedAt, ...baseMetadata } = metadata ?? {};
|
|
16110
|
+
return baseMetadata;
|
|
16111
|
+
}
|
|
16112
|
+
function endpointStateAfterSuccessfulSessionWarmup(state) {
|
|
16113
|
+
return state === "active" ? "active" : "idle";
|
|
16114
|
+
}
|
|
15528
16115
|
async function shutdownLocalSessionEndpoint(endpoint) {
|
|
15529
16116
|
if (endpoint.transport === "codex_app_server") {
|
|
15530
16117
|
await shutdownCodexAppServerAgent(buildCodexEndpointSessionOptions(endpoint));
|
|
@@ -15953,7 +16540,7 @@ function invocationSessionFreshness(invocation) {
|
|
|
15953
16540
|
case "existing":
|
|
15954
16541
|
return "continuing session";
|
|
15955
16542
|
case "any":
|
|
15956
|
-
return "
|
|
16543
|
+
return invocation.execution?.targetSessionId ? "continuing session" : "fresh session";
|
|
15957
16544
|
default:
|
|
15958
16545
|
return "session unspecified";
|
|
15959
16546
|
}
|
|
@@ -17342,6 +17929,7 @@ var init_local_agents = __esm(() => {
|
|
|
17342
17929
|
"mcp__scout__messages_inbox",
|
|
17343
17930
|
"mcp__scout__messages_channel",
|
|
17344
17931
|
"mcp__scout__broker_feed",
|
|
17932
|
+
"mcp__scout__tail_events",
|
|
17345
17933
|
"mcp__scout__agents_search",
|
|
17346
17934
|
"mcp__scout__agents_resolve",
|
|
17347
17935
|
"mcp__scout__messages_reply",
|
|
@@ -20662,17 +21250,24 @@ function queryActivity(limit = 60) {
|
|
|
20662
21250
|
ai.invocation_id,
|
|
20663
21251
|
ai.session_id,
|
|
20664
21252
|
ai.message_id,
|
|
20665
|
-
ai.record_id
|
|
21253
|
+
ai.record_id,
|
|
21254
|
+
c.kind AS conversation_kind
|
|
20666
21255
|
FROM activity_items ai
|
|
20667
21256
|
LEFT JOIN actors ac ON ac.id = ai.actor_id
|
|
20668
21257
|
LEFT JOIN actors agent_actor ON agent_actor.id = ai.agent_id
|
|
21258
|
+
LEFT JOIN conversations c ON c.id = ai.conversation_id
|
|
20669
21259
|
WHERE ai.kind != 'ask_replied'
|
|
20670
21260
|
AND ${staleFlightActivityPredicate("ai")}
|
|
20671
21261
|
ORDER BY ${activityTsExpression} DESC
|
|
20672
21262
|
LIMIT ?`).all(limit);
|
|
20673
21263
|
const items = rows.map((r) => ({
|
|
20674
21264
|
id: r.id,
|
|
20675
|
-
kind: r.kind,
|
|
21265
|
+
kind: normalizeActivityKind(r.kind, {
|
|
21266
|
+
conversationKind: r.conversation_kind,
|
|
21267
|
+
messageId: r.message_id,
|
|
21268
|
+
invocationId: r.invocation_id,
|
|
21269
|
+
flightId: r.flight_id
|
|
21270
|
+
}),
|
|
20676
21271
|
ts: r.ts,
|
|
20677
21272
|
actorName: r.actor_name,
|
|
20678
21273
|
title: r.title,
|
|
@@ -20689,6 +21284,12 @@ function queryActivity(limit = 60) {
|
|
|
20689
21284
|
}));
|
|
20690
21285
|
return items.filter((item, index) => !isDuplicateActivityFeedItem(items[index - 1] ?? null, item));
|
|
20691
21286
|
}
|
|
21287
|
+
function normalizeActivityKind(kind, input) {
|
|
21288
|
+
if (kind === "ask_opened" && input.messageId && !input.invocationId && !input.flightId && input.conversationKind !== "direct") {
|
|
21289
|
+
return "message_posted";
|
|
21290
|
+
}
|
|
21291
|
+
return kind;
|
|
21292
|
+
}
|
|
20692
21293
|
var HEARTRATE_WINDOW_MS = 7 * 24 * 60 * 60000;
|
|
20693
21294
|
var DEFAULT_HEARTRATE_BUCKETS = 56;
|
|
20694
21295
|
function smoothHeartrateCounts(counts) {
|
|
@@ -21540,6 +22141,7 @@ function queryWorkItems(opts) {
|
|
|
21540
22141
|
}
|
|
21541
22142
|
|
|
21542
22143
|
// packages/web/server/db/sessions.ts
|
|
22144
|
+
init_src();
|
|
21543
22145
|
function pickDirectConversationAgentId(participants, candidateAgentIds) {
|
|
21544
22146
|
const uniqueAgentIds = Array.from(new Set(candidateAgentIds.filter(Boolean)));
|
|
21545
22147
|
if (uniqueAgentIds.length === 0) {
|
|
@@ -21609,6 +22211,64 @@ function queryConversationDefinitionById(conversationId) {
|
|
|
21609
22211
|
participantIds: participants
|
|
21610
22212
|
};
|
|
21611
22213
|
}
|
|
22214
|
+
function parseMetadataJson(raw2) {
|
|
22215
|
+
if (!raw2)
|
|
22216
|
+
return {};
|
|
22217
|
+
try {
|
|
22218
|
+
const parsed = JSON.parse(raw2);
|
|
22219
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
22220
|
+
} catch {
|
|
22221
|
+
return {};
|
|
22222
|
+
}
|
|
22223
|
+
}
|
|
22224
|
+
function metadataString2(metadata, key) {
|
|
22225
|
+
const value = metadata[key];
|
|
22226
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
22227
|
+
}
|
|
22228
|
+
function formatChannelAlias(value) {
|
|
22229
|
+
const trimmed = value.trim();
|
|
22230
|
+
if (!trimmed)
|
|
22231
|
+
return "";
|
|
22232
|
+
return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
22233
|
+
}
|
|
22234
|
+
function conversationAliasForRow(row, metadata) {
|
|
22235
|
+
const explicitAlias = metadataString2(metadata, "alias");
|
|
22236
|
+
if (explicitAlias)
|
|
22237
|
+
return explicitAlias;
|
|
22238
|
+
const channel = metadataString2(metadata, "channel");
|
|
22239
|
+
if (channel && channel !== "system") {
|
|
22240
|
+
return formatChannelAlias(channel);
|
|
22241
|
+
}
|
|
22242
|
+
if (row.kind === "channel") {
|
|
22243
|
+
if (row.id.startsWith("channel.")) {
|
|
22244
|
+
return formatChannelAlias(row.id.slice("channel.".length));
|
|
22245
|
+
}
|
|
22246
|
+
return formatChannelAlias(row.title);
|
|
22247
|
+
}
|
|
22248
|
+
return null;
|
|
22249
|
+
}
|
|
22250
|
+
function conversationIdentityFields(row, metadata) {
|
|
22251
|
+
const alias = conversationAliasForRow(row, metadata);
|
|
22252
|
+
const naturalKey = channelNaturalKeyFromMetadata(metadata);
|
|
22253
|
+
return {
|
|
22254
|
+
...alias ? { alias } : {},
|
|
22255
|
+
...naturalKey ? { naturalKey } : {}
|
|
22256
|
+
};
|
|
22257
|
+
}
|
|
22258
|
+
function resolveConversationAlias(conversationId) {
|
|
22259
|
+
const byMetadata = db().prepare(`SELECT id, metadata_json
|
|
22260
|
+
FROM conversations
|
|
22261
|
+
ORDER BY created_at ASC`).all();
|
|
22262
|
+
const directConversation = parseDirectConversationId(conversationId);
|
|
22263
|
+
const naturalKey = directConversation ? directChannelNaturalKey([directConversation.operatorId, directConversation.agentId]) : conversationId.startsWith("channel.") ? namedChannelNaturalKey(conversationId.slice("channel.".length)) : null;
|
|
22264
|
+
for (const row of byMetadata) {
|
|
22265
|
+
const metadata = parseMetadataJson(row.metadata_json);
|
|
22266
|
+
if (naturalKey && channelNaturalKeyFromMetadata(metadata) === naturalKey) {
|
|
22267
|
+
return row.id;
|
|
22268
|
+
}
|
|
22269
|
+
}
|
|
22270
|
+
return null;
|
|
22271
|
+
}
|
|
21612
22272
|
function projectSessionConversationRows(rows, opts) {
|
|
21613
22273
|
const dedupeLocalSessionDirects = opts?.dedupeLocalSessionDirects ?? false;
|
|
21614
22274
|
const messageCreatedAtExpression = sqlTimestampMsExpression("created_at");
|
|
@@ -21634,6 +22294,7 @@ function projectSessionConversationRows(rows, opts) {
|
|
|
21634
22294
|
ORDER BY ${messageCreatedAtExpression} DESC
|
|
21635
22295
|
LIMIT 1`);
|
|
21636
22296
|
const summaries = rows.flatMap((r) => {
|
|
22297
|
+
const conversationMetadata = parseMetadataJson(r.metadata_json);
|
|
21637
22298
|
const participants = memberStmt.all(r.id).map((m) => m.actor_id);
|
|
21638
22299
|
const agentParticipants = agentMemberStmt.all(r.id);
|
|
21639
22300
|
const primaryAgentId = r.kind === "direct" ? pickDirectConversationAgentId(participants, agentParticipants.map((entry) => entry.agent_id)) : agentParticipants.length === 1 ? agentParticipants[0]?.agent_id ?? null : null;
|
|
@@ -21666,10 +22327,12 @@ function projectSessionConversationRows(rows, opts) {
|
|
|
21666
22327
|
}
|
|
21667
22328
|
}
|
|
21668
22329
|
const preview = previewStmt.get(r.id)?.body ?? null;
|
|
22330
|
+
const identityFields = conversationIdentityFields(r, conversationMetadata);
|
|
21669
22331
|
return [{
|
|
21670
22332
|
id: r.id,
|
|
21671
22333
|
kind: r.kind,
|
|
21672
22334
|
title: agentName ?? r.title,
|
|
22335
|
+
...identityFields,
|
|
21673
22336
|
participantIds: participants,
|
|
21674
22337
|
agentId,
|
|
21675
22338
|
agentName,
|
|
@@ -21739,7 +22402,7 @@ function querySessions(limit = 80) {
|
|
|
21739
22402
|
}
|
|
21740
22403
|
function querySessionById(conversationId) {
|
|
21741
22404
|
const messageCreatedAtExpression = sqlTimestampMsExpression("created_at");
|
|
21742
|
-
const
|
|
22405
|
+
const readRow = (id) => db().prepare(`SELECT
|
|
21743
22406
|
c.id,
|
|
21744
22407
|
c.kind,
|
|
21745
22408
|
c.title,
|
|
@@ -21754,11 +22417,20 @@ function querySessionById(conversationId) {
|
|
|
21754
22417
|
GROUP BY conversation_id
|
|
21755
22418
|
) ms ON ms.conversation_id = c.id
|
|
21756
22419
|
WHERE c.id = ?
|
|
21757
|
-
LIMIT 1`).get(
|
|
22420
|
+
LIMIT 1`).get(id, id);
|
|
22421
|
+
const row = readRow(conversationId);
|
|
21758
22422
|
const existing = row ? projectSessionConversationRows([row], { dedupeLocalSessionDirects: false, limit: 1 })[0] ?? null : null;
|
|
21759
22423
|
if (existing) {
|
|
21760
22424
|
return existing;
|
|
21761
22425
|
}
|
|
22426
|
+
const resolvedConversationId = resolveConversationAlias(conversationId);
|
|
22427
|
+
if (resolvedConversationId) {
|
|
22428
|
+
const resolvedRow = readRow(resolvedConversationId);
|
|
22429
|
+
const resolved = resolvedRow ? projectSessionConversationRows([resolvedRow], { dedupeLocalSessionDirects: false, limit: 1 })[0] ?? null : null;
|
|
22430
|
+
if (resolved) {
|
|
22431
|
+
return resolved;
|
|
22432
|
+
}
|
|
22433
|
+
}
|
|
21762
22434
|
const directConversation = parseDirectConversationId(conversationId);
|
|
21763
22435
|
if (!directConversation) {
|
|
21764
22436
|
return null;
|
|
@@ -22214,7 +22886,7 @@ init_user_config();
|
|
|
22214
22886
|
function projectFleetActivity(row) {
|
|
22215
22887
|
return {
|
|
22216
22888
|
id: row.id,
|
|
22217
|
-
kind: row
|
|
22889
|
+
kind: normalizeFleetActivityKind(row),
|
|
22218
22890
|
ts: row.ts,
|
|
22219
22891
|
actorName: row.actor_name,
|
|
22220
22892
|
title: row.title,
|
|
@@ -22231,6 +22903,12 @@ function projectFleetActivity(row) {
|
|
|
22231
22903
|
sessionId: row.session_id
|
|
22232
22904
|
};
|
|
22233
22905
|
}
|
|
22906
|
+
function normalizeFleetActivityKind(row) {
|
|
22907
|
+
if (row.kind === "ask_opened" && row.message_id && !row.invocation_id && !row.flight_id && row.conversation_kind !== "direct") {
|
|
22908
|
+
return "message_posted";
|
|
22909
|
+
}
|
|
22910
|
+
return row.kind;
|
|
22911
|
+
}
|
|
22234
22912
|
function queryFleetActivity(opts) {
|
|
22235
22913
|
const filters = [];
|
|
22236
22914
|
const params = [];
|
|
@@ -22279,10 +22957,12 @@ function queryFleetActivity(opts) {
|
|
|
22279
22957
|
ai.invocation_id,
|
|
22280
22958
|
ai.flight_id,
|
|
22281
22959
|
ai.record_id,
|
|
22282
|
-
ai.session_id
|
|
22960
|
+
ai.session_id,
|
|
22961
|
+
c.kind AS conversation_kind
|
|
22283
22962
|
FROM activity_items ai
|
|
22284
22963
|
LEFT JOIN actors ac ON ac.id = ai.actor_id
|
|
22285
22964
|
LEFT JOIN actors agent_actor ON agent_actor.id = ai.agent_id
|
|
22965
|
+
LEFT JOIN conversations c ON c.id = ai.conversation_id
|
|
22286
22966
|
${sqlWhereClause([
|
|
22287
22967
|
staleFlightActivityPredicate("ai"),
|
|
22288
22968
|
...andClauses,
|
|
@@ -22554,6 +23234,7 @@ init_broker_process_manager();
|
|
|
22554
23234
|
init_local_agents();
|
|
22555
23235
|
init_support_paths();
|
|
22556
23236
|
init_user_config();
|
|
23237
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
22557
23238
|
import { mkdir as mkdir7, readFile as readFile7, unlink, writeFile as writeFile7 } from "fs/promises";
|
|
22558
23239
|
import { basename as basename9, join as join22, resolve as resolve8 } from "path";
|
|
22559
23240
|
|
|
@@ -22565,6 +23246,8 @@ var scoutBrokerPaths = {
|
|
|
22565
23246
|
node: "/v1/node",
|
|
22566
23247
|
snapshot: "/v1/snapshot",
|
|
22567
23248
|
topologySnapshot: "/v1/topology/snapshot",
|
|
23249
|
+
tailDiscover: "/v1/tail/discover",
|
|
23250
|
+
tailRecent: "/v1/tail/recent",
|
|
22568
23251
|
messages: "/v1/messages",
|
|
22569
23252
|
eventsStream: "/v1/events/stream",
|
|
22570
23253
|
actors: "/v1/actors",
|
|
@@ -22583,9 +23266,6 @@ var scoutBrokerPaths = {
|
|
|
22583
23266
|
};
|
|
22584
23267
|
|
|
22585
23268
|
// packages/web/server/core/broker/service.ts
|
|
22586
|
-
var BROKER_SHARED_CHANNEL_ID = "channel.shared";
|
|
22587
|
-
var BROKER_VOICE_CHANNEL_ID = "channel.voice";
|
|
22588
|
-
var BROKER_SYSTEM_CHANNEL_ID = "channel.system";
|
|
22589
23269
|
var OPERATOR_ID = "operator";
|
|
22590
23270
|
var DEFAULT_BROKER_HOST2 = "127.0.0.1";
|
|
22591
23271
|
var DEFAULT_BROKER_PORT2 = 65535;
|
|
@@ -22619,7 +23299,7 @@ function titleCaseName(value) {
|
|
|
22619
23299
|
return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
22620
23300
|
}
|
|
22621
23301
|
function displayNameForBrokerActor(snapshot, actorId) {
|
|
22622
|
-
return snapshot.agents[actorId]?.displayName ?? snapshot.actors[actorId]?.displayName ?? titleCaseName(
|
|
23302
|
+
return snapshot.agents[actorId]?.displayName ?? snapshot.actors[actorId]?.displayName ?? titleCaseName(metadataString4(snapshot.agents[actorId]?.metadata, "definitionId") || actorId);
|
|
22623
23303
|
}
|
|
22624
23304
|
function firstEndpointForActor(snapshot, actorId) {
|
|
22625
23305
|
return Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === actorId).sort((lhs, rhs) => lhs.id.localeCompare(rhs.id))[0];
|
|
@@ -22628,9 +23308,9 @@ function buildScoutReturnAddress2(snapshot, actorId, options = {}) {
|
|
|
22628
23308
|
const agent = snapshot.agents[actorId];
|
|
22629
23309
|
const actor = snapshot.actors[actorId];
|
|
22630
23310
|
const endpoint = firstEndpointForActor(snapshot, actorId);
|
|
22631
|
-
const selector = agent?.selector?.trim() ||
|
|
22632
|
-
const defaultSelector = agent?.defaultSelector?.trim() ||
|
|
22633
|
-
const projectRoot = endpoint?.projectRoot ?? endpoint?.cwd ??
|
|
23311
|
+
const selector = agent?.selector?.trim() || metadataString4(agent?.metadata, "selector") || metadataString4(actor?.metadata, "selector");
|
|
23312
|
+
const defaultSelector = agent?.defaultSelector?.trim() || metadataString4(agent?.metadata, "defaultSelector") || metadataString4(actor?.metadata, "defaultSelector");
|
|
23313
|
+
const projectRoot = endpoint?.projectRoot ?? endpoint?.cwd ?? metadataString4(agent?.metadata, "projectRoot") ?? metadataString4(actor?.metadata, "projectRoot");
|
|
22634
23314
|
return buildScoutReturnAddress({
|
|
22635
23315
|
actorId,
|
|
22636
23316
|
handle: agent?.handle?.trim() || actor?.handle?.trim() || actorId,
|
|
@@ -22644,10 +23324,7 @@ function buildScoutReturnAddress2(snapshot, actorId, options = {}) {
|
|
|
22644
23324
|
sessionId: endpoint?.sessionId
|
|
22645
23325
|
});
|
|
22646
23326
|
}
|
|
22647
|
-
function
|
|
22648
|
-
return value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-") || "shared";
|
|
22649
|
-
}
|
|
22650
|
-
function metadataString3(metadata, key) {
|
|
23327
|
+
function metadataString4(metadata, key) {
|
|
22651
23328
|
const value = metadata?.[key];
|
|
22652
23329
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
22653
23330
|
}
|
|
@@ -22692,6 +23369,24 @@ function renderScoutTargetLabel(targetLabel) {
|
|
|
22692
23369
|
}
|
|
22693
23370
|
return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
|
|
22694
23371
|
}
|
|
23372
|
+
function renderedScoutAskTarget(target) {
|
|
23373
|
+
switch (target.kind) {
|
|
23374
|
+
case "agent_id":
|
|
23375
|
+
return target.agentId.trim();
|
|
23376
|
+
case "agent_label":
|
|
23377
|
+
return target.label.trim();
|
|
23378
|
+
case "session_id":
|
|
23379
|
+
return `session:${target.sessionId.trim()}`;
|
|
23380
|
+
case "binding_ref":
|
|
23381
|
+
return `ref:${target.ref.trim()}`;
|
|
23382
|
+
case "project_path":
|
|
23383
|
+
return target.projectPath.trim();
|
|
23384
|
+
case "channel":
|
|
23385
|
+
return `channel:${target.channel.trim()}`;
|
|
23386
|
+
case "broadcast":
|
|
23387
|
+
return "broadcast";
|
|
23388
|
+
}
|
|
23389
|
+
}
|
|
22695
23390
|
function scoutTargetDiagnosticFromDeliveryFailure(delivery) {
|
|
22696
23391
|
const dispatch = delivery.kind === "question" ? delivery.question : delivery.rejection;
|
|
22697
23392
|
if (dispatch.kind === "ambiguous") {
|
|
@@ -22827,18 +23522,18 @@ async function requireScoutBrokerContext(baseUrl = resolveScoutBrokerUrl()) {
|
|
|
22827
23522
|
function buildMentionCandidate(snapshot, agent) {
|
|
22828
23523
|
const endpoints = Object.values(snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === agent.id);
|
|
22829
23524
|
const preferred = endpoints.find((endpoint) => endpoint.state === "active") ?? endpoints.find((endpoint) => endpoint.state === "idle" || endpoint.state === "waiting") ?? endpoints[0];
|
|
22830
|
-
const harness = preferred?.harness ??
|
|
22831
|
-
const profile =
|
|
23525
|
+
const harness = preferred?.harness ?? metadataString4(agent.metadata, "harness") ?? metadataString4(agent.metadata, "defaultHarness");
|
|
23526
|
+
const profile = metadataString4(agent.metadata, "profile");
|
|
22832
23527
|
return {
|
|
22833
23528
|
agentId: agent.id,
|
|
22834
|
-
definitionId:
|
|
22835
|
-
nodeQualifier:
|
|
22836
|
-
workspaceQualifier:
|
|
23529
|
+
definitionId: metadataString4(agent.metadata, "definitionId") || agent.id,
|
|
23530
|
+
nodeQualifier: metadataString4(agent.metadata, "nodeQualifier"),
|
|
23531
|
+
workspaceQualifier: metadataString4(agent.metadata, "workspaceQualifier"),
|
|
22837
23532
|
...harness ? { harness } : {},
|
|
22838
23533
|
...profile ? { profile } : {},
|
|
22839
23534
|
aliases: [
|
|
22840
|
-
|
|
22841
|
-
|
|
23535
|
+
metadataString4(agent.metadata, "selector"),
|
|
23536
|
+
metadataString4(agent.metadata, "defaultSelector")
|
|
22842
23537
|
].filter(Boolean)
|
|
22843
23538
|
};
|
|
22844
23539
|
}
|
|
@@ -23054,50 +23749,74 @@ function conversationDefinition(snapshot, nodeId, channel, senderId, targetParti
|
|
|
23054
23749
|
const sharedParticipants = [...new Set([OPERATOR_ID, senderId, ...Object.keys(snapshot.agents)])].sort();
|
|
23055
23750
|
const scopedParticipants = [...new Set([OPERATOR_ID, senderId, ...targetParticipantIds])].sort();
|
|
23056
23751
|
if (normalizedChannel === "voice") {
|
|
23752
|
+
const naturalKey2 = namedChannelNaturalKey("voice");
|
|
23753
|
+
const existing2 = findConversationByIdentity(snapshot, naturalKey2);
|
|
23057
23754
|
return {
|
|
23058
|
-
id:
|
|
23755
|
+
id: existing2?.id ?? mintChannelId(randomUUID3),
|
|
23059
23756
|
kind: "channel",
|
|
23060
23757
|
title: "voice",
|
|
23061
23758
|
visibility: "workspace",
|
|
23062
23759
|
shareMode: resolveConversationShareMode(snapshot, nodeId, scopedParticipants, "local"),
|
|
23063
23760
|
authorityNodeId: nodeId,
|
|
23064
23761
|
participantIds: scopedParticipants,
|
|
23065
|
-
metadata: {
|
|
23762
|
+
metadata: {
|
|
23763
|
+
surface: "scout-cli",
|
|
23764
|
+
channel: "voice",
|
|
23765
|
+
naturalKey: naturalKey2
|
|
23766
|
+
}
|
|
23066
23767
|
};
|
|
23067
23768
|
}
|
|
23068
23769
|
if (normalizedChannel === "system") {
|
|
23770
|
+
const naturalKey2 = systemChannelNaturalKey("system");
|
|
23771
|
+
const existing2 = findConversationByIdentity(snapshot, naturalKey2);
|
|
23069
23772
|
return {
|
|
23070
|
-
id:
|
|
23773
|
+
id: existing2?.id ?? mintChannelId(randomUUID3),
|
|
23071
23774
|
kind: "system",
|
|
23072
23775
|
title: "system",
|
|
23073
23776
|
visibility: "system",
|
|
23074
23777
|
shareMode: "local",
|
|
23075
23778
|
authorityNodeId: nodeId,
|
|
23076
23779
|
participantIds: [OPERATOR_ID, senderId].sort(),
|
|
23077
|
-
metadata: {
|
|
23780
|
+
metadata: {
|
|
23781
|
+
surface: "scout-cli",
|
|
23782
|
+
channel: "system",
|
|
23783
|
+
naturalKey: naturalKey2
|
|
23784
|
+
}
|
|
23078
23785
|
};
|
|
23079
23786
|
}
|
|
23080
23787
|
if (normalizedChannel === "shared") {
|
|
23788
|
+
const naturalKey2 = namedChannelNaturalKey("shared");
|
|
23789
|
+
const existing2 = findConversationByIdentity(snapshot, naturalKey2);
|
|
23081
23790
|
return {
|
|
23082
|
-
id:
|
|
23791
|
+
id: existing2?.id ?? mintChannelId(randomUUID3),
|
|
23083
23792
|
kind: "channel",
|
|
23084
23793
|
title: "shared-channel",
|
|
23085
23794
|
visibility: "workspace",
|
|
23086
23795
|
shareMode: "shared",
|
|
23087
23796
|
authorityNodeId: nodeId,
|
|
23088
23797
|
participantIds: sharedParticipants,
|
|
23089
|
-
metadata: {
|
|
23798
|
+
metadata: {
|
|
23799
|
+
surface: "scout-cli",
|
|
23800
|
+
channel: "shared",
|
|
23801
|
+
naturalKey: naturalKey2
|
|
23802
|
+
}
|
|
23090
23803
|
};
|
|
23091
23804
|
}
|
|
23805
|
+
const naturalKey = namedChannelNaturalKey(normalizedChannel);
|
|
23806
|
+
const existing = findConversationByIdentity(snapshot, naturalKey);
|
|
23092
23807
|
return {
|
|
23093
|
-
id:
|
|
23808
|
+
id: existing?.id ?? mintChannelId(randomUUID3),
|
|
23094
23809
|
kind: "channel",
|
|
23095
23810
|
title: normalizedChannel,
|
|
23096
23811
|
visibility: "workspace",
|
|
23097
23812
|
shareMode: resolveConversationShareMode(snapshot, nodeId, scopedParticipants, "local"),
|
|
23098
23813
|
authorityNodeId: nodeId,
|
|
23099
23814
|
participantIds: scopedParticipants,
|
|
23100
|
-
metadata: {
|
|
23815
|
+
metadata: {
|
|
23816
|
+
surface: "scout-cli",
|
|
23817
|
+
channel: normalizedChannel,
|
|
23818
|
+
naturalKey
|
|
23819
|
+
}
|
|
23101
23820
|
};
|
|
23102
23821
|
}
|
|
23103
23822
|
async function ensureBrokerConversation(baseUrl, snapshot, nodeId, channel, senderId, targetParticipantIds = []) {
|
|
@@ -23115,21 +23834,15 @@ async function ensureBrokerConversation(baseUrl, snapshot, nodeId, channel, send
|
|
|
23115
23834
|
}
|
|
23116
23835
|
return existing;
|
|
23117
23836
|
}
|
|
23118
|
-
function
|
|
23119
|
-
|
|
23120
|
-
return `dm.${sourceId}.${targetId}`;
|
|
23121
|
-
}
|
|
23122
|
-
if (sourceId === OPERATOR_ID || targetId === OPERATOR_ID) {
|
|
23123
|
-
const peerId = sourceId === OPERATOR_ID ? targetId : sourceId;
|
|
23124
|
-
return `dm.${OPERATOR_ID}.${peerId}`;
|
|
23125
|
-
}
|
|
23126
|
-
return `dm.${[sourceId, targetId].sort().join(".")}`;
|
|
23837
|
+
function findConversationByIdentity(snapshot, naturalKey) {
|
|
23838
|
+
return Object.values(snapshot.conversations).find((conversation) => channelNaturalKeyFromMetadata(conversation.metadata) === naturalKey);
|
|
23127
23839
|
}
|
|
23128
23840
|
async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId, sourceId, targetId) {
|
|
23129
|
-
const conversationId = targetId === SCOUT_AGENT_ID && sourceId === OPERATOR_ID ? BROKER_SHARED_CHANNEL_ID : directConversationIdForActors(sourceId, targetId);
|
|
23130
23841
|
const participantIds = [...new Set([sourceId, targetId])].sort();
|
|
23842
|
+
const naturalKey = directChannelNaturalKey(participantIds);
|
|
23843
|
+
const existing = findConversationByIdentity(snapshot, naturalKey);
|
|
23844
|
+
const conversationId = existing?.id ?? mintChannelId(randomUUID3);
|
|
23131
23845
|
const nextShareMode = resolveConversationShareMode(snapshot, nodeId, participantIds, "local");
|
|
23132
|
-
const existing = snapshot.conversations[conversationId];
|
|
23133
23846
|
const alreadyMatches = existing && existing.kind === "direct" && existing.shareMode === nextShareMode && existing.visibility === "private" && existing.participantIds.join("\x00") === participantIds.join("\x00");
|
|
23134
23847
|
if (alreadyMatches) {
|
|
23135
23848
|
const preferredTargetId = targetId === OPERATOR_ID ? sourceId : targetId;
|
|
@@ -23151,6 +23864,7 @@ async function ensureBrokerDirectConversationBetween(baseUrl, snapshot, nodeId,
|
|
|
23151
23864
|
participantIds,
|
|
23152
23865
|
metadata: {
|
|
23153
23866
|
surface: "scout",
|
|
23867
|
+
naturalKey,
|
|
23154
23868
|
...targetId === SCOUT_AGENT_ID && sourceId === OPERATOR_ID ? { role: "partner" } : {}
|
|
23155
23869
|
}
|
|
23156
23870
|
};
|
|
@@ -23411,23 +24125,30 @@ async function sendScoutDirectMessage(input) {
|
|
|
23411
24125
|
};
|
|
23412
24126
|
}
|
|
23413
24127
|
async function askScoutQuestion(input) {
|
|
24128
|
+
const renderedTarget = input.targetLabel?.trim() || (input.target ? renderedScoutAskTarget(input.target) : "") || input.targetAgentId?.trim() || "";
|
|
23414
24129
|
const broker = await loadScoutBrokerContext();
|
|
23415
24130
|
if (!broker) {
|
|
23416
|
-
return { usedBroker: false, unresolvedTarget:
|
|
24131
|
+
return { usedBroker: false, unresolvedTarget: renderedTarget };
|
|
23417
24132
|
}
|
|
23418
24133
|
const currentDirectory = input.currentDirectory ?? process.cwd();
|
|
23419
24134
|
const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
|
|
23420
|
-
|
|
23421
|
-
|
|
24135
|
+
if (!renderedTarget) {
|
|
24136
|
+
return { usedBroker: true, unresolvedTarget: renderedTarget };
|
|
24137
|
+
}
|
|
24138
|
+
const normalizedTargetLabel = input.target?.kind === "project_path" ? "" : renderScoutTargetLabel(renderedTarget);
|
|
24139
|
+
const explicitTargetAgentId = input.targetAgentId?.trim() || (input.target ? undefined : broker.snapshot.agents[renderedTarget]?.id);
|
|
23422
24140
|
if (explicitTargetAgentId) {
|
|
23423
24141
|
await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, explicitTargetAgentId, currentDirectory);
|
|
23424
24142
|
}
|
|
23425
|
-
const messageBody = input.body.trim().startsWith(normalizedTargetLabel) ? input.body.trim() : `${normalizedTargetLabel} ${input.body.trim()}
|
|
24143
|
+
const messageBody = normalizedTargetLabel && input.body.trim().startsWith(normalizedTargetLabel) ? input.body.trim() : normalizedTargetLabel ? `${normalizedTargetLabel} ${input.body.trim()}` : input.body.trim();
|
|
24144
|
+
const createdAt = input.createdAtMs ?? Date.now();
|
|
24145
|
+
const source = input.source?.trim() || "scout-cli";
|
|
23426
24146
|
const delivery = await brokerPostDeliver(broker.baseUrl, {
|
|
23427
|
-
id: `deliver-${
|
|
24147
|
+
id: `deliver-${createdAt.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
23428
24148
|
requesterId: senderId,
|
|
23429
24149
|
requesterNodeId: broker.node.id,
|
|
23430
|
-
|
|
24150
|
+
...input.target ? { target: input.target } : {},
|
|
24151
|
+
targetLabel: renderedTarget,
|
|
23431
24152
|
targetAgentId: explicitTargetAgentId,
|
|
23432
24153
|
body: messageBody,
|
|
23433
24154
|
intent: "consult",
|
|
@@ -23435,21 +24156,23 @@ async function askScoutQuestion(input) {
|
|
|
23435
24156
|
speechText: input.shouldSpeak ? stripScoutAgentSelectorLabels(messageBody) : undefined,
|
|
23436
24157
|
execution: {
|
|
23437
24158
|
...input.executionHarness ? { harness: input.executionHarness } : {},
|
|
23438
|
-
|
|
24159
|
+
...input.executionModel?.trim() ? { model: input.executionModel.trim() } : {},
|
|
24160
|
+
session: input.executionSession ?? "new"
|
|
23439
24161
|
},
|
|
24162
|
+
...input.projectAgent ? { projectAgent: input.projectAgent } : {},
|
|
23440
24163
|
ensureAwake: true,
|
|
23441
|
-
createdAt
|
|
24164
|
+
createdAt,
|
|
23442
24165
|
messageMetadata: {
|
|
23443
|
-
source
|
|
24166
|
+
source
|
|
23444
24167
|
},
|
|
23445
24168
|
invocationMetadata: {
|
|
23446
|
-
source
|
|
24169
|
+
source
|
|
23447
24170
|
}
|
|
23448
24171
|
});
|
|
23449
24172
|
if (delivery.kind !== "delivery") {
|
|
23450
24173
|
return {
|
|
23451
24174
|
usedBroker: true,
|
|
23452
|
-
unresolvedTarget:
|
|
24175
|
+
unresolvedTarget: renderedTarget,
|
|
23453
24176
|
targetDiagnostic: scoutTargetDiagnosticFromDeliveryFailure(delivery)
|
|
23454
24177
|
};
|
|
23455
24178
|
}
|
|
@@ -23470,6 +24193,7 @@ async function loadScoutRelayConfig() {
|
|
|
23470
24193
|
}
|
|
23471
24194
|
|
|
23472
24195
|
// packages/web/server/core/conversations/service.ts
|
|
24196
|
+
init_src();
|
|
23473
24197
|
var DEFAULT_CONVERSATION_KINDS = [
|
|
23474
24198
|
"direct",
|
|
23475
24199
|
"channel",
|
|
@@ -23511,10 +24235,38 @@ function endpointActivity(endpoint) {
|
|
|
23511
24235
|
function endpointStartedAt(endpoint) {
|
|
23512
24236
|
return Math.max(normalizeMetadataTimestamp(endpoint.metadata?.lastStartedAt), normalizeMetadataTimestamp(endpoint.metadata?.startedAt));
|
|
23513
24237
|
}
|
|
23514
|
-
function
|
|
24238
|
+
function metadataString5(metadata, key) {
|
|
23515
24239
|
const value = metadata?.[key];
|
|
23516
24240
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
23517
24241
|
}
|
|
24242
|
+
function formatChannelAlias2(value) {
|
|
24243
|
+
const trimmed = value.trim();
|
|
24244
|
+
if (!trimmed)
|
|
24245
|
+
return "";
|
|
24246
|
+
return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
24247
|
+
}
|
|
24248
|
+
function conversationAlias(input) {
|
|
24249
|
+
const explicitAlias = metadataString5(input.metadata, "alias");
|
|
24250
|
+
if (explicitAlias)
|
|
24251
|
+
return explicitAlias;
|
|
24252
|
+
const channel = metadataString5(input.metadata, "channel");
|
|
24253
|
+
if (channel && channel !== "system") {
|
|
24254
|
+
return formatChannelAlias2(channel);
|
|
24255
|
+
}
|
|
24256
|
+
if (input.kind === "channel") {
|
|
24257
|
+
if (input.id.startsWith("channel.")) {
|
|
24258
|
+
return formatChannelAlias2(input.id.slice("channel.".length));
|
|
24259
|
+
}
|
|
24260
|
+
return formatChannelAlias2(input.title);
|
|
24261
|
+
}
|
|
24262
|
+
return null;
|
|
24263
|
+
}
|
|
24264
|
+
function conversationIdentityFields2(input) {
|
|
24265
|
+
return {
|
|
24266
|
+
alias: conversationAlias(input),
|
|
24267
|
+
naturalKey: channelNaturalKeyFromMetadata(input.metadata)
|
|
24268
|
+
};
|
|
24269
|
+
}
|
|
23518
24270
|
function metadataBoolean2(metadata, key) {
|
|
23519
24271
|
return metadata?.[key] === true;
|
|
23520
24272
|
}
|
|
@@ -23599,17 +24351,19 @@ async function getScoutConversations(filters = {}) {
|
|
|
23599
24351
|
return [];
|
|
23600
24352
|
}
|
|
23601
24353
|
const title = agentDisplayName(snapshot, agentId);
|
|
24354
|
+
const identityFields2 = conversationIdentityFields2(conversation);
|
|
23602
24355
|
return [{
|
|
23603
24356
|
id: conversation.id,
|
|
23604
24357
|
kind: conversation.kind,
|
|
23605
24358
|
title,
|
|
24359
|
+
...identityFields2,
|
|
23606
24360
|
participantIds: [...conversation.participantIds],
|
|
23607
24361
|
authorityNodeId: conversation.authorityNodeId ?? null,
|
|
23608
24362
|
authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
|
|
23609
24363
|
agentId,
|
|
23610
24364
|
agentName: title,
|
|
23611
24365
|
harness: endpoint?.harness ?? null,
|
|
23612
|
-
currentBranch:
|
|
24366
|
+
currentBranch: metadataString5(endpoint?.metadata, "branch") ?? metadataString5(endpoint?.metadata, "workspaceQualifier") ?? metadataString5(agent.metadata, "branch") ?? metadataString5(agent.metadata, "workspaceQualifier"),
|
|
23613
24367
|
preview: latestMessage?.body ?? null,
|
|
23614
24368
|
messageCount,
|
|
23615
24369
|
lastMessageAt: normalizeTimestampMs2(latestMessage?.createdAt),
|
|
@@ -23628,10 +24382,12 @@ async function getScoutConversations(filters = {}) {
|
|
|
23628
24382
|
} else if (conversation.kind === "system") {
|
|
23629
24383
|
return [];
|
|
23630
24384
|
}
|
|
24385
|
+
const identityFields = conversationIdentityFields2(conversation);
|
|
23631
24386
|
return [{
|
|
23632
24387
|
id: conversation.id,
|
|
23633
24388
|
kind: conversation.kind,
|
|
23634
24389
|
title: conversation.title,
|
|
24390
|
+
...identityFields,
|
|
23635
24391
|
participantIds: [...conversation.participantIds],
|
|
23636
24392
|
authorityNodeId: conversation.authorityNodeId ?? null,
|
|
23637
24393
|
authorityNodeName: snapshot.nodes?.[conversation.authorityNodeId]?.name ?? null,
|
|
@@ -25109,7 +25865,7 @@ function metadataRecord3(metadata, key) {
|
|
|
25109
25865
|
}
|
|
25110
25866
|
return value;
|
|
25111
25867
|
}
|
|
25112
|
-
function
|
|
25868
|
+
function metadataString6(metadata, key) {
|
|
25113
25869
|
const value = metadata?.[key];
|
|
25114
25870
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
25115
25871
|
}
|
|
@@ -25133,50 +25889,50 @@ function buildObserveMetadata(snapshot, sessionStart) {
|
|
|
25133
25889
|
}
|
|
25134
25890
|
if (snapshot.turns.length > 0)
|
|
25135
25891
|
sessionMeta.turnCount = snapshot.turns.length;
|
|
25136
|
-
if (
|
|
25137
|
-
sessionMeta.externalSessionId =
|
|
25892
|
+
if (metadataString6(providerMeta, "externalSessionId")) {
|
|
25893
|
+
sessionMeta.externalSessionId = metadataString6(providerMeta, "externalSessionId");
|
|
25138
25894
|
}
|
|
25139
|
-
if (
|
|
25140
|
-
sessionMeta.threadId =
|
|
25895
|
+
if (metadataString6(providerMeta, "threadId")) {
|
|
25896
|
+
sessionMeta.threadId = metadataString6(providerMeta, "threadId");
|
|
25141
25897
|
}
|
|
25142
|
-
if (
|
|
25143
|
-
sessionMeta.threadPath =
|
|
25898
|
+
if (metadataString6(providerMeta, "threadPath")) {
|
|
25899
|
+
sessionMeta.threadPath = metadataString6(providerMeta, "threadPath");
|
|
25144
25900
|
}
|
|
25145
|
-
if (
|
|
25146
|
-
sessionMeta.gitBranch =
|
|
25901
|
+
if (metadataString6(observeRuntime, "gitBranch")) {
|
|
25902
|
+
sessionMeta.gitBranch = metadataString6(observeRuntime, "gitBranch");
|
|
25147
25903
|
}
|
|
25148
|
-
if (
|
|
25149
|
-
sessionMeta.cliVersion =
|
|
25904
|
+
if (metadataString6(observeRuntime, "cliVersion")) {
|
|
25905
|
+
sessionMeta.cliVersion = metadataString6(observeRuntime, "cliVersion");
|
|
25150
25906
|
}
|
|
25151
|
-
if (
|
|
25152
|
-
sessionMeta.entrypoint =
|
|
25907
|
+
if (metadataString6(observeRuntime, "entrypoint")) {
|
|
25908
|
+
sessionMeta.entrypoint = metadataString6(observeRuntime, "entrypoint");
|
|
25153
25909
|
}
|
|
25154
|
-
if (
|
|
25155
|
-
sessionMeta.originator =
|
|
25910
|
+
if (metadataString6(observeRuntime, "originator")) {
|
|
25911
|
+
sessionMeta.originator = metadataString6(observeRuntime, "originator");
|
|
25156
25912
|
}
|
|
25157
|
-
if (
|
|
25158
|
-
sessionMeta.source =
|
|
25913
|
+
if (metadataString6(observeRuntime, "source")) {
|
|
25914
|
+
sessionMeta.source = metadataString6(observeRuntime, "source");
|
|
25159
25915
|
}
|
|
25160
|
-
if (
|
|
25161
|
-
sessionMeta.permissionMode =
|
|
25916
|
+
if (metadataString6(observeRuntime, "permissionMode")) {
|
|
25917
|
+
sessionMeta.permissionMode = metadataString6(observeRuntime, "permissionMode");
|
|
25162
25918
|
}
|
|
25163
|
-
if (
|
|
25164
|
-
sessionMeta.approvalPolicy =
|
|
25919
|
+
if (metadataString6(observeRuntime, "approvalPolicy")) {
|
|
25920
|
+
sessionMeta.approvalPolicy = metadataString6(observeRuntime, "approvalPolicy");
|
|
25165
25921
|
}
|
|
25166
|
-
if (
|
|
25167
|
-
sessionMeta.sandbox =
|
|
25922
|
+
if (metadataString6(observeRuntime, "sandbox")) {
|
|
25923
|
+
sessionMeta.sandbox = metadataString6(observeRuntime, "sandbox");
|
|
25168
25924
|
}
|
|
25169
|
-
if (
|
|
25170
|
-
sessionMeta.userType =
|
|
25925
|
+
if (metadataString6(observeRuntime, "userType")) {
|
|
25926
|
+
sessionMeta.userType = metadataString6(observeRuntime, "userType");
|
|
25171
25927
|
}
|
|
25172
|
-
if (
|
|
25173
|
-
sessionMeta.effort =
|
|
25928
|
+
if (metadataString6(observeRuntime, "effort")) {
|
|
25929
|
+
sessionMeta.effort = metadataString6(observeRuntime, "effort");
|
|
25174
25930
|
}
|
|
25175
|
-
if (
|
|
25176
|
-
sessionMeta.modelProvider =
|
|
25931
|
+
if (metadataString6(observeRuntime, "modelProvider")) {
|
|
25932
|
+
sessionMeta.modelProvider = metadataString6(observeRuntime, "modelProvider");
|
|
25177
25933
|
}
|
|
25178
|
-
if (
|
|
25179
|
-
sessionMeta.timezone =
|
|
25934
|
+
if (metadataString6(observeRuntime, "timezone")) {
|
|
25935
|
+
sessionMeta.timezone = metadataString6(observeRuntime, "timezone");
|
|
25180
25936
|
}
|
|
25181
25937
|
const usageMeta = {};
|
|
25182
25938
|
if (metadataNumber(observeUsage, "assistantMessages") !== undefined) {
|
|
@@ -25209,14 +25965,14 @@ function buildObserveMetadata(snapshot, sessionStart) {
|
|
|
25209
25965
|
if (metadataNumber(observeUsage, "webFetchRequests") !== undefined) {
|
|
25210
25966
|
usageMeta.webFetchRequests = metadataNumber(observeUsage, "webFetchRequests");
|
|
25211
25967
|
}
|
|
25212
|
-
if (
|
|
25213
|
-
usageMeta.serviceTier =
|
|
25968
|
+
if (metadataString6(observeUsage, "serviceTier")) {
|
|
25969
|
+
usageMeta.serviceTier = metadataString6(observeUsage, "serviceTier");
|
|
25214
25970
|
}
|
|
25215
|
-
if (
|
|
25216
|
-
usageMeta.speed =
|
|
25971
|
+
if (metadataString6(observeUsage, "speed")) {
|
|
25972
|
+
usageMeta.speed = metadataString6(observeUsage, "speed");
|
|
25217
25973
|
}
|
|
25218
|
-
if (
|
|
25219
|
-
usageMeta.planType =
|
|
25974
|
+
if (metadataString6(observeUsage, "planType")) {
|
|
25975
|
+
usageMeta.planType = metadataString6(observeUsage, "planType");
|
|
25220
25976
|
}
|
|
25221
25977
|
const observedTopology = metadataRecord3(providerMeta, OBSERVED_HARNESS_TOPOLOGY_META_KEY);
|
|
25222
25978
|
const hasSessionMeta = Object.keys(sessionMeta).length > 0;
|
|
@@ -25293,6 +26049,9 @@ function readHistorySnapshot(candidate) {
|
|
|
25293
26049
|
adapterType: candidate.adapterType,
|
|
25294
26050
|
baseTimestampMs: stat6.mtimeMs
|
|
25295
26051
|
});
|
|
26052
|
+
if (replay.events.length === 0) {
|
|
26053
|
+
return null;
|
|
26054
|
+
}
|
|
25296
26055
|
const nextEntry = {
|
|
25297
26056
|
historyPath: candidate.path,
|
|
25298
26057
|
snapshot: replay.snapshot,
|
|
@@ -30642,6 +31401,9 @@ init_src();
|
|
|
30642
31401
|
// packages/runtime/src/drizzle-migrate.ts
|
|
30643
31402
|
init_support_paths();
|
|
30644
31403
|
if (false) {}
|
|
31404
|
+
|
|
31405
|
+
// packages/runtime/src/conversations/api.ts
|
|
31406
|
+
init_src();
|
|
30645
31407
|
// packages/runtime/src/tailscale.ts
|
|
30646
31408
|
import { readFile as readFile8 } from "fs/promises";
|
|
30647
31409
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -31316,8 +32078,8 @@ function candidateFromValue(key, value, root, now) {
|
|
|
31316
32078
|
title: firstMetadataString(root, "title", "attentionTitle"),
|
|
31317
32079
|
summary: firstMetadataString(root, "summary", "attentionSummary", "blockedReason", "needsInputReason", "reason", "message"),
|
|
31318
32080
|
detail: firstMetadataString(root, "detail", "statusDetail"),
|
|
31319
|
-
turnId:
|
|
31320
|
-
blockId:
|
|
32081
|
+
turnId: metadataString7(root, "turnId"),
|
|
32082
|
+
blockId: metadataString7(root, "blockId"),
|
|
31321
32083
|
version: metadataNumber2(root, "version"),
|
|
31322
32084
|
updatedAt: metadataTimestamp(root, now),
|
|
31323
32085
|
severity: nativeSeverity(root)
|
|
@@ -31336,8 +32098,8 @@ function candidateFromValue(key, value, root, now) {
|
|
|
31336
32098
|
title: firstMetadataString(root, "title", "attentionTitle"),
|
|
31337
32099
|
summary: firstMetadataString(root, "summary", "attentionSummary", "blockedReason", "needsInputReason", "reason", "message"),
|
|
31338
32100
|
detail: firstMetadataString(root, "detail", "statusDetail"),
|
|
31339
|
-
turnId:
|
|
31340
|
-
blockId:
|
|
32101
|
+
turnId: metadataString7(root, "turnId"),
|
|
32102
|
+
blockId: metadataString7(root, "blockId"),
|
|
31341
32103
|
version: metadataNumber2(root, "version"),
|
|
31342
32104
|
updatedAt: metadataTimestamp(root, now),
|
|
31343
32105
|
severity: nativeSeverity(root)
|
|
@@ -31366,8 +32128,8 @@ function candidateFromRecord(key, record, now, keyImpliesAttention) {
|
|
|
31366
32128
|
title: firstMetadataString(record, "title", "label"),
|
|
31367
32129
|
summary: firstMetadataString(record, "summary", "message", "reason", "description"),
|
|
31368
32130
|
detail: firstMetadataString(record, "detail", "statusDetail", "hint"),
|
|
31369
|
-
turnId:
|
|
31370
|
-
blockId:
|
|
32131
|
+
turnId: metadataString7(record, "turnId"),
|
|
32132
|
+
blockId: metadataString7(record, "blockId"),
|
|
31371
32133
|
version: metadataNumber2(record, "version"),
|
|
31372
32134
|
updatedAt: metadataTimestamp(record, now),
|
|
31373
32135
|
severity: nativeSeverity(record)
|
|
@@ -31387,7 +32149,7 @@ function stableNativeIdPart(value) {
|
|
|
31387
32149
|
return stable || "blocked";
|
|
31388
32150
|
}
|
|
31389
32151
|
function nativeSeverity(record) {
|
|
31390
|
-
const severity =
|
|
32152
|
+
const severity = metadataString7(record, "severity")?.toLowerCase();
|
|
31391
32153
|
if (severity === "critical" || severity === "warning" || severity === "info") {
|
|
31392
32154
|
return severity;
|
|
31393
32155
|
}
|
|
@@ -31396,7 +32158,7 @@ function nativeSeverity(record) {
|
|
|
31396
32158
|
function metadataRecord4(value) {
|
|
31397
32159
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
31398
32160
|
}
|
|
31399
|
-
function
|
|
32161
|
+
function metadataString7(record, key) {
|
|
31400
32162
|
const value = record?.[key];
|
|
31401
32163
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
31402
32164
|
}
|
|
@@ -31417,7 +32179,7 @@ function metadataTimestamp(record, fallback) {
|
|
|
31417
32179
|
}
|
|
31418
32180
|
function firstMetadataString(record, ...keys) {
|
|
31419
32181
|
for (const key of keys) {
|
|
31420
|
-
const value =
|
|
32182
|
+
const value = metadataString7(record, key);
|
|
31421
32183
|
if (value) {
|
|
31422
32184
|
return value;
|
|
31423
32185
|
}
|
|
@@ -31695,7 +32457,7 @@ function endpointTmuxTarget(endpoint) {
|
|
|
31695
32457
|
if (endpoint.transport !== "tmux") {
|
|
31696
32458
|
return null;
|
|
31697
32459
|
}
|
|
31698
|
-
const sessionName = endpoint.sessionId ??
|
|
32460
|
+
const sessionName = endpoint.sessionId ?? metadataString8(endpoint, "tmuxSession");
|
|
31699
32461
|
if (!sessionName) {
|
|
31700
32462
|
return null;
|
|
31701
32463
|
}
|
|
@@ -31813,7 +32575,7 @@ function buildStandaloneTmuxNode(tmuxSession) {
|
|
|
31813
32575
|
};
|
|
31814
32576
|
}
|
|
31815
32577
|
function endpointTitle(endpoint, sessionName) {
|
|
31816
|
-
const agentName =
|
|
32578
|
+
const agentName = metadataString8(endpoint, "agentName") ?? metadataString8(endpoint, "definitionId") ?? endpoint.agentId;
|
|
31817
32579
|
return `${agentName} (${sessionName})`;
|
|
31818
32580
|
}
|
|
31819
32581
|
function layoutForIndex(index2) {
|
|
@@ -31861,7 +32623,7 @@ function fnv1a(value) {
|
|
|
31861
32623
|
}
|
|
31862
32624
|
return (hash >>> 0).toString(36);
|
|
31863
32625
|
}
|
|
31864
|
-
function
|
|
32626
|
+
function metadataString8(endpoint, key) {
|
|
31865
32627
|
const value = endpoint.metadata?.[key];
|
|
31866
32628
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
31867
32629
|
}
|
|
@@ -32521,7 +33283,7 @@ async function loadOpenScoutWebShellState() {
|
|
|
32521
33283
|
}
|
|
32522
33284
|
|
|
32523
33285
|
// packages/web/server/scoutbot-assistant.ts
|
|
32524
|
-
import { randomUUID as
|
|
33286
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
32525
33287
|
var DEFAULT_MODEL = "gpt-4.1-mini";
|
|
32526
33288
|
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
32527
33289
|
var PRESENTER_MODEL = "gpt-4o-mini";
|
|
@@ -32706,13 +33468,13 @@ function createScoutbotAssistantService(input) {
|
|
|
32706
33468
|
}
|
|
32707
33469
|
const now = Date.now();
|
|
32708
33470
|
const userMessage = {
|
|
32709
|
-
id: `msg_${
|
|
33471
|
+
id: `msg_${randomUUID4()}`,
|
|
32710
33472
|
role: "user",
|
|
32711
33473
|
body: trimmed,
|
|
32712
33474
|
createdAt: now
|
|
32713
33475
|
};
|
|
32714
33476
|
const assistantMessage = {
|
|
32715
|
-
id: `msg_${
|
|
33477
|
+
id: `msg_${randomUUID4()}`,
|
|
32716
33478
|
role: "assistant",
|
|
32717
33479
|
body: replyBody,
|
|
32718
33480
|
createdAt: Date.now()
|
|
@@ -33175,7 +33937,7 @@ function parseBriefResponse(raw2, timing, mode = "tour") {
|
|
|
33175
33937
|
const steps = normalizeBriefSteps(record.steps, timing.preparedAt, expiresAt);
|
|
33176
33938
|
const fallbackSummary = raw2.replace(/\s+/g, " ").trim();
|
|
33177
33939
|
return {
|
|
33178
|
-
id: `brf_${
|
|
33940
|
+
id: `brf_${randomUUID4()}`,
|
|
33179
33941
|
title: stringField(record.title, "One-minute brief"),
|
|
33180
33942
|
summary: stringField(record.summary, fallbackSummary || "Scoutbot prepared a current control-plane brief."),
|
|
33181
33943
|
preparedAt: timing.preparedAt,
|
|
@@ -33232,7 +33994,7 @@ function briefFromMarkdown(markdown, timing, mode) {
|
|
|
33232
33994
|
observations
|
|
33233
33995
|
};
|
|
33234
33996
|
return {
|
|
33235
|
-
id: `brf_${
|
|
33997
|
+
id: `brf_${randomUUID4()}`,
|
|
33236
33998
|
title,
|
|
33237
33999
|
summary: headline || fallbackSummary || "Scoutbot prepared a current control-plane brief.",
|
|
33238
34000
|
preparedAt: timing.preparedAt,
|
|
@@ -33542,7 +34304,7 @@ function clampNumber(value, min, max) {
|
|
|
33542
34304
|
function createSession(model) {
|
|
33543
34305
|
const now = Date.now();
|
|
33544
34306
|
return {
|
|
33545
|
-
id: `rgr_${
|
|
34307
|
+
id: `rgr_${randomUUID4()}`,
|
|
33546
34308
|
title: "New Scout Session",
|
|
33547
34309
|
createdAt: now,
|
|
33548
34310
|
updatedAt: now,
|
|
@@ -33738,7 +34500,7 @@ function pruneBriefings(maxRows) {
|
|
|
33738
34500
|
|
|
33739
34501
|
// packages/web/server/scoutbot-reminders.ts
|
|
33740
34502
|
init_support_paths();
|
|
33741
|
-
import { randomUUID as
|
|
34503
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
33742
34504
|
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
33743
34505
|
import { dirname as dirname14, join as join27 } from "path";
|
|
33744
34506
|
|
|
@@ -33802,7 +34564,7 @@ function createScoutbotReminderStore(input = {}) {
|
|
|
33802
34564
|
const title = firstNonEmptyString2(stringValue3(input2.title), titleFromBody(body)) ?? "Reminder";
|
|
33803
34565
|
const context = recordValue(input2.context);
|
|
33804
34566
|
const stored = {
|
|
33805
|
-
id: `rem_${
|
|
34567
|
+
id: `rem_${randomUUID5()}`,
|
|
33806
34568
|
title,
|
|
33807
34569
|
body,
|
|
33808
34570
|
status: "scheduled",
|
|
@@ -34309,9 +35071,9 @@ function renderHelp() {
|
|
|
34309
35071
|
"- `/recent @agent` \u2014 latest messages from an agent",
|
|
34310
35072
|
"- `/doing @agent` \u2014 active work for an agent",
|
|
34311
35073
|
"- `/flight <id>` \u2014 flight status by id",
|
|
34312
|
-
"- `/steer
|
|
35074
|
+
"- `/steer session:<session>` \u2014 target this ScoutBot thread at a session",
|
|
34313
35075
|
"",
|
|
34314
|
-
"Directives can appear anywhere: `eff:low`, `eff:high`, `
|
|
35076
|
+
"Directives can appear anywhere: `eff:low`, `eff:high`, `session:<session>`."
|
|
34315
35077
|
].join(`
|
|
34316
35078
|
`);
|
|
34317
35079
|
}
|
|
@@ -34352,7 +35114,7 @@ function renderStatus(snapshot, rawArg = "") {
|
|
|
34352
35114
|
function renderSteer(parsed, rawArg) {
|
|
34353
35115
|
const targetSessionId = parsed.directives.targetSessionId ?? firstSessionishToken(rawArg);
|
|
34354
35116
|
if (!targetSessionId) {
|
|
34355
|
-
return "Usage: `/steer
|
|
35117
|
+
return "Usage: `/steer session:<session>`.";
|
|
34356
35118
|
}
|
|
34357
35119
|
return `Steering this ScoutBot thread to session ${targetSessionId}.`;
|
|
34358
35120
|
}
|
|
@@ -34450,7 +35212,7 @@ function firstAgentishToken(value) {
|
|
|
34450
35212
|
}
|
|
34451
35213
|
function firstSessionishToken(value) {
|
|
34452
35214
|
const token = value.trim().split(/\s+/).find(Boolean);
|
|
34453
|
-
const normalized = token?.replace(/^sid:/i, "").replace(/[.,!?;]+$/g, "").trim();
|
|
35215
|
+
const normalized = token?.replace(/^(?:sid|session):/i, "").replace(/[.,!?;]+$/g, "").trim();
|
|
34454
35216
|
return normalized || null;
|
|
34455
35217
|
}
|
|
34456
35218
|
function compact2(value, max = 220) {
|
|
@@ -34866,7 +35628,7 @@ async function ensureScoutbotRegistered(baseUrl, snapshot, nodeId, currentDirect
|
|
|
34866
35628
|
await postJson(baseUrl, "/v1/actors", actor);
|
|
34867
35629
|
}
|
|
34868
35630
|
const existingAgent = snapshot.agents?.[SCOUTBOT_AGENT_ID];
|
|
34869
|
-
if (!existingAgent || !
|
|
35631
|
+
if (!existingAgent || !hasCurrentScoutbotAgentRegistration(existingAgent, nodeId)) {
|
|
34870
35632
|
await postJson(baseUrl, "/v1/agents", agent);
|
|
34871
35633
|
}
|
|
34872
35634
|
const existingEndpoint = findScoutbotEndpoint(snapshot, nodeId);
|
|
@@ -34966,12 +35728,12 @@ function isInvalidScoutbotSessionEndpoint(endpoint) {
|
|
|
34966
35728
|
return lastError.includes("no rollout found for thread id") || lastError.includes("codex_app_server session unavailable") || lastError.includes("session unavailable");
|
|
34967
35729
|
}
|
|
34968
35730
|
function hasCurrentScoutbotRuntimeConfig(endpoint) {
|
|
34969
|
-
return
|
|
35731
|
+
return metadataString9(endpoint.metadata, "systemPrompt") === SCOUTBOT_ROLE_CONFIG.systemPrompt && metadataString9(endpoint.metadata, "reasoningEffort") === SCOUTBOT_ROLE_CONFIG.defaults.reasoningEffort;
|
|
34970
35732
|
}
|
|
34971
35733
|
function scoutbotEndpointTransportSessionId(endpoint) {
|
|
34972
35734
|
if (isInvalidScoutbotSessionEndpoint(endpoint))
|
|
34973
35735
|
return null;
|
|
34974
|
-
return
|
|
35736
|
+
return metadataString9(endpoint.metadata, "threadId") ?? metadataString9(endpoint.metadata, "externalSessionId") ?? endpoint.sessionId?.trim() ?? null;
|
|
34975
35737
|
}
|
|
34976
35738
|
async function ensureScoutbotEndpointSession(baseUrl, endpoint, log) {
|
|
34977
35739
|
try {
|
|
@@ -35122,7 +35884,7 @@ async function handleBlock(block, options) {
|
|
|
35122
35884
|
}
|
|
35123
35885
|
options.log.info(`handling thread=${thread.threadId} source=${shortId(message.id)} promptChars=${prompt.length}`);
|
|
35124
35886
|
if (prefilter) {
|
|
35125
|
-
const steerTargetSessionId = prefilter.metadata.matched_rule === "slash.steer" ?
|
|
35887
|
+
const steerTargetSessionId = prefilter.metadata.matched_rule === "slash.steer" ? metadataString9(prefilter.metadata, "targetSessionId") : undefined;
|
|
35126
35888
|
if (steerTargetSessionId) {
|
|
35127
35889
|
await options.threadMap.setThreadTransportSessionId(thread.threadId, steerTargetSessionId);
|
|
35128
35890
|
options.log.info(`thread=${thread.threadId} steered transportSession=${shortId(steerTargetSessionId)}`);
|
|
@@ -35183,7 +35945,7 @@ async function postScoutbotInvocation(input) {
|
|
|
35183
35945
|
const prompt = stripMentions(input.message.body);
|
|
35184
35946
|
const parsed = parseScoutbotDirectives(prompt);
|
|
35185
35947
|
const requestedReasoningEffort = parsed.directives.reasoningEffort ?? metadataReasoningEffort(input.message.metadata);
|
|
35186
|
-
const transportSessionId = parsed.directives.targetSessionId ??
|
|
35948
|
+
const transportSessionId = parsed.directives.targetSessionId ?? metadataString9(input.message.metadata, "targetSessionId") ?? input.thread.transportSessionId?.trim() ?? null;
|
|
35187
35949
|
const task = parsed.body || parsed.messageBody || prompt;
|
|
35188
35950
|
const context = scoutbotInvocationDirectiveContext({
|
|
35189
35951
|
parsed,
|
|
@@ -35201,7 +35963,7 @@ async function postScoutbotInvocation(input) {
|
|
|
35201
35963
|
messageId: input.message.id,
|
|
35202
35964
|
...Object.keys(context).length > 0 ? { context } : {},
|
|
35203
35965
|
execution: {
|
|
35204
|
-
session: "existing",
|
|
35966
|
+
session: transportSessionId ? "existing" : "new",
|
|
35205
35967
|
...transportSessionId ? { targetSessionId: transportSessionId } : {}
|
|
35206
35968
|
},
|
|
35207
35969
|
ensureAwake: true,
|
|
@@ -35277,16 +36039,16 @@ function extractMessage(event) {
|
|
|
35277
36039
|
return typeof record.id === "string" && typeof record.conversationId === "string" ? record : null;
|
|
35278
36040
|
}
|
|
35279
36041
|
function scoutbotReplyTransportSessionId(message) {
|
|
35280
|
-
return
|
|
36042
|
+
return metadataString9(message.metadata, "targetSessionId") ?? metadataString9(message.metadata, "responderSessionId") ?? metadataString9(metadataObject2(message.metadata, "returnAddress"), "sessionId") ?? null;
|
|
35281
36043
|
}
|
|
35282
36044
|
function isScoutbotAddressedMessage(message) {
|
|
35283
36045
|
if (message.mentions?.some((mention) => mention.actorId === SCOUTBOT_AGENT_ID))
|
|
35284
36046
|
return true;
|
|
35285
36047
|
if (message.audience?.notify?.includes(SCOUTBOT_AGENT_ID))
|
|
35286
36048
|
return true;
|
|
35287
|
-
if (
|
|
36049
|
+
if (metadataString9(message.metadata, "destinationId") === SCOUTBOT_AGENT_ID)
|
|
35288
36050
|
return true;
|
|
35289
|
-
if (
|
|
36051
|
+
if (metadataString9(message.metadata, "relayTarget") === SCOUTBOT_AGENT_ID)
|
|
35290
36052
|
return true;
|
|
35291
36053
|
if (metadataStringArrayIncludes(message.metadata, "relayTargetIds", SCOUTBOT_AGENT_ID))
|
|
35292
36054
|
return true;
|
|
@@ -35310,18 +36072,18 @@ function findScoutbotFlightForMessage(snapshot, messageId, options = {}) {
|
|
|
35310
36072
|
if (invocation?.messageId === messageId)
|
|
35311
36073
|
return true;
|
|
35312
36074
|
const returnAddress = metadataObject2(flight.metadata, "returnAddress");
|
|
35313
|
-
return
|
|
36075
|
+
return metadataString9(returnAddress, "replyToMessageId") === messageId;
|
|
35314
36076
|
});
|
|
35315
36077
|
return candidates.sort((left, right) => (right.startedAt ?? 0) - (left.startedAt ?? 0))[0] ?? null;
|
|
35316
36078
|
}
|
|
35317
36079
|
function isScoutbotDirectDeliveryFlight(flight) {
|
|
35318
36080
|
if (flight.targetAgentId !== SCOUTBOT_AGENT_ID)
|
|
35319
36081
|
return false;
|
|
35320
|
-
if (
|
|
36082
|
+
if (metadataString9(flight.metadata, "source") === "scoutbot")
|
|
35321
36083
|
return false;
|
|
35322
|
-
if (
|
|
36084
|
+
if (metadataString9(flight.metadata, "destinationId") === SCOUTBOT_AGENT_ID)
|
|
35323
36085
|
return true;
|
|
35324
|
-
if (
|
|
36086
|
+
if (metadataString9(flight.metadata, "relayTarget") === SCOUTBOT_AGENT_ID)
|
|
35325
36087
|
return true;
|
|
35326
36088
|
return false;
|
|
35327
36089
|
}
|
|
@@ -35349,15 +36111,18 @@ function hasScoutbotLabels(agent) {
|
|
|
35349
36111
|
const labels = new Set(agent.labels ?? []);
|
|
35350
36112
|
return labels.has("assistant") && labels.has("scout") && labels.has("scoutbot");
|
|
35351
36113
|
}
|
|
36114
|
+
function hasCurrentScoutbotAgentRegistration(agent, nodeId) {
|
|
36115
|
+
return hasScoutbotLabels(agent) && hasCurrentScoutbotAgentConfig(agent) && agent.homeNodeId === nodeId && agent.authorityNodeId === nodeId && agent.advertiseScope === "local" && agent.wakePolicy === "keep_warm" && agent.metadata?.source === "scoutbot";
|
|
36116
|
+
}
|
|
35352
36117
|
function hasCurrentScoutbotAgentConfig(agent) {
|
|
35353
36118
|
const roleConfig = metadataObject2(agent.metadata, "roleConfig");
|
|
35354
|
-
return
|
|
36119
|
+
return metadataString9(roleConfig, "systemPrompt") === SCOUTBOT_ROLE_CONFIG.systemPrompt;
|
|
35355
36120
|
}
|
|
35356
36121
|
function metadataObject2(metadata, key) {
|
|
35357
36122
|
const value = metadata?.[key];
|
|
35358
36123
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
35359
36124
|
}
|
|
35360
|
-
function
|
|
36125
|
+
function metadataString9(metadata, key) {
|
|
35361
36126
|
const value = metadata?.[key];
|
|
35362
36127
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
35363
36128
|
}
|
|
@@ -35370,7 +36135,7 @@ var SCOUTBOT_REASONING_EFFORTS = new Set([
|
|
|
35370
36135
|
"xhigh"
|
|
35371
36136
|
]);
|
|
35372
36137
|
function metadataReasoningEffort(metadata) {
|
|
35373
|
-
const value =
|
|
36138
|
+
const value = metadataString9(metadata, "reasoningEffort");
|
|
35374
36139
|
return SCOUTBOT_REASONING_EFFORTS.has(value) ? value : undefined;
|
|
35375
36140
|
}
|
|
35376
36141
|
function metadataStringArrayIncludes(metadata, key, value) {
|
|
@@ -37162,7 +37927,7 @@ function listDirectoryEntries(input) {
|
|
|
37162
37927
|
import { existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
|
|
37163
37928
|
import { homedir as homedir25 } from "os";
|
|
37164
37929
|
import { dirname as dirname19, join as join32 } from "path";
|
|
37165
|
-
import { randomUUID as
|
|
37930
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
37166
37931
|
var VOX_CLIENT_ID = "openscout-web";
|
|
37167
37932
|
var DEFAULT_VOX_RPC_PORT = 42137;
|
|
37168
37933
|
var DEFAULT_VOX_RPC_HOST = "127.0.0.1";
|
|
@@ -37279,7 +38044,7 @@ async function callVoxRpc(method, params, options = {}) {
|
|
|
37279
38044
|
const timeoutMs = options.timeoutMs ?? VOX_RPC_TIMEOUT_MS;
|
|
37280
38045
|
const port = resolveVoxRpcPort();
|
|
37281
38046
|
const socket = new WebSocket(`ws://${DEFAULT_VOX_RPC_HOST}:${port}`);
|
|
37282
|
-
const id =
|
|
38047
|
+
const id = randomUUID6();
|
|
37283
38048
|
return await new Promise((resolve13, reject) => {
|
|
37284
38049
|
let timeout;
|
|
37285
38050
|
const cleanup = () => {
|
|
@@ -37422,7 +38187,7 @@ function defaultVoiceForModel(providers, modelId) {
|
|
|
37422
38187
|
|
|
37423
38188
|
// packages/web/server/vantage-handoff.ts
|
|
37424
38189
|
import { execFile as execFile5, execFileSync as execFileSync6, spawn as spawn6 } from "child_process";
|
|
37425
|
-
import { randomUUID as
|
|
38190
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
37426
38191
|
import { appendFileSync, existsSync as existsSync24, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
37427
38192
|
import { dirname as dirname20, join as join33, resolve as resolve13 } from "path";
|
|
37428
38193
|
import { promisify as promisify5 } from "util";
|
|
@@ -37451,7 +38216,7 @@ async function createOpenScoutVantageHandoff(input) {
|
|
|
37451
38216
|
selectedNativeSessionIds,
|
|
37452
38217
|
now: input.now
|
|
37453
38218
|
});
|
|
37454
|
-
const handoffId = `handoff-${Date.now()}-${
|
|
38219
|
+
const handoffId = `handoff-${Date.now()}-${randomUUID7().slice(0, 8)}`;
|
|
37455
38220
|
const { handoffPath, setupPath } = writeVantageHandoffFiles({
|
|
37456
38221
|
handoffId,
|
|
37457
38222
|
plan,
|
|
@@ -38065,8 +38830,9 @@ function inferDirectTargetAgentId(conversationId, session, senderId) {
|
|
|
38065
38830
|
const operatorCandidates = new Set([
|
|
38066
38831
|
senderId.trim(),
|
|
38067
38832
|
"operator",
|
|
38833
|
+
process.env.OPENSCOUT_OPERATOR_NAME?.trim(),
|
|
38068
38834
|
...configuredOperatorActorIds()
|
|
38069
|
-
]);
|
|
38835
|
+
].filter((candidate) => Boolean(candidate)));
|
|
38070
38836
|
if (session.agentId) {
|
|
38071
38837
|
const participants2 = session.participantIds.filter((participantId) => participantId.trim().length > 0);
|
|
38072
38838
|
if (participants2.length === 0 || participants2.some((participantId) => operatorCandidates.has(participantId))) {
|
|
@@ -38090,7 +38856,7 @@ function inferDirectTargetAgentId(conversationId, session, senderId) {
|
|
|
38090
38856
|
return participants[0] ?? null;
|
|
38091
38857
|
}
|
|
38092
38858
|
}
|
|
38093
|
-
const parsedDirectConversation = conversationId ? parseDirectConversationId(conversationId) : null;
|
|
38859
|
+
const parsedDirectConversation = !session && conversationId ? parseDirectConversationId(conversationId) : null;
|
|
38094
38860
|
if (parsedDirectConversation) {
|
|
38095
38861
|
return parsedDirectConversation.agentId || null;
|
|
38096
38862
|
}
|
|
@@ -38121,6 +38887,15 @@ function resolveConversationRouting(conversationId) {
|
|
|
38121
38887
|
const existingConversationId = session && !directAgentId && !channel ? conversationId ?? null : null;
|
|
38122
38888
|
return { directAgentId, channel, conversationId: existingConversationId, senderId };
|
|
38123
38889
|
}
|
|
38890
|
+
function conversationKindAfterMemberMutation(kind, participantIds) {
|
|
38891
|
+
if (kind === "direct" && participantIds.length > 2) {
|
|
38892
|
+
return "group_direct";
|
|
38893
|
+
}
|
|
38894
|
+
if (kind === "group_direct" && participantIds.length <= 2) {
|
|
38895
|
+
return "direct";
|
|
38896
|
+
}
|
|
38897
|
+
return kind;
|
|
38898
|
+
}
|
|
38124
38899
|
function buildAgentSessionCatalogPayload(input) {
|
|
38125
38900
|
const runtimeDir = relayAgentRuntimeDirectory(input.agentId);
|
|
38126
38901
|
const catalog = readSessionCatalogSync(runtimeDir);
|
|
@@ -39025,7 +39800,7 @@ async function buildAgentConfigurationSnapshot(currentDirectory) {
|
|
|
39025
39800
|
workspaceRoots: settings?.discovery.workspaceRoots ?? [],
|
|
39026
39801
|
hiddenProjectCount: settings?.discovery.hiddenProjectRoots.length ?? 0,
|
|
39027
39802
|
defaultHarness: settings?.agents.defaultHarness ?? "claude",
|
|
39028
|
-
defaultTransport: settings?.agents.defaultTransport ?? "
|
|
39803
|
+
defaultTransport: settings?.agents.defaultTransport ?? "tmux",
|
|
39029
39804
|
defaultCapabilities: settings?.agents.defaultCapabilities ?? [],
|
|
39030
39805
|
sessionPrefix: settings?.agents.sessionPrefix ?? "relay"
|
|
39031
39806
|
},
|
|
@@ -39590,6 +40365,7 @@ async function createOpenScoutWebServer(options) {
|
|
|
39590
40365
|
systemPrompt: optionalString(body.systemPrompt) ?? existing.systemPrompt,
|
|
39591
40366
|
launchArgs: stringList(body.launchArgs, existing.launchArgs),
|
|
39592
40367
|
model,
|
|
40368
|
+
channelEnabled: hasOwn(body, "channelEnabled") ? body.channelEnabled === true : existing.channelEnabled,
|
|
39593
40369
|
capabilities: stringList(body.capabilities, existing.capabilities)
|
|
39594
40370
|
});
|
|
39595
40371
|
if (!nextConfig) {
|
|
@@ -39663,7 +40439,14 @@ async function createOpenScoutWebServer(options) {
|
|
|
39663
40439
|
activityLimit: parseOptionalPositiveInt(c.req.query("activityLimit")),
|
|
39664
40440
|
activityLookbackMs: parseOptionalPositiveInt(c.req.query("activityLookbackMs"))
|
|
39665
40441
|
})));
|
|
39666
|
-
app.get("/api/messages", (c) =>
|
|
40442
|
+
app.get("/api/messages", (c) => {
|
|
40443
|
+
const cId = c.req.query("cId") || c.req.query("conversationId") || undefined;
|
|
40444
|
+
const messages2 = queryRecentMessages(parseOptionalPositiveInt(c.req.query("limit"), 80) ?? 80, { conversationId: cId });
|
|
40445
|
+
return c.json(messages2.map((message) => ({
|
|
40446
|
+
...message,
|
|
40447
|
+
cId: message.conversationId
|
|
40448
|
+
})));
|
|
40449
|
+
});
|
|
39667
40450
|
const rawHeuristicsFromRequest = async (c) => {
|
|
39668
40451
|
const body = await c.req.json().catch(() => null);
|
|
39669
40452
|
if (body && typeof body === "object" && !Array.isArray(body) && typeof body.raw === "string") {
|
|
@@ -39823,14 +40606,24 @@ async function createOpenScoutWebServer(options) {
|
|
|
39823
40606
|
sessionId: c.req.query("sessionId") || undefined,
|
|
39824
40607
|
targetAgentId: c.req.query("targetAgentId") || undefined
|
|
39825
40608
|
})));
|
|
39826
|
-
|
|
40609
|
+
const readCommsList = async (c) => {
|
|
39827
40610
|
const rawLimit = Number(c.req.query("limit"));
|
|
39828
40611
|
const rawKinds = c.req.query("kinds")?.trim();
|
|
39829
|
-
return
|
|
40612
|
+
return getScoutConversations({
|
|
39830
40613
|
query: c.req.query("query") || undefined,
|
|
39831
40614
|
limit: Number.isFinite(rawLimit) ? Math.min(250, Math.max(1, Math.floor(rawLimit))) : undefined,
|
|
39832
40615
|
kinds: parseConversationKinds(rawKinds)
|
|
39833
|
-
})
|
|
40616
|
+
});
|
|
40617
|
+
};
|
|
40618
|
+
app.get("/api/comms", async (c) => {
|
|
40619
|
+
const items = await readCommsList(c);
|
|
40620
|
+
return c.json(items.map((item) => ({
|
|
40621
|
+
...item,
|
|
40622
|
+
cId: item.id
|
|
40623
|
+
})));
|
|
40624
|
+
});
|
|
40625
|
+
app.get("/api/conversations", async (c) => {
|
|
40626
|
+
return c.json(await readCommsList(c));
|
|
39834
40627
|
});
|
|
39835
40628
|
app.get("/api/conversations/:id/read-cursors", async (c) => {
|
|
39836
40629
|
try {
|
|
@@ -39860,13 +40653,16 @@ async function createOpenScoutWebServer(options) {
|
|
|
39860
40653
|
}
|
|
39861
40654
|
});
|
|
39862
40655
|
const writeConversationMembers = async (conversationId, mutate) => {
|
|
39863
|
-
const
|
|
40656
|
+
const currentSession = querySessionById(conversationId);
|
|
40657
|
+
const canonicalConversationId = currentSession?.id ?? conversationId;
|
|
40658
|
+
const existing = queryConversationDefinitionById(canonicalConversationId);
|
|
39864
40659
|
if (!existing)
|
|
39865
40660
|
return null;
|
|
39866
40661
|
const nextParticipants = mutate(existing.participantIds);
|
|
40662
|
+
const nextKind = conversationKindAfterMemberMutation(existing.kind, nextParticipants);
|
|
39867
40663
|
await upsertScoutConversation({
|
|
39868
40664
|
id: existing.id,
|
|
39869
|
-
kind:
|
|
40665
|
+
kind: nextKind,
|
|
39870
40666
|
title: existing.title,
|
|
39871
40667
|
visibility: existing.visibility,
|
|
39872
40668
|
shareMode: existing.shareMode,
|
|
@@ -39877,7 +40673,11 @@ async function createOpenScoutWebServer(options) {
|
|
|
39877
40673
|
...existing.messageId ? { messageId: existing.messageId } : {},
|
|
39878
40674
|
...existing.metadata ? { metadata: existing.metadata } : {}
|
|
39879
40675
|
});
|
|
39880
|
-
return
|
|
40676
|
+
return {
|
|
40677
|
+
kind: nextKind,
|
|
40678
|
+
participantIds: nextParticipants,
|
|
40679
|
+
session: querySessionById(existing.id)
|
|
40680
|
+
};
|
|
39881
40681
|
};
|
|
39882
40682
|
app.post("/api/conversations/:id/members", async (c) => {
|
|
39883
40683
|
const conversationId = c.req.param("id");
|
|
@@ -39888,7 +40688,7 @@ async function createOpenScoutWebServer(options) {
|
|
|
39888
40688
|
const next = await writeConversationMembers(conversationId, (current) => Array.from(new Set([...current, actorId])).sort());
|
|
39889
40689
|
if (!next)
|
|
39890
40690
|
return c.json({ error: "conversation not found" }, 404);
|
|
39891
|
-
return c.json({ ok: true,
|
|
40691
|
+
return c.json({ ok: true, ...next });
|
|
39892
40692
|
});
|
|
39893
40693
|
app.delete("/api/conversations/:id/members/:actorId", async (c) => {
|
|
39894
40694
|
const conversationId = c.req.param("id");
|
|
@@ -39896,7 +40696,7 @@ async function createOpenScoutWebServer(options) {
|
|
|
39896
40696
|
const next = await writeConversationMembers(conversationId, (current) => current.filter((id) => id !== actorId));
|
|
39897
40697
|
if (!next)
|
|
39898
40698
|
return c.json({ error: "conversation not found" }, 404);
|
|
39899
|
-
return c.json({ ok: true,
|
|
40699
|
+
return c.json({ ok: true, ...next });
|
|
39900
40700
|
});
|
|
39901
40701
|
app.get("/api/sessions", (c) => c.json(querySessions()));
|
|
39902
40702
|
app.get("/api/session-ref/:id", async (c) => {
|
|
@@ -40221,11 +41021,12 @@ async function createOpenScoutWebServer(options) {
|
|
|
40221
41021
|
}
|
|
40222
41022
|
});
|
|
40223
41023
|
app.post("/api/send", async (c) => {
|
|
40224
|
-
const { body, conversationId, threadId } = await c.req.json();
|
|
41024
|
+
const { body, cId, conversationId, threadId } = await c.req.json();
|
|
40225
41025
|
if (!body?.trim()) {
|
|
40226
41026
|
return c.json({ error: "body is required" }, 400);
|
|
40227
41027
|
}
|
|
40228
|
-
|
|
41028
|
+
const routeCId = cId ?? conversationId;
|
|
41029
|
+
if (!routeCId && scoutbotRunner) {
|
|
40229
41030
|
try {
|
|
40230
41031
|
const result2 = await scoutbotRunner.postOperatorMessage({
|
|
40231
41032
|
body: body.trim(),
|
|
@@ -40240,7 +41041,7 @@ async function createOpenScoutWebServer(options) {
|
|
|
40240
41041
|
return c.json({ error: message }, /unknown scoutbot thread/i.test(message) ? 404 : 500);
|
|
40241
41042
|
}
|
|
40242
41043
|
}
|
|
40243
|
-
const { directAgentId, channel, conversationId: routedConversationId, senderId } = resolveConversationRouting(
|
|
41044
|
+
const { directAgentId, channel, conversationId: routedConversationId, senderId } = resolveConversationRouting(routeCId);
|
|
40244
41045
|
if (directAgentId) {
|
|
40245
41046
|
if (directAgentId === SCOUTBOT_AGENT_ID && scoutbotRunner) {
|
|
40246
41047
|
try {
|
|
@@ -40290,11 +41091,11 @@ async function createOpenScoutWebServer(options) {
|
|
|
40290
41091
|
return c.json(result);
|
|
40291
41092
|
});
|
|
40292
41093
|
app.post("/api/ask", async (c) => {
|
|
40293
|
-
const { body, conversationId } = await c.req.json();
|
|
41094
|
+
const { body, cId, conversationId } = await c.req.json();
|
|
40294
41095
|
if (!body?.trim()) {
|
|
40295
41096
|
return c.json({ error: "body is required" }, 400);
|
|
40296
41097
|
}
|
|
40297
|
-
const { directAgentId, senderId } = resolveConversationRouting(conversationId);
|
|
41098
|
+
const { directAgentId, senderId } = resolveConversationRouting(cId ?? conversationId);
|
|
40298
41099
|
if (!directAgentId) {
|
|
40299
41100
|
return c.json({
|
|
40300
41101
|
error: "ask is only available in a direct conversation with one agent"
|
|
@@ -40341,7 +41142,7 @@ async function createOpenScoutWebServer(options) {
|
|
|
40341
41142
|
signal: c.req.raw.signal
|
|
40342
41143
|
}));
|
|
40343
41144
|
} catch (error) {
|
|
40344
|
-
const message = error instanceof Error ? error.message : "
|
|
41145
|
+
const message = error instanceof Error ? error.message : "Voice speech failed";
|
|
40345
41146
|
return c.json({ error: message }, 503);
|
|
40346
41147
|
}
|
|
40347
41148
|
});
|
|
@@ -40406,28 +41207,28 @@ async function createOpenScoutWebServer(options) {
|
|
|
40406
41207
|
}
|
|
40407
41208
|
});
|
|
40408
41209
|
app.get("/api/tail/discover", async (c) => {
|
|
40409
|
-
const
|
|
40410
|
-
|
|
40411
|
-
|
|
41210
|
+
const url = new URL(scoutBrokerPaths.v1.tailDiscover, resolveScoutBrokerUrl());
|
|
41211
|
+
if (c.req.query("force") === "true" || c.req.query("force") === "1") {
|
|
41212
|
+
url.searchParams.set("force", "1");
|
|
41213
|
+
}
|
|
41214
|
+
const res = await fetch(url);
|
|
41215
|
+
if (!res.ok) {
|
|
41216
|
+
return c.json({ error: `broker tail discovery unavailable (${res.status})` }, 502);
|
|
41217
|
+
}
|
|
41218
|
+
return c.json(await res.json());
|
|
40412
41219
|
});
|
|
40413
41220
|
app.get("/api/tail/recent", async (c) => {
|
|
40414
41221
|
const limitParam = parseOptionalPositiveInt(c.req.query("limit"), 500) ?? 500;
|
|
40415
|
-
const
|
|
40416
|
-
|
|
40417
|
-
|
|
40418
|
-
|
|
40419
|
-
const transcriptEvents = await readRecentTranscriptEvents(limitParam, {
|
|
40420
|
-
perTranscriptLineLimit: Math.min(200, Math.max(50, limitParam))
|
|
40421
|
-
});
|
|
40422
|
-
const eventsById = new Map;
|
|
40423
|
-
for (const event of transcriptEvents) {
|
|
40424
|
-
eventsById.set(event.id, event);
|
|
41222
|
+
const url = new URL(scoutBrokerPaths.v1.tailRecent, resolveScoutBrokerUrl());
|
|
41223
|
+
url.searchParams.set("limit", String(limitParam));
|
|
41224
|
+
if (c.req.query("transcripts") === "true" || c.req.query("transcripts") === "1") {
|
|
41225
|
+
url.searchParams.set("transcripts", "true");
|
|
40425
41226
|
}
|
|
40426
|
-
|
|
40427
|
-
|
|
41227
|
+
const res = await fetch(url);
|
|
41228
|
+
if (!res.ok) {
|
|
41229
|
+
return c.json({ error: `broker tail unavailable (${res.status})` }, 502);
|
|
40428
41230
|
}
|
|
40429
|
-
|
|
40430
|
-
return c.json({ events: events2 });
|
|
41231
|
+
return c.json(await res.json());
|
|
40431
41232
|
});
|
|
40432
41233
|
app.get("/api/broadcast/recent", (c) => {
|
|
40433
41234
|
const limitParam = parseOptionalPositiveInt(c.req.query("limit"), 50) ?? 50;
|
|
@@ -40571,7 +41372,7 @@ function resolveOpenScoutWebApplicationServerIdentity(env = process.env, _machin
|
|
|
40571
41372
|
// packages/web/server/relay.ts
|
|
40572
41373
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
40573
41374
|
import { join as join35 } from "path";
|
|
40574
|
-
import { randomUUID as
|
|
41375
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
40575
41376
|
var UPLOAD_DIR = "/tmp/scout-uploads";
|
|
40576
41377
|
async function handleRelayUpload(req) {
|
|
40577
41378
|
const { name, data } = await req.json();
|
|
@@ -40579,7 +41380,7 @@ async function handleRelayUpload(req) {
|
|
|
40579
41380
|
return Response.json({ error: "Missing name or data" }, { status: 400 });
|
|
40580
41381
|
}
|
|
40581
41382
|
await mkdir9(UPLOAD_DIR, { recursive: true });
|
|
40582
|
-
const filename = `${
|
|
41383
|
+
const filename = `${randomUUID8()}-${name}`;
|
|
40583
41384
|
const filepath = join35(UPLOAD_DIR, filename);
|
|
40584
41385
|
await Bun.write(filepath, Buffer.from(data, "base64"));
|
|
40585
41386
|
return Response.json({ path: filepath });
|