@openscout/scout 0.2.55 → 0.2.56

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.
@@ -122,6 +122,35 @@ function extractPendingApprovalRequests(snapshot) {
122
122
  }
123
123
 
124
124
  // packages/agent-sessions/src/state.ts
125
+ function normalizeTrackedTimestamp(value) {
126
+ if (value == null) {
127
+ return null;
128
+ }
129
+ if (typeof value === "number") {
130
+ if (!Number.isFinite(value)) {
131
+ return null;
132
+ }
133
+ return value < 1000000000000 ? value * 1000 : value;
134
+ }
135
+ if (value instanceof Date) {
136
+ const ms = value.getTime();
137
+ return Number.isFinite(ms) ? ms : null;
138
+ }
139
+ if (typeof value === "string") {
140
+ const trimmed = value.trim();
141
+ if (!trimmed) {
142
+ return null;
143
+ }
144
+ const numeric = Number(trimmed);
145
+ if (Number.isFinite(numeric)) {
146
+ return numeric < 1000000000000 ? numeric * 1000 : numeric;
147
+ }
148
+ const parsed = Date.parse(trimmed);
149
+ return Number.isNaN(parsed) ? null : parsed;
150
+ }
151
+ return null;
152
+ }
153
+
125
154
  class StateTracker {
126
155
  states = new Map;
127
156
  createSession(sessionId, session) {
@@ -156,7 +185,7 @@ class StateTracker {
156
185
  }
157
186
  return summaries;
158
187
  }
159
- trackEvent(sessionId, event) {
188
+ trackEvent(sessionId, event, capturedAt) {
160
189
  const state = this.states.get(sessionId);
161
190
  if (!state)
162
191
  return;
@@ -168,13 +197,13 @@ class StateTracker {
168
197
  state.session = { ...state.session, status: "closed" };
169
198
  break;
170
199
  case "turn:start":
171
- this.handleTurnStart(state, event);
200
+ this.handleTurnStart(state, event, capturedAt);
172
201
  break;
173
202
  case "turn:end":
174
- this.handleTurnEnd(state, event);
203
+ this.handleTurnEnd(state, event, capturedAt);
175
204
  break;
176
205
  case "turn:error":
177
- this.handleTurnError(state, event);
206
+ this.handleTurnError(state, event, capturedAt);
178
207
  break;
179
208
  case "block:start":
180
209
  this.handleBlockStart(state, event);
@@ -199,17 +228,17 @@ class StateTracker {
199
228
  break;
200
229
  }
201
230
  }
202
- handleTurnStart(state, event) {
231
+ handleTurnStart(state, event, capturedAt) {
203
232
  const turn = {
204
233
  id: event.turn.id,
205
234
  status: "streaming",
206
235
  blocks: [],
207
- startedAt: Date.now()
236
+ startedAt: normalizeTrackedTimestamp(event.turn.startedAt) ?? normalizeTrackedTimestamp(capturedAt) ?? Date.now()
208
237
  };
209
238
  state.turns.push(turn);
210
239
  state.currentTurnId = turn.id;
211
240
  }
212
- handleTurnEnd(state, event) {
241
+ handleTurnEnd(state, event, capturedAt) {
213
242
  const turn = this.findTurn(state, event.turnId);
214
243
  if (!turn)
215
244
  return;
@@ -227,17 +256,17 @@ class StateTracker {
227
256
  turn.status = "completed";
228
257
  break;
229
258
  }
230
- turn.endedAt = Date.now();
259
+ turn.endedAt = normalizeTrackedTimestamp(capturedAt) ?? Date.now();
231
260
  if (state.currentTurnId === event.turnId) {
232
261
  state.currentTurnId = undefined;
233
262
  }
234
263
  }
235
- handleTurnError(state, event) {
264
+ handleTurnError(state, event, capturedAt) {
236
265
  const turn = this.findTurn(state, event.turnId);
237
266
  if (!turn)
238
267
  return;
239
268
  turn.status = "error";
240
- turn.endedAt = Date.now();
269
+ turn.endedAt = normalizeTrackedTimestamp(capturedAt) ?? Date.now();
241
270
  if (state.currentTurnId === event.turnId) {
242
271
  state.currentTurnId = undefined;
243
272
  }
@@ -577,6 +606,682 @@ var init_registry = __esm(() => {
577
606
  };
578
607
  });
579
608
 
609
+ // packages/agent-sessions/src/history.ts
610
+ import { basename, dirname } from "path";
611
+ function decodeClaudeProjectsSlug(name) {
612
+ if (!name || !name.startsWith("-")) {
613
+ return null;
614
+ }
615
+ const tail = name.slice(1);
616
+ if (!tail) {
617
+ return null;
618
+ }
619
+ return `/${tail.replace(/-/g, "/")}`;
620
+ }
621
+ function inferClaudeHistoryCwd(path) {
622
+ const parent = basename(dirname(path));
623
+ return decodeClaudeProjectsSlug(parent);
624
+ }
625
+ function deriveHistoryName(path) {
626
+ const claudeCwd = inferClaudeHistoryCwd(path);
627
+ if (claudeCwd) {
628
+ return basename(claudeCwd) || claudeCwd;
629
+ }
630
+ const parent = basename(dirname(path));
631
+ return parent || basename(path) || "History Session";
632
+ }
633
+ function normalizeTimestamp(value) {
634
+ if (typeof value === "number") {
635
+ if (!Number.isFinite(value)) {
636
+ return null;
637
+ }
638
+ return value < 1000000000000 ? value * 1000 : value;
639
+ }
640
+ if (value instanceof Date) {
641
+ const time = value.getTime();
642
+ return Number.isFinite(time) ? time : null;
643
+ }
644
+ if (typeof value === "string") {
645
+ const trimmed = value.trim();
646
+ if (!trimmed) {
647
+ return null;
648
+ }
649
+ const numeric = Number(trimmed);
650
+ if (Number.isFinite(numeric)) {
651
+ return numeric < 1000000000000 ? numeric * 1000 : numeric;
652
+ }
653
+ const parsed = Date.parse(trimmed);
654
+ return Number.isNaN(parsed) ? null : parsed;
655
+ }
656
+ return null;
657
+ }
658
+ function extractRecordTimestamp(record) {
659
+ const candidates = [
660
+ record.timestamp,
661
+ record.createdAt,
662
+ record.created_at,
663
+ record.updatedAt,
664
+ record.updated_at,
665
+ record.time,
666
+ record.ts
667
+ ];
668
+ for (const candidate of candidates) {
669
+ const normalized = normalizeTimestamp(candidate);
670
+ if (normalized !== null) {
671
+ return normalized;
672
+ }
673
+ }
674
+ return null;
675
+ }
676
+ function stringifyUnknown(value) {
677
+ if (typeof value === "string") {
678
+ return value;
679
+ }
680
+ if (value == null) {
681
+ return "";
682
+ }
683
+ try {
684
+ return JSON.stringify(value);
685
+ } catch {
686
+ return String(value);
687
+ }
688
+ }
689
+ function renderToolResultContent(content) {
690
+ if (typeof content === "string") {
691
+ return content;
692
+ }
693
+ if (Array.isArray(content)) {
694
+ const text = content.map((entry) => {
695
+ if (typeof entry === "string") {
696
+ return entry;
697
+ }
698
+ const item = entry;
699
+ if (typeof item.text === "string") {
700
+ return item.text;
701
+ }
702
+ if (typeof item.content === "string") {
703
+ return item.content;
704
+ }
705
+ return stringifyUnknown(entry);
706
+ }).filter(Boolean).join(`
707
+ `);
708
+ return text || stringifyUnknown(content);
709
+ }
710
+ return stringifyUnknown(content);
711
+ }
712
+ function extractQuestionOptions(firstQuestion) {
713
+ const options = Array.isArray(firstQuestion.options) ? firstQuestion.options : [];
714
+ return options.map((option) => {
715
+ if (typeof option === "string") {
716
+ return { label: option };
717
+ }
718
+ const record = option;
719
+ return {
720
+ label: typeof record.label === "string" ? record.label : String(option),
721
+ description: typeof record.description === "string" ? record.description : undefined
722
+ };
723
+ });
724
+ }
725
+ function parseQuestionAnswer(content) {
726
+ const text = renderToolResultContent(content).trim();
727
+ if (!text) {
728
+ return [];
729
+ }
730
+ return text.split(/\s*,\s*/u).map((part) => part.trim()).filter(Boolean);
731
+ }
732
+ function inferHistoryAdapterType(path, adapterType) {
733
+ const normalizedAdapter = adapterType?.trim();
734
+ if (normalizedAdapter === "claude-code") {
735
+ return "claude-code";
736
+ }
737
+ if (normalizedAdapter === "codex") {
738
+ return "codex";
739
+ }
740
+ const normalizedPath = path.toLowerCase();
741
+ if (normalizedPath.includes("/.claude/projects/")) {
742
+ return "claude-code";
743
+ }
744
+ if (normalizedPath.includes("/.codex/") || normalizedPath.includes("/.openai-codex/")) {
745
+ return "codex";
746
+ }
747
+ return "unknown";
748
+ }
749
+ function buildBaseHistorySession(input, adapterType) {
750
+ const cwd = input.cwd?.trim() || inferClaudeHistoryCwd(input.path) || undefined;
751
+ return {
752
+ id: input.sessionId?.trim() || `history:${input.path}`,
753
+ name: input.name?.trim() || deriveHistoryName(input.path),
754
+ adapterType,
755
+ status: "idle",
756
+ ...cwd ? { cwd } : {},
757
+ providerMeta: {
758
+ historyPath: input.path,
759
+ historyAdapterType: adapterType,
760
+ source: "external_history"
761
+ }
762
+ };
763
+ }
764
+
765
+ class ClaudeCodeHistoryParser {
766
+ session;
767
+ baseTimestampMs;
768
+ events = [];
769
+ currentTurn = null;
770
+ turnCounter = 0;
771
+ blockIndex = 0;
772
+ toolBlockMap = new Map;
773
+ questionBlockMap = new Map;
774
+ blockById = new Map;
775
+ activeStreamBlocks = new Map;
776
+ sawStreamTextThisTurn = false;
777
+ constructor(session, baseTimestampMs) {
778
+ this.session = session;
779
+ this.baseTimestampMs = baseTimestampMs;
780
+ }
781
+ parse(content) {
782
+ const lines = content.split(/\r?\n/u);
783
+ let parsedLineCount = 0;
784
+ let skippedLineCount = 0;
785
+ let lineCount = 0;
786
+ for (let index = 0;index < lines.length; index += 1) {
787
+ const rawLine = lines[index];
788
+ const trimmed = rawLine.trim();
789
+ if (!trimmed) {
790
+ continue;
791
+ }
792
+ lineCount += 1;
793
+ let record;
794
+ try {
795
+ const parsed = JSON.parse(trimmed);
796
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
797
+ skippedLineCount += 1;
798
+ continue;
799
+ }
800
+ record = parsed;
801
+ } catch {
802
+ skippedLineCount += 1;
803
+ continue;
804
+ }
805
+ const capturedAt = extractRecordTimestamp(record) ?? this.baseTimestampMs + index;
806
+ if (this.handleRecord(record, capturedAt)) {
807
+ parsedLineCount += 1;
808
+ } else {
809
+ skippedLineCount += 1;
810
+ }
811
+ }
812
+ return {
813
+ events: this.events,
814
+ lineCount,
815
+ parsedLineCount,
816
+ skippedLineCount
817
+ };
818
+ }
819
+ handleRecord(record, capturedAt) {
820
+ const type = typeof record.type === "string" ? record.type : null;
821
+ if (!type) {
822
+ return false;
823
+ }
824
+ switch (type) {
825
+ case "system":
826
+ this.handleSystem(record, capturedAt);
827
+ return true;
828
+ case "user":
829
+ this.startTurn(capturedAt);
830
+ return true;
831
+ case "assistant":
832
+ this.handleAssistant(record, capturedAt);
833
+ return true;
834
+ case "tool_use":
835
+ this.handleToolUse(record, capturedAt);
836
+ return true;
837
+ case "tool_result":
838
+ this.handleToolResult(record, capturedAt);
839
+ return true;
840
+ case "stream_event":
841
+ this.handleStreamEvent(record, capturedAt);
842
+ return true;
843
+ case "result":
844
+ this.handleResult(record, capturedAt);
845
+ return true;
846
+ case "error":
847
+ this.handleError(record, capturedAt);
848
+ return true;
849
+ default:
850
+ return false;
851
+ }
852
+ }
853
+ handleSystem(record, capturedAt) {
854
+ if (record.subtype !== "init") {
855
+ return;
856
+ }
857
+ let changed = false;
858
+ const externalSessionId = typeof record.session_id === "string" ? record.session_id : typeof record.sessionId === "string" ? record.sessionId : null;
859
+ if (externalSessionId) {
860
+ const providerMeta = { ...this.session.providerMeta ?? {} };
861
+ if (providerMeta.externalSessionId !== externalSessionId) {
862
+ providerMeta.externalSessionId = externalSessionId;
863
+ this.session.providerMeta = providerMeta;
864
+ changed = true;
865
+ }
866
+ }
867
+ if (typeof record.cwd === "string" && record.cwd.trim() && this.session.cwd !== record.cwd) {
868
+ this.session.cwd = record.cwd;
869
+ changed = true;
870
+ }
871
+ if (typeof record.model === "string" && record.model.trim() && this.session.model !== record.model) {
872
+ this.session.model = record.model;
873
+ changed = true;
874
+ }
875
+ if (changed) {
876
+ this.emitEvent(capturedAt, {
877
+ event: "session:update",
878
+ session: { ...this.session }
879
+ });
880
+ }
881
+ }
882
+ handleAssistant(record, capturedAt) {
883
+ const turn = this.ensureTurn(capturedAt);
884
+ if (this.sawStreamTextThisTurn) {
885
+ return;
886
+ }
887
+ const content = record.message && typeof record.message === "object" ? record.message.content : record.content;
888
+ if (!Array.isArray(content)) {
889
+ return;
890
+ }
891
+ for (const part of content) {
892
+ const contentPart = part;
893
+ const contentType = typeof contentPart.type === "string" ? contentPart.type : "";
894
+ if (contentType === "thinking" || contentType === "reasoning") {
895
+ const block = this.startBlock(turn, capturedAt, {
896
+ type: "reasoning",
897
+ text: typeof contentPart.thinking === "string" ? contentPart.thinking : typeof contentPart.text === "string" ? contentPart.text : "",
898
+ status: "completed"
899
+ });
900
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
901
+ } else if (contentType === "text") {
902
+ const block = this.startBlock(turn, capturedAt, {
903
+ type: "text",
904
+ text: typeof contentPart.text === "string" ? contentPart.text : "",
905
+ status: "completed"
906
+ });
907
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
908
+ }
909
+ }
910
+ }
911
+ handleStreamEvent(record, capturedAt) {
912
+ const turn = this.ensureTurn(capturedAt);
913
+ const streamEvent = record.event;
914
+ if (!streamEvent || typeof streamEvent !== "object") {
915
+ return;
916
+ }
917
+ const eventRecord = streamEvent;
918
+ const streamType = typeof eventRecord.type === "string" ? eventRecord.type : "";
919
+ if (streamType === "message_start") {
920
+ this.activeStreamBlocks.clear();
921
+ this.sawStreamTextThisTurn = false;
922
+ return;
923
+ }
924
+ if (streamType === "content_block_start") {
925
+ const index = typeof eventRecord.index === "number" ? eventRecord.index : 0;
926
+ const contentBlock = eventRecord.content_block;
927
+ if (!contentBlock || typeof contentBlock !== "object") {
928
+ return;
929
+ }
930
+ const contentRecord = contentBlock;
931
+ const contentType = typeof contentRecord.type === "string" ? contentRecord.type : "";
932
+ if (contentType !== "text" && contentType !== "thinking") {
933
+ return;
934
+ }
935
+ const block = this.startBlock(turn, capturedAt, {
936
+ type: contentType === "thinking" ? "reasoning" : "text",
937
+ text: "",
938
+ status: "streaming"
939
+ });
940
+ this.activeStreamBlocks.set(index, block);
941
+ this.sawStreamTextThisTurn = true;
942
+ const initialText = contentType === "thinking" ? typeof contentRecord.thinking === "string" ? contentRecord.thinking : "" : typeof contentRecord.text === "string" ? contentRecord.text : "";
943
+ this.appendTextDelta(capturedAt, turn, block, initialText);
944
+ return;
945
+ }
946
+ if (streamType === "content_block_delta") {
947
+ const index = typeof eventRecord.index === "number" ? eventRecord.index : 0;
948
+ const block = this.activeStreamBlocks.get(index);
949
+ const delta = eventRecord.delta;
950
+ if (!block || !delta || typeof delta !== "object") {
951
+ return;
952
+ }
953
+ const deltaRecord = delta;
954
+ const deltaType = typeof deltaRecord.type === "string" ? deltaRecord.type : "";
955
+ if (deltaType === "text_delta") {
956
+ this.appendTextDelta(capturedAt, turn, block, typeof deltaRecord.text === "string" ? deltaRecord.text : "");
957
+ return;
958
+ }
959
+ if (deltaType === "thinking_delta") {
960
+ this.appendTextDelta(capturedAt, turn, block, typeof deltaRecord.thinking === "string" ? deltaRecord.thinking : "");
961
+ }
962
+ return;
963
+ }
964
+ if (streamType === "content_block_stop") {
965
+ const index = typeof eventRecord.index === "number" ? eventRecord.index : 0;
966
+ const block = this.activeStreamBlocks.get(index);
967
+ if (!block) {
968
+ return;
969
+ }
970
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
971
+ this.activeStreamBlocks.delete(index);
972
+ }
973
+ }
974
+ handleToolUse(record, capturedAt) {
975
+ const turn = this.ensureTurn(capturedAt);
976
+ const toolName = typeof record.tool_name === "string" ? record.tool_name : typeof record.name === "string" ? record.name : "unknown";
977
+ const toolCallId = typeof record.tool_use_id === "string" ? record.tool_use_id : typeof record.id === "string" ? record.id : `${turn.id}:tool:${this.blockIndex}`;
978
+ if (toolName === "AskUserQuestion") {
979
+ const input = record.input && typeof record.input === "object" ? record.input : {};
980
+ const questions = Array.isArray(input.questions) ? input.questions : [];
981
+ const firstQuestion = questions[0] ?? {};
982
+ const block2 = this.startBlock(turn, capturedAt, {
983
+ id: `${turn.id}:question:${toolCallId}`,
984
+ type: "question",
985
+ header: typeof firstQuestion.header === "string" ? firstQuestion.header : undefined,
986
+ question: typeof firstQuestion.question === "string" ? firstQuestion.question : "",
987
+ options: extractQuestionOptions(firstQuestion),
988
+ multiSelect: firstQuestion.multiSelect === true,
989
+ questionStatus: "awaiting_answer",
990
+ answer: undefined,
991
+ status: "streaming"
992
+ });
993
+ this.toolBlockMap.set(toolCallId, block2.id);
994
+ this.questionBlockMap.set(toolCallId, block2.id);
995
+ return;
996
+ }
997
+ let action;
998
+ if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
999
+ const input = record.input && typeof record.input === "object" ? record.input : {};
1000
+ action = {
1001
+ kind: "file_change",
1002
+ path: typeof input.file_path === "string" ? input.file_path : typeof input.path === "string" ? input.path : "",
1003
+ diff: "",
1004
+ status: "running",
1005
+ output: ""
1006
+ };
1007
+ } else if (toolName === "Bash") {
1008
+ const input = record.input && typeof record.input === "object" ? record.input : {};
1009
+ action = {
1010
+ kind: "command",
1011
+ command: typeof input.command === "string" ? input.command : "",
1012
+ status: "running",
1013
+ output: ""
1014
+ };
1015
+ } else if (toolName === "Agent") {
1016
+ const input = record.input && typeof record.input === "object" ? record.input : {};
1017
+ action = {
1018
+ kind: "subagent",
1019
+ agentId: toolCallId,
1020
+ agentName: typeof input.description === "string" ? input.description : undefined,
1021
+ prompt: typeof input.prompt === "string" ? input.prompt : undefined,
1022
+ status: "running",
1023
+ output: ""
1024
+ };
1025
+ } else {
1026
+ action = {
1027
+ kind: "tool_call",
1028
+ toolName,
1029
+ toolCallId,
1030
+ input: record.input,
1031
+ status: "running",
1032
+ output: ""
1033
+ };
1034
+ }
1035
+ const block = this.startBlock(turn, capturedAt, {
1036
+ id: `${turn.id}:action:${toolCallId}`,
1037
+ type: "action",
1038
+ action,
1039
+ status: "streaming"
1040
+ });
1041
+ this.toolBlockMap.set(toolCallId, block.id);
1042
+ }
1043
+ handleToolResult(record, capturedAt) {
1044
+ const turn = this.ensureTurn(capturedAt);
1045
+ const toolCallId = typeof record.tool_use_id === "string" ? record.tool_use_id : typeof record.id === "string" ? record.id : "";
1046
+ if (!toolCallId) {
1047
+ return;
1048
+ }
1049
+ const questionBlockId = this.questionBlockMap.get(toolCallId);
1050
+ if (questionBlockId) {
1051
+ const answer = parseQuestionAnswer(record.content);
1052
+ this.emitEvent(capturedAt, {
1053
+ event: "block:question:answer",
1054
+ sessionId: this.session.id,
1055
+ turnId: turn.id,
1056
+ blockId: questionBlockId,
1057
+ questionStatus: "answered",
1058
+ ...answer.length > 0 ? { answer } : {}
1059
+ });
1060
+ const block2 = this.blockById.get(questionBlockId);
1061
+ if (block2) {
1062
+ this.emitBlockEnd(capturedAt, turn, block2, "completed");
1063
+ }
1064
+ this.questionBlockMap.delete(toolCallId);
1065
+ this.toolBlockMap.delete(toolCallId);
1066
+ return;
1067
+ }
1068
+ const blockId = this.toolBlockMap.get(toolCallId);
1069
+ if (!blockId) {
1070
+ return;
1071
+ }
1072
+ const output = renderToolResultContent(record.content);
1073
+ if (output) {
1074
+ this.emitEvent(capturedAt, {
1075
+ event: "block:action:output",
1076
+ sessionId: this.session.id,
1077
+ turnId: turn.id,
1078
+ blockId,
1079
+ output
1080
+ });
1081
+ }
1082
+ const status = record.is_error === true ? "failed" : "completed";
1083
+ this.emitEvent(capturedAt, {
1084
+ event: "block:action:status",
1085
+ sessionId: this.session.id,
1086
+ turnId: turn.id,
1087
+ blockId,
1088
+ status
1089
+ });
1090
+ const block = this.blockById.get(blockId);
1091
+ if (block) {
1092
+ this.emitBlockEnd(capturedAt, turn, block, status === "failed" ? "failed" : "completed");
1093
+ }
1094
+ this.toolBlockMap.delete(toolCallId);
1095
+ }
1096
+ handleResult(record, capturedAt) {
1097
+ const turn = this.currentTurn;
1098
+ if (!turn) {
1099
+ return;
1100
+ }
1101
+ this.completeOpenStreamBlocks(capturedAt, turn);
1102
+ const denials = Array.isArray(record.permission_denials) ? record.permission_denials : [];
1103
+ for (const denial of denials) {
1104
+ const denialRecord = denial;
1105
+ if (denialRecord.tool_name !== "AskUserQuestion") {
1106
+ continue;
1107
+ }
1108
+ const input = denialRecord.tool_input && typeof denialRecord.tool_input === "object" ? denialRecord.tool_input : {};
1109
+ const questions = Array.isArray(input.questions) ? input.questions : [];
1110
+ const firstQuestion = questions[0] ?? {};
1111
+ const block = this.startBlock(turn, capturedAt, {
1112
+ type: "question",
1113
+ header: typeof firstQuestion.header === "string" ? firstQuestion.header : undefined,
1114
+ question: typeof firstQuestion.question === "string" ? firstQuestion.question : "",
1115
+ options: extractQuestionOptions(firstQuestion),
1116
+ multiSelect: firstQuestion.multiSelect === true,
1117
+ questionStatus: "denied",
1118
+ status: "completed"
1119
+ });
1120
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
1121
+ }
1122
+ this.endTurn(record.subtype === "error" ? "failed" : "completed", capturedAt);
1123
+ }
1124
+ handleError(record, capturedAt) {
1125
+ const turn = this.ensureTurn(capturedAt);
1126
+ const error = record.error && typeof record.error === "object" ? record.error : {};
1127
+ const message = typeof error.message === "string" ? error.message : typeof record.message === "string" ? record.message : "Unknown error";
1128
+ this.emitError(turn, capturedAt, message);
1129
+ this.endTurn("failed", capturedAt);
1130
+ }
1131
+ startTurn(capturedAt) {
1132
+ if (this.currentTurn) {
1133
+ this.endTurn("stopped", capturedAt);
1134
+ }
1135
+ this.turnCounter += 1;
1136
+ this.blockIndex = 0;
1137
+ this.toolBlockMap.clear();
1138
+ this.questionBlockMap.clear();
1139
+ this.blockById.clear();
1140
+ this.activeStreamBlocks.clear();
1141
+ this.sawStreamTextThisTurn = false;
1142
+ this.session.status = "active";
1143
+ this.emitEvent(capturedAt, {
1144
+ event: "session:update",
1145
+ session: { ...this.session }
1146
+ });
1147
+ const turn = {
1148
+ id: `history-turn-${this.turnCounter}`,
1149
+ sessionId: this.session.id,
1150
+ status: "started",
1151
+ startedAt: new Date(capturedAt).toISOString(),
1152
+ blocks: []
1153
+ };
1154
+ this.currentTurn = turn;
1155
+ this.emitEvent(capturedAt, {
1156
+ event: "turn:start",
1157
+ sessionId: this.session.id,
1158
+ turn
1159
+ });
1160
+ return turn;
1161
+ }
1162
+ ensureTurn(capturedAt) {
1163
+ return this.currentTurn ?? this.startTurn(capturedAt);
1164
+ }
1165
+ endTurn(status, capturedAt) {
1166
+ const turn = this.currentTurn;
1167
+ if (!turn) {
1168
+ return;
1169
+ }
1170
+ turn.status = status;
1171
+ turn.endedAt = new Date(capturedAt).toISOString();
1172
+ this.emitEvent(capturedAt, {
1173
+ event: "turn:end",
1174
+ sessionId: this.session.id,
1175
+ turnId: turn.id,
1176
+ status
1177
+ });
1178
+ this.currentTurn = null;
1179
+ this.toolBlockMap.clear();
1180
+ this.questionBlockMap.clear();
1181
+ this.blockById.clear();
1182
+ this.activeStreamBlocks.clear();
1183
+ this.sawStreamTextThisTurn = false;
1184
+ this.session.status = "idle";
1185
+ this.emitEvent(capturedAt, {
1186
+ event: "session:update",
1187
+ session: { ...this.session }
1188
+ });
1189
+ }
1190
+ startBlock(turn, capturedAt, partial) {
1191
+ const block = {
1192
+ ...partial,
1193
+ id: partial.id || `${turn.id}:block:${this.blockIndex}`,
1194
+ turnId: turn.id,
1195
+ index: this.blockIndex
1196
+ };
1197
+ this.blockIndex += 1;
1198
+ turn.blocks.push(block);
1199
+ this.blockById.set(block.id, block);
1200
+ this.emitEvent(capturedAt, {
1201
+ event: "block:start",
1202
+ sessionId: this.session.id,
1203
+ turnId: turn.id,
1204
+ block
1205
+ });
1206
+ return block;
1207
+ }
1208
+ appendTextDelta(capturedAt, turn, block, text) {
1209
+ if (!text) {
1210
+ return;
1211
+ }
1212
+ block.text += text;
1213
+ block.status = "streaming";
1214
+ this.emitEvent(capturedAt, {
1215
+ event: "block:delta",
1216
+ sessionId: this.session.id,
1217
+ turnId: turn.id,
1218
+ blockId: block.id,
1219
+ text
1220
+ });
1221
+ }
1222
+ emitBlockEnd(capturedAt, turn, block, status) {
1223
+ block.status = status;
1224
+ this.emitEvent(capturedAt, {
1225
+ event: "block:end",
1226
+ sessionId: this.session.id,
1227
+ turnId: turn.id,
1228
+ blockId: block.id,
1229
+ status
1230
+ });
1231
+ }
1232
+ completeOpenStreamBlocks(capturedAt, turn) {
1233
+ for (const block of this.activeStreamBlocks.values()) {
1234
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
1235
+ }
1236
+ this.activeStreamBlocks.clear();
1237
+ }
1238
+ emitError(turn, capturedAt, message) {
1239
+ const block = this.startBlock(turn, capturedAt, {
1240
+ type: "error",
1241
+ message,
1242
+ status: "completed"
1243
+ });
1244
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
1245
+ }
1246
+ emitEvent(capturedAt, event) {
1247
+ this.events.push({
1248
+ capturedAt,
1249
+ event: structuredClone(event)
1250
+ });
1251
+ }
1252
+ }
1253
+ function supportsHistorySessionSnapshotForPath(path, adapterType) {
1254
+ return inferHistoryAdapterType(path, adapterType) === "claude-code";
1255
+ }
1256
+ function createHistorySessionSnapshot(input) {
1257
+ const adapterType = inferHistoryAdapterType(input.path, input.adapterType);
1258
+ if (adapterType !== "claude-code") {
1259
+ throw new Error(`History snapshot is not supported for adapter type "${adapterType}".`);
1260
+ }
1261
+ const session = buildBaseHistorySession(input, adapterType);
1262
+ const baseTimestampMs = normalizeTimestamp(input.baseTimestampMs) ?? Date.now();
1263
+ const parser = new ClaudeCodeHistoryParser(session, baseTimestampMs);
1264
+ const replay = parser.parse(input.content);
1265
+ const tracker = new StateTracker;
1266
+ tracker.createSession(session.id, session);
1267
+ for (const entry of replay.events) {
1268
+ tracker.trackEvent(session.id, entry.event, entry.capturedAt);
1269
+ }
1270
+ const snapshot = tracker.getSessionState(session.id);
1271
+ if (!snapshot) {
1272
+ throw new Error("Failed to reconstruct session snapshot from history.");
1273
+ }
1274
+ return {
1275
+ adapterType,
1276
+ lineCount: replay.lineCount,
1277
+ parsedLineCount: replay.parsedLineCount,
1278
+ skippedLineCount: replay.skippedLineCount,
1279
+ events: replay.events,
1280
+ snapshot
1281
+ };
1282
+ }
1283
+ var init_history = () => {};
1284
+
580
1285
  // packages/agent-sessions/src/adapters/claude-code.ts
581
1286
  import { existsSync, readdirSync, statSync } from "fs";
582
1287
  import { homedir } from "os";
@@ -602,7 +1307,7 @@ function resolveClaudeResumeContext(config) {
602
1307
  if (!existsSync(sessionPath)) {
603
1308
  continue;
604
1309
  }
605
- const cwd = decodeClaudeProjectsSlug(slug);
1310
+ const cwd = decodeClaudeProjectsSlug2(slug);
606
1311
  if (!cwd) {
607
1312
  continue;
608
1313
  }
@@ -621,7 +1326,7 @@ function resolveClaudeResumeContext(config) {
621
1326
  }
622
1327
  return null;
623
1328
  }
624
- function decodeClaudeProjectsSlug(slug) {
1329
+ function decodeClaudeProjectsSlug2(slug) {
625
1330
  if (!slug.startsWith("-")) {
626
1331
  return null;
627
1332
  }
@@ -1142,7 +1847,7 @@ Referenced files: ${prompt.files.join(", ")}` });
1142
1847
  // packages/agent-sessions/src/codex-launch-config.ts
1143
1848
  import { accessSync, constants, existsSync as existsSync2 } from "fs";
1144
1849
  import { homedir as homedir2 } from "os";
1145
- import { basename, delimiter, dirname, join as join2, resolve } from "path";
1850
+ import { basename as basename2, delimiter, dirname as dirname2, join as join2, resolve } from "path";
1146
1851
  import { fileURLToPath } from "url";
1147
1852
  function isExecutable(filePath) {
1148
1853
  if (!filePath) {
@@ -1163,7 +1868,7 @@ function ancestorChain(start) {
1163
1868
  let current = resolve(start);
1164
1869
  while (true) {
1165
1870
  chain.push(current);
1166
- const parent = dirname(current);
1871
+ const parent = dirname2(current);
1167
1872
  if (parent === current) {
1168
1873
  return chain;
1169
1874
  }
@@ -1199,7 +1904,7 @@ function resolveBunExecutable(env) {
1199
1904
  return candidate;
1200
1905
  }
1201
1906
  }
1202
- if (basename(process.execPath).startsWith("bun") && isExecutable(process.execPath)) {
1907
+ if (basename2(process.execPath).startsWith("bun") && isExecutable(process.execPath)) {
1203
1908
  return process.execPath;
1204
1909
  }
1205
1910
  return resolveExecutableFromSearchPath(["bun"], env);
@@ -1219,7 +1924,7 @@ function resolveScoutExecutable(env) {
1219
1924
  return resolveExecutableFromSearchPath(["scout"], env);
1220
1925
  }
1221
1926
  function resolveRepoScoutScript(currentDirectory) {
1222
- const moduleDirectory = dirname(fileURLToPath(import.meta.url));
1927
+ const moduleDirectory = dirname2(fileURLToPath(import.meta.url));
1223
1928
  const starts = uniquePaths([currentDirectory, moduleDirectory]);
1224
1929
  for (const start of starts) {
1225
1930
  for (const candidate of ancestorChain(start)) {
@@ -3394,6 +4099,7 @@ var init_echo = __esm(() => {
3394
4099
  var init_src = __esm(() => {
3395
4100
  init_registry();
3396
4101
  init_registry();
4102
+ init_history();
3397
4103
  init_claude_code();
3398
4104
  init_codex();
3399
4105
  init_openai_compat();
@@ -4795,17 +5501,127 @@ var init_mesh = () => {};
4795
5501
 
4796
5502
  // packages/protocol/dist/conversations.js
4797
5503
  var init_conversations = () => {};
4798
- // packages/protocol/dist/messages.js
4799
- var init_messages = () => {};
4800
5504
 
4801
- // packages/protocol/dist/invocations.js
4802
- var init_invocations = () => {};
4803
-
4804
- // packages/protocol/dist/scout-dispatch.js
4805
- var init_scout_dispatch = () => {};
4806
-
4807
- // packages/protocol/dist/deliveries.js
4808
- var init_deliveries = () => {};
5505
+ // packages/protocol/dist/collaboration.js
5506
+ function isQuestionTerminalState(state) {
5507
+ return state === "closed" || state === "declined";
5508
+ }
5509
+ function isWorkItemTerminalState(state) {
5510
+ return state === "done" || state === "cancelled";
5511
+ }
5512
+ function collaborationRequiresNextMoveOwner(record) {
5513
+ if (record.kind === "question") {
5514
+ return !isQuestionTerminalState(record.state);
5515
+ }
5516
+ return !isWorkItemTerminalState(record.state);
5517
+ }
5518
+ function collaborationRequiresOwner(record) {
5519
+ return record.kind === "work_item" && !isWorkItemTerminalState(record.state);
5520
+ }
5521
+ function collaborationRequiresWaitingOn(record) {
5522
+ return record.kind === "work_item" && record.state === "waiting";
5523
+ }
5524
+ function collaborationRequiresAcceptance(record) {
5525
+ if (record.acceptanceState === "none") {
5526
+ return false;
5527
+ }
5528
+ if (record.kind === "question") {
5529
+ return Boolean(record.askedById && record.askedOfId);
5530
+ }
5531
+ return Boolean(record.requestedById);
5532
+ }
5533
+ function validateCollaborationRecord(record) {
5534
+ const errors = [];
5535
+ if (!record.id.trim()) {
5536
+ errors.push("collaboration record id is required");
5537
+ }
5538
+ if (!record.title.trim()) {
5539
+ errors.push("collaboration title is required");
5540
+ }
5541
+ if (!record.createdById.trim()) {
5542
+ errors.push("createdById is required");
5543
+ }
5544
+ if (record.parentId && record.parentId === record.id) {
5545
+ errors.push("parentId cannot reference the record itself");
5546
+ }
5547
+ if (record.createdAt > record.updatedAt) {
5548
+ errors.push("updatedAt must be greater than or equal to createdAt");
5549
+ }
5550
+ if (collaborationRequiresOwner(record) && !record.ownerId) {
5551
+ errors.push("non-terminal work items require ownerId");
5552
+ }
5553
+ if (collaborationRequiresNextMoveOwner(record) && !record.nextMoveOwnerId) {
5554
+ errors.push("non-terminal collaboration records require nextMoveOwnerId");
5555
+ }
5556
+ if (record.kind === "work_item" && collaborationRequiresWaitingOn(record) && !record.waitingOn) {
5557
+ errors.push("waiting work items require waitingOn");
5558
+ }
5559
+ if (record.kind === "question") {
5560
+ if (record.spawnedWorkItemId && record.spawnedWorkItemId === record.id) {
5561
+ errors.push("question spawnedWorkItemId cannot reference the question itself");
5562
+ }
5563
+ } else if (record.waitingOn?.targetId && record.waitingOn.targetId === record.id) {
5564
+ errors.push("waitingOn.targetId cannot reference the work item itself");
5565
+ }
5566
+ if (record.acceptanceState !== "none" && !collaborationRequiresAcceptance(record)) {
5567
+ errors.push("acceptanceState requires the corresponding requester and reviewer identities");
5568
+ }
5569
+ return errors;
5570
+ }
5571
+ function assertValidCollaborationRecord(record) {
5572
+ const errors = validateCollaborationRecord(record);
5573
+ if (errors.length > 0) {
5574
+ throw new Error(errors.join("; "));
5575
+ }
5576
+ }
5577
+ function validateCollaborationEvent(event, record) {
5578
+ const errors = [];
5579
+ if (!event.id.trim()) {
5580
+ errors.push("collaboration event id is required");
5581
+ }
5582
+ if (!event.recordId.trim()) {
5583
+ errors.push("collaboration event recordId is required");
5584
+ }
5585
+ if (!event.actorId.trim()) {
5586
+ errors.push("collaboration event actorId is required");
5587
+ }
5588
+ if (record) {
5589
+ if (record.id !== event.recordId) {
5590
+ errors.push("collaboration event recordId does not match the target record");
5591
+ }
5592
+ if (record.kind !== event.recordKind) {
5593
+ errors.push("collaboration event recordKind does not match the target record");
5594
+ }
5595
+ }
5596
+ if (event.kind === "answered" && event.recordKind !== "question") {
5597
+ errors.push("answered events only apply to questions");
5598
+ }
5599
+ if (event.kind === "declined" && event.recordKind !== "question") {
5600
+ errors.push("declined events only apply to questions");
5601
+ }
5602
+ if ((event.kind === "waiting" || event.kind === "progressed" || event.kind === "review_requested" || event.kind === "done" || event.kind === "cancelled") && event.recordKind !== "work_item") {
5603
+ errors.push(`${event.kind} events only apply to work items`);
5604
+ }
5605
+ return errors;
5606
+ }
5607
+ function assertValidCollaborationEvent(event, record) {
5608
+ const errors = validateCollaborationEvent(event, record);
5609
+ if (errors.length > 0) {
5610
+ throw new Error(errors.join("; "));
5611
+ }
5612
+ }
5613
+
5614
+ // packages/protocol/dist/messages.js
5615
+ var init_messages = () => {};
5616
+
5617
+ // packages/protocol/dist/invocations.js
5618
+ var init_invocations = () => {};
5619
+
5620
+ // packages/protocol/dist/scout-dispatch.js
5621
+ var init_scout_dispatch = () => {};
5622
+
5623
+ // packages/protocol/dist/deliveries.js
5624
+ var init_deliveries = () => {};
4809
5625
 
4810
5626
  // packages/protocol/dist/transports.js
4811
5627
  var init_transports = () => {};
@@ -4886,9 +5702,9 @@ var init_support_paths = () => {};
4886
5702
  // packages/runtime/src/harness-catalog.ts
4887
5703
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
4888
5704
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
4889
- import { dirname as dirname2, join as join5 } from "path";
5705
+ import { dirname as dirname3, join as join5 } from "path";
4890
5706
  async function writeHarnessCatalogOverrides(overrides, overridePath = resolveOpenScoutSupportPaths().harnessCatalogPath) {
4891
- await mkdir2(dirname2(overridePath), { recursive: true });
5707
+ await mkdir2(dirname3(overridePath), { recursive: true });
4892
5708
  const payload = {
4893
5709
  version: HARNESS_CATALOG_VERSION,
4894
5710
  entries: overrides,
@@ -4984,9 +5800,9 @@ var init_harness_catalog = __esm(() => {
4984
5800
  // packages/runtime/src/user-project-hints.ts
4985
5801
  import { readdir, readFile as readFile3, stat } from "fs/promises";
4986
5802
  import { homedir as homedir5 } from "os";
4987
- import { dirname as dirname3, join as join6, resolve as resolve2 } from "path";
5803
+ import { dirname as dirname4, join as join6, resolve as resolve2 } from "path";
4988
5804
  import { fileURLToPath as fileURLToPath2 } from "url";
4989
- function decodeClaudeProjectsSlug2(name) {
5805
+ function decodeClaudeProjectsSlug3(name) {
4990
5806
  if (!name || name === "." || name === "..") {
4991
5807
  return null;
4992
5808
  }
@@ -5011,7 +5827,7 @@ async function resolveExistingDirectoryHint(pathLike) {
5011
5827
  return absolutePath;
5012
5828
  }
5013
5829
  if (info.isFile()) {
5014
- return dirname3(absolutePath);
5830
+ return dirname4(absolutePath);
5015
5831
  }
5016
5832
  } catch {
5017
5833
  return null;
@@ -5038,7 +5854,7 @@ async function findLikelyProjectRoot(absolutePath) {
5038
5854
  }
5039
5855
  }
5040
5856
  }
5041
- const parent = dirname3(current);
5857
+ const parent = dirname4(current);
5042
5858
  if (parent === current) {
5043
5859
  return lastCandidate ?? absolutePath;
5044
5860
  }
@@ -5179,7 +5995,7 @@ async function appendCursorWorkspacePaths(home, roots) {
5179
5995
  }
5180
5996
  const wsRaw = await readFile3(wsFile, "utf8");
5181
5997
  const wsDoc = JSON.parse(wsRaw);
5182
- const wsDir = dirname3(wsFile);
5998
+ const wsDir = dirname4(wsFile);
5183
5999
  for (const f of wsDoc.folders ?? []) {
5184
6000
  if (typeof f.path !== "string" || !f.path) {
5185
6001
  continue;
@@ -5200,7 +6016,7 @@ async function appendClaudeProjectSlugs(home, roots) {
5200
6016
  return;
5201
6017
  }
5202
6018
  for (const name of names) {
5203
- const decoded = decodeClaudeProjectsSlug2(name);
6019
+ const decoded = decodeClaudeProjectsSlug3(name);
5204
6020
  if (!decoded) {
5205
6021
  continue;
5206
6022
  }
@@ -5336,7 +6152,7 @@ import { execFileSync } from "child_process";
5336
6152
  import { existsSync as existsSync5, readFileSync } from "fs";
5337
6153
  import { access as access2, mkdir as mkdir3, readdir as readdir2, readFile as readFile4, realpath, rm as rm2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
5338
6154
  import { homedir as homedir6, hostname, userInfo } from "os";
5339
- import { basename as basename2, dirname as dirname4, isAbsolute, join as join7, relative, resolve as resolve3 } from "path";
6155
+ import { basename as basename3, dirname as dirname5, isAbsolute, join as join7, relative, resolve as resolve3 } from "path";
5340
6156
  import { fileURLToPath as fileURLToPath3 } from "url";
5341
6157
  function titleCaseWords(value) {
5342
6158
  return value.split(/[\s._-]+/).map((part) => part.trim()).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
@@ -5811,7 +6627,7 @@ async function readJsonFile(filePath) {
5811
6627
  }
5812
6628
  }
5813
6629
  async function writeJsonFile(filePath, value) {
5814
- await mkdir3(dirname4(filePath), { recursive: true });
6630
+ await mkdir3(dirname5(filePath), { recursive: true });
5815
6631
  await writeFile3(filePath, JSON.stringify(value, null, 2) + `
5816
6632
  `, "utf8");
5817
6633
  }
@@ -6056,7 +6872,7 @@ function applyImportedEnvironmentSeeds(config, codexImport, projectRoot) {
6056
6872
  ...(config.environment?.actions?.length ?? 0) === 0 && importedEnvironment.actions ? { actions: importedEnvironment.actions } : {}
6057
6873
  };
6058
6874
  }
6059
- const defaultProjectName = titleCase(basename2(projectRoot));
6875
+ const defaultProjectName = titleCase(basename3(projectRoot));
6060
6876
  if (codexImport?.name?.trim() && (!config.project.name?.trim() || config.project.name === defaultProjectName)) {
6061
6877
  next.project = {
6062
6878
  ...config.project,
@@ -6224,7 +7040,7 @@ async function seedWorkspaceRoots(options) {
6224
7040
  const roots = new Set;
6225
7041
  const currentProjectRoot = options.currentDirectory ? await findNearestProjectRoot(options.currentDirectory) : null;
6226
7042
  if (currentProjectRoot) {
6227
- roots.add(dirname4(currentProjectRoot));
7043
+ roots.add(dirname5(currentProjectRoot));
6228
7044
  }
6229
7045
  const legacyRoot = options.legacyRelayConfig?.projectRoot?.trim();
6230
7046
  if (legacyRoot) {
@@ -6232,7 +7048,7 @@ async function seedWorkspaceRoots(options) {
6232
7048
  }
6233
7049
  for (const record of Object.values(options.legacyAgents ?? {})) {
6234
7050
  if (record.cwd?.trim()) {
6235
- roots.add(dirname4(normalizePath(record.cwd)));
7051
+ roots.add(dirname5(normalizePath(record.cwd)));
6236
7052
  }
6237
7053
  }
6238
7054
  if (roots.size === 0) {
@@ -6323,7 +7139,7 @@ async function installScoutSkillToHarnesses() {
6323
7139
  continue;
6324
7140
  }
6325
7141
  try {
6326
- await mkdir3(dirname4(target), { recursive: true });
7142
+ await mkdir3(dirname5(target), { recursive: true });
6327
7143
  await writeFile3(target, content, "utf8");
6328
7144
  entries.push({ harness, target, status: "installed" });
6329
7145
  } catch (error) {
@@ -6424,7 +7240,7 @@ async function findNearestProjectRoot(startDirectory) {
6424
7240
  if (await pathExists2(projectConfigPath(current)) || await pathExists2(join7(current, "package.json")) || await pathExists2(join7(current, "AGENTS.md")) || await pathExists2(join7(current, "CLAUDE.md"))) {
6425
7241
  lastCandidate = current;
6426
7242
  }
6427
- const parent = dirname4(current);
7243
+ const parent = dirname5(current);
6428
7244
  if (parent === current) {
6429
7245
  return lastCandidate;
6430
7246
  }
@@ -6432,7 +7248,7 @@ async function findNearestProjectRoot(startDirectory) {
6432
7248
  }
6433
7249
  }
6434
7250
  function defaultProjectConfig(projectRoot, settings, preferredHarness) {
6435
- const projectName = basename2(projectRoot);
7251
+ const projectName = basename3(projectRoot);
6436
7252
  const definitionId = normalizeAgentId(projectName);
6437
7253
  const relativeRoot = relative(projectRoot, projectRoot) || ".";
6438
7254
  return {
@@ -6738,8 +7554,8 @@ function selectorFromInput(value) {
6738
7554
  }
6739
7555
  async function resolveManifestBackedAgent(projectRoot, config, settings, override) {
6740
7556
  const resolvedProjectRoot = resolveProjectRootFromConfig(projectRoot, config);
6741
- const projectName = config.project.name?.trim() || titleCase(config.project.id || basename2(resolvedProjectRoot));
6742
- const fallbackDefinitionId = normalizeAgentId(config.project.id || basename2(resolvedProjectRoot));
7557
+ const projectName = config.project.name?.trim() || titleCase(config.project.id || basename3(resolvedProjectRoot));
7558
+ const fallbackDefinitionId = normalizeAgentId(config.project.id || basename3(resolvedProjectRoot));
6743
7559
  const definitionId = normalizeAgentId(config.agent?.id || override?.definitionId || fallbackDefinitionId);
6744
7560
  const detectedHarness = await detectPreferredHarness(resolvedProjectRoot, settings.agents.defaultHarness);
6745
7561
  const runtimeDefaults = config.agent?.runtime?.defaults;
@@ -6776,7 +7592,7 @@ async function resolveManifestBackedAgent(projectRoot, config, settings, overrid
6776
7592
  return mergeResolvedAgentConfig(base, override, settings);
6777
7593
  }
6778
7594
  async function resolveInferredAgent(projectRoot, settings, override) {
6779
- const projectName = basename2(projectRoot);
7595
+ const projectName = basename3(projectRoot);
6780
7596
  const definitionId = normalizeAgentId(override?.definitionId?.trim() || projectName);
6781
7597
  const detectedHarness = await detectPreferredHarness(projectRoot, settings.agents.defaultHarness);
6782
7598
  const defaultHarness = normalizeManagedHarness(override?.defaultHarness ?? override?.runtime?.harness, normalizeManagedHarness(detectedHarness, "claude"));
@@ -6812,7 +7628,7 @@ async function resolveInferredAgent(projectRoot, settings, override) {
6812
7628
  return mergeResolvedAgentConfig(base, override, settings);
6813
7629
  }
6814
7630
  async function buildProjectInventoryEntry(agent, sourceRoot) {
6815
- const manifestRoot = agent.projectConfigPath ? normalizePath(join7(dirname4(agent.projectConfigPath), "..")) : null;
7631
+ const manifestRoot = agent.projectConfigPath ? normalizePath(join7(dirname5(agent.projectConfigPath), "..")) : null;
6816
7632
  const manifest = manifestRoot ? await readProjectConfig(manifestRoot) : null;
6817
7633
  const markers = await detectHarnessMarkers(agent.projectRoot);
6818
7634
  const harnesses = new Map;
@@ -7205,7 +8021,7 @@ var init_setup = __esm(() => {
7205
8021
  PROJECT_STRONG_MARKERS_FLAT_NESTED = partitionFlatAndNestedMarkers(PROJECT_STRONG_MARKERS);
7206
8022
  PROJECT_WEAK_MARKERS_FLAT_NESTED = partitionFlatAndNestedMarkers(PROJECT_WEAK_MARKERS);
7207
8023
  gitBranchCache = new Map;
7208
- SETUP_MODULE_DIRECTORY = dirname4(fileURLToPath3(import.meta.url));
8024
+ SETUP_MODULE_DIRECTORY = dirname5(fileURLToPath3(import.meta.url));
7209
8025
  SCOUT_SKILL_REPO_ROOT = resolve3(SETUP_MODULE_DIRECTORY, "..", "..", "..");
7210
8026
  SCOUT_SKILL_INSTALL_PATHS = {
7211
8027
  claude: join7(homedir6(), ".claude", "skills", "scout", SCOUT_SKILL_FILE_NAME),
@@ -7290,7 +8106,7 @@ function updateBlockCompletion(blockState, completed) {
7290
8106
  blockState.status = completed ? "completed" : "streaming";
7291
8107
  blockState.block.status = completed ? "completed" : "streaming";
7292
8108
  }
7293
- function parseQuestionAnswer(value) {
8109
+ function parseQuestionAnswer2(value) {
7294
8110
  if (Array.isArray(value)) {
7295
8111
  return value.map((entry) => typeof entry === "string" ? entry.trim() : String(entry).trim()).filter(Boolean);
7296
8112
  }
@@ -7608,7 +8424,7 @@ function buildClaudeStreamJsonSessionSnapshot(raw2, options, targetClaudeSession
7608
8424
  if (blockState.block.type === "question") {
7609
8425
  const questionBlock = blockState.block;
7610
8426
  questionBlock.questionStatus = result.is_error ? "denied" : "answered";
7611
- const answer = parseQuestionAnswer(result.content);
8427
+ const answer = parseQuestionAnswer2(result.content);
7612
8428
  questionBlock.answer = answer.length > 0 ? answer : undefined;
7613
8429
  updateBlockCompletion(blockState, true);
7614
8430
  continue;
@@ -9621,7 +10437,7 @@ function buildInvocationCollaborationContextPrompt(invocation) {
9621
10437
  import { spawnSync } from "child_process";
9622
10438
  import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
9623
10439
  import { homedir as homedir11 } from "os";
9624
- import { basename as basename3, dirname as dirname9, join as join15, resolve as resolve5 } from "path";
10440
+ import { basename as basename4, dirname as dirname10, join as join15, resolve as resolve5 } from "path";
9625
10441
  import { fileURLToPath as fileURLToPath5 } from "url";
9626
10442
  function isTmpPath(p) {
9627
10443
  return /^\/(?:private\/)?tmp\//.test(p);
@@ -9654,7 +10470,7 @@ function runtimePackageDir() {
9654
10470
  const fromCwd = findWorkspaceRuntimeDir(process.cwd());
9655
10471
  if (fromCwd)
9656
10472
  return fromCwd;
9657
- const moduleDir = dirname9(fileURLToPath5(import.meta.url));
10473
+ const moduleDir = dirname10(fileURLToPath5(import.meta.url));
9658
10474
  return resolve5(moduleDir, "..");
9659
10475
  }
9660
10476
  function isInstalledRuntimePackageDir(candidate) {
@@ -9692,7 +10508,7 @@ function findWorkspaceRuntimeDir(startDir) {
9692
10508
  if (existsSync9(join15(candidate, "package.json")) && existsSync9(join15(candidate, "src"))) {
9693
10509
  return candidate;
9694
10510
  }
9695
- const parent = dirname9(current);
10511
+ const parent = dirname10(current);
9696
10512
  if (parent === current)
9697
10513
  return null;
9698
10514
  current = parent;
@@ -9703,7 +10519,7 @@ function resolveBunExecutable2() {
9703
10519
  if (explicit && explicit.trim().length > 0) {
9704
10520
  return explicit;
9705
10521
  }
9706
- if (basename3(process.execPath).startsWith("bun") && existsSync9(process.execPath)) {
10522
+ if (basename4(process.execPath).startsWith("bun") && existsSync9(process.execPath)) {
9707
10523
  return process.execPath;
9708
10524
  }
9709
10525
  const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
@@ -9856,8 +10672,8 @@ function collectOptionalEnvVars(keys) {
9856
10672
  }
9857
10673
  function resolveLaunchAgentPATH() {
9858
10674
  const entries = [
9859
- ...(process.env.PATH ?? "").split(":").filter(Boolean),
9860
10675
  join15(homedir11(), ".bun", "bin"),
10676
+ ...(process.env.PATH ?? "").split(":").filter(Boolean),
9861
10677
  "/opt/homebrew/bin",
9862
10678
  "/usr/local/bin",
9863
10679
  "/usr/bin",
@@ -9871,7 +10687,7 @@ function xmlEscape(value) {
9871
10687
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
9872
10688
  }
9873
10689
  function ensureParentDirectory(filePath) {
9874
- mkdirSync5(dirname9(filePath), { recursive: true });
10690
+ mkdirSync5(dirname10(filePath), { recursive: true });
9875
10691
  }
9876
10692
  function ensureServiceDirectories(config) {
9877
10693
  ensureOpenScoutCleanSlateSync();
@@ -10128,7 +10944,7 @@ var DEFAULT_BROKER_HOST = "127.0.0.1", DEFAULT_BROKER_HOST_MESH = "0.0.0.0", DEF
10128
10944
  var init_broker_service = __esm(async () => {
10129
10945
  init_support_paths();
10130
10946
  DEFAULT_BROKER_URL = buildDefaultBrokerUrl();
10131
- if (process.argv[1] && fileURLToPath5(import.meta.url) === process.argv[1]) {
10947
+ if (process.argv[1] && fileURLToPath5(import.meta.url) === process.argv[1] && !process.argv[1].endsWith("/main.mjs")) {
10132
10948
  await main();
10133
10949
  }
10134
10950
  });
@@ -10184,7 +11000,7 @@ import { randomUUID as randomUUID2 } from "crypto";
10184
11000
  import { execFileSync as execFileSync2, execSync } from "child_process";
10185
11001
  import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
10186
11002
  import { mkdir as mkdir6, rm as rm5, stat as stat4, writeFile as writeFile6 } from "fs/promises";
10187
- import { basename as basename4, dirname as dirname10, join as join16, resolve as resolve6 } from "path";
11003
+ import { basename as basename5, dirname as dirname11, join as join16, resolve as resolve6 } from "path";
10188
11004
  import { fileURLToPath as fileURLToPath6 } from "url";
10189
11005
  function resolveRelayHub() {
10190
11006
  return resolveOpenScoutSupportPaths().relayHubDirectory;
@@ -10196,13 +11012,13 @@ function resolveProjectsRoot(projectPath) {
10196
11012
  try {
10197
11013
  const supportPaths = resolveOpenScoutSupportPaths();
10198
11014
  if (!existsSync10(supportPaths.settingsPath)) {
10199
- return dirname10(projectPath);
11015
+ return dirname11(projectPath);
10200
11016
  }
10201
11017
  const raw2 = JSON.parse(readFileSync6(supportPaths.settingsPath, "utf8"));
10202
11018
  const workspaceRoot = raw2.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
10203
- return workspaceRoot ? resolve6(workspaceRoot) : dirname10(projectPath);
11019
+ return workspaceRoot ? resolve6(workspaceRoot) : dirname11(projectPath);
10204
11020
  } catch {
10205
- return dirname10(projectPath);
11021
+ return dirname11(projectPath);
10206
11022
  }
10207
11023
  }
10208
11024
  function resolveBrokerUrl() {
@@ -10629,7 +11445,7 @@ function recordForHarness(record, harnessOverride) {
10629
11445
  function normalizeLocalAgentRecord(agentId, record) {
10630
11446
  const cwd = normalizeProjectPath(record.cwd || process.cwd());
10631
11447
  const projectRoot = normalizeProjectPath(record.projectRoot || cwd);
10632
- const project = record.project?.trim() || basename4(projectRoot);
11448
+ const project = record.project?.trim() || basename5(projectRoot);
10633
11449
  const definitionId = record.definitionId?.trim() || agentId;
10634
11450
  const defaultHarness = activeLocalHarness(record);
10635
11451
  const harnessProfiles = normalizeLocalHarnessProfiles(agentId, {
@@ -10694,7 +11510,7 @@ function localAgentRecordFromResolvedConfig(config) {
10694
11510
  function localAgentRecordFromRelayAgentOverride(agentId, override) {
10695
11511
  return normalizeLocalAgentRecord(agentId, {
10696
11512
  definitionId: override.definitionId ?? agentId,
10697
- project: override.projectName ?? basename4(override.projectRoot || override.runtime?.cwd || agentId),
11513
+ project: override.projectName ?? basename5(override.projectRoot || override.runtime?.cwd || agentId),
10698
11514
  projectRoot: override.projectRoot ?? override.runtime?.cwd,
10699
11515
  tmuxSession: override.runtime?.sessionId ?? `relay-${agentId}`,
10700
11516
  cwd: override.runtime?.cwd ?? override.projectRoot,
@@ -11135,7 +11951,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11135
11951
  return normalizedRecord;
11136
11952
  }
11137
11953
  const projectPath = normalizedRecord.cwd;
11138
- const projectName = normalizedRecord.project || basename4(projectPath);
11954
+ const projectName = normalizedRecord.project || basename5(projectPath);
11139
11955
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
11140
11956
  const systemPrompt = renderLocalAgentSystemPromptTemplate(systemPromptTemplate, buildLocalAgentTemplateContext(agentName, projectName, projectPath), { transport: normalizedRecord.transport });
11141
11957
  const agentRuntimeDir = relayAgentRuntimeDirectory(agentName);
@@ -11357,7 +12173,7 @@ async function resolveLocalAgentIdentity(input) {
11357
12173
  nodeQualifier: instance2.nodeQualifier,
11358
12174
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11359
12175
  projectRoot: normalizeProjectPath(override.projectRoot),
11360
- projectName: override.projectName ?? basename4(override.projectRoot),
12176
+ projectName: override.projectName ?? basename5(override.projectRoot),
11361
12177
  source: "existing"
11362
12178
  };
11363
12179
  }
@@ -11376,12 +12192,12 @@ async function resolveLocalAgentIdentity(input) {
11376
12192
  nodeQualifier: instance2.nodeQualifier,
11377
12193
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11378
12194
  projectRoot: normalizeProjectPath(override.projectRoot),
11379
- projectName: override.projectName ?? basename4(override.projectRoot),
12195
+ projectName: override.projectName ?? basename5(override.projectRoot),
11380
12196
  source: "existing"
11381
12197
  };
11382
12198
  }
11383
12199
  const configDefinitionId = config?.agent?.id?.trim() ? normalizeAgentSelectorSegment(config.agent.id.trim()) : "";
11384
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12200
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11385
12201
  const configDisplayName = config?.agent?.displayName?.trim() || "";
11386
12202
  const displayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11387
12203
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11395,7 +12211,7 @@ async function resolveLocalAgentIdentity(input) {
11395
12211
  nodeQualifier: instance.nodeQualifier,
11396
12212
  harness: effectiveHarness,
11397
12213
  projectRoot,
11398
- projectName: basename4(projectRoot),
12214
+ projectName: basename5(projectRoot),
11399
12215
  source: configDefinitionId || configDisplayName ? "config" : "new"
11400
12216
  };
11401
12217
  }
@@ -11451,7 +12267,7 @@ async function startLocalAgent(input) {
11451
12267
  }
11452
12268
  if (!matchingOverride) {
11453
12269
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
11454
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12270
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11455
12271
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
11456
12272
  const effectiveDisplayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11457
12273
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11467,7 +12283,7 @@ async function startLocalAgent(input) {
11467
12283
  agentId: instance.id,
11468
12284
  definitionId,
11469
12285
  displayName: effectiveDisplayName,
11470
- projectName: basename4(projectRoot),
12286
+ projectName: basename5(projectRoot),
11471
12287
  projectRoot,
11472
12288
  projectConfigPath: coldProjectConfigPath,
11473
12289
  source: "manual",
@@ -11503,7 +12319,7 @@ async function startLocalAgent(input) {
11503
12319
  agentId: instance.id,
11504
12320
  definitionId: requestedDefinitionId,
11505
12321
  displayName: input.displayName || titleCaseLocalAgentName(requestedDefinitionId),
11506
- projectName: matchingOverride.projectName ?? basename4(matchingProjectRoot),
12322
+ projectName: matchingOverride.projectName ?? basename5(matchingProjectRoot),
11507
12323
  projectRoot: matchingProjectRoot,
11508
12324
  projectConfigPath: matchingOverride.projectConfigPath ?? null,
11509
12325
  source: "manual",
@@ -11808,7 +12624,7 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
11808
12624
  const requestedHarness = invocation.execution?.harness;
11809
12625
  const record = existing ?? (projectRoot ? {
11810
12626
  definitionId,
11811
- project: basename4(projectRoot),
12627
+ project: basename5(projectRoot),
11812
12628
  projectRoot,
11813
12629
  tmuxSession: String(endpoint.metadata?.tmuxSession ?? `relay-${agentRuntimeId}`),
11814
12630
  cwd: projectRoot,
@@ -11888,20 +12704,20 @@ var init_local_agents = __esm(async () => {
11888
12704
  init_support_paths();
11889
12705
  init_local_agent_template();
11890
12706
  await init_broker_service();
11891
- MODULE_DIRECTORY = dirname10(fileURLToPath6(import.meta.url));
12707
+ MODULE_DIRECTORY = dirname11(fileURLToPath6(import.meta.url));
11892
12708
  OPENSCOUT_REPO_ROOT = resolve6(MODULE_DIRECTORY, "..", "..", "..");
11893
12709
  DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
11894
12710
  SUPPORTED_LOCAL_AGENT_HARNESSES = ["claude", "codex"];
11895
12711
  });
11896
12712
 
11897
12713
  // packages/web/server/index.ts
11898
- import { existsSync as existsSync13 } from "fs";
11899
- import { dirname as dirname12, join as join20, resolve as resolve9 } from "path";
12714
+ import { existsSync as existsSync14 } from "fs";
12715
+ import { dirname as dirname13, join as join21, resolve as resolve10 } from "path";
11900
12716
  import { fileURLToPath as fileURLToPath8 } from "url";
11901
12717
 
11902
12718
  // packages/web/server/create-openscout-web-server.ts
11903
- import { existsSync as existsSync11, rmSync as rmSync3 } from "fs";
11904
- import { dirname as dirname11, resolve as resolve8 } from "path";
12719
+ import { existsSync as existsSync12, rmSync as rmSync3 } from "fs";
12720
+ import { dirname as dirname12, resolve as resolve9 } from "path";
11905
12721
  import { fileURLToPath as fileURLToPath7 } from "url";
11906
12722
 
11907
12723
  // node_modules/.bun/hono@4.12.10/node_modules/hono/dist/compose.js
@@ -13457,7 +14273,7 @@ import {
13457
14273
  writeFileSync as writeFileSync3
13458
14274
  } from "fs";
13459
14275
  import { homedir as homedir8 } from "os";
13460
- import { dirname as dirname6, join as join9, resolve as resolve4 } from "path";
14276
+ import { dirname as dirname7, join as join9, resolve as resolve4 } from "path";
13461
14277
  import { fileURLToPath as fileURLToPath4 } from "url";
13462
14278
 
13463
14279
  // packages/runtime/src/local-config.ts
@@ -13469,7 +14285,7 @@ import {
13469
14285
  writeFileSync as writeFileSync2
13470
14286
  } from "fs";
13471
14287
  import { homedir as homedir7 } from "os";
13472
- import { dirname as dirname5, join as join8 } from "path";
14288
+ import { dirname as dirname6, join as join8 } from "path";
13473
14289
  var LOCAL_CONFIG_VERSION = 1;
13474
14290
  var DEFAULT_LOCAL_CONFIG = {
13475
14291
  version: LOCAL_CONFIG_VERSION,
@@ -13523,7 +14339,7 @@ function isValidPort(value) {
13523
14339
  }
13524
14340
  function writeLocalConfig(config) {
13525
14341
  const configPath = localConfigPath();
13526
- mkdirSync2(dirname5(configPath), { recursive: true });
14342
+ mkdirSync2(dirname6(configPath), { recursive: true });
13527
14343
  const body = JSON.stringify({ ...config, version: LOCAL_CONFIG_VERSION }, null, 2) + `
13528
14344
  `;
13529
14345
  const tmp = `${configPath}.tmp`;
@@ -13983,7 +14799,7 @@ function resolveScoutPairingRuntimeScriptPath() {
13983
14799
  throw new Error("Unable to locate the Scout pair supervisor entrypoint.");
13984
14800
  }
13985
14801
  function resolveOpenScoutWebServerDirectory(moduleUrl = import.meta.url) {
13986
- return dirname6(fileURLToPath4(moduleUrl));
14802
+ return dirname7(fileURLToPath4(moduleUrl));
13987
14803
  }
13988
14804
  function resolveOpenScoutWebPackageRoot(moduleUrl = import.meta.url) {
13989
14805
  return resolve4(resolveOpenScoutWebServerDirectory(moduleUrl), "..");
@@ -14141,6 +14957,17 @@ async function getScoutWebPairingState(currentDirectory) {
14141
14957
  async function refreshScoutWebPairingState(currentDirectory) {
14142
14958
  return readScoutPairingState(currentDirectory);
14143
14959
  }
14960
+ async function getScoutWebPairingSessionSnapshot(sessionId) {
14961
+ if (!isScoutPairingRuntimeRunning()) {
14962
+ return null;
14963
+ }
14964
+ const resolvedConfig = resolveScoutPairingConfig();
14965
+ try {
14966
+ return await withScoutPairingBridgeClient(resolvedConfig.port, async (client) => client.query("session.snapshot", { sessionId }));
14967
+ } catch {
14968
+ return null;
14969
+ }
14970
+ }
14144
14971
  async function controlScoutWebPairingService(action, currentDirectory) {
14145
14972
  return serializePairingMutation(async () => {
14146
14973
  switch (action) {
@@ -14679,7 +15506,7 @@ import { join as join12 } from "path";
14679
15506
  // packages/runtime/src/user-config.ts
14680
15507
  import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
14681
15508
  import { homedir as homedir9 } from "os";
14682
- import { dirname as dirname8, join as join11 } from "path";
15509
+ import { dirname as dirname9, join as join11 } from "path";
14683
15510
  function userConfigPath() {
14684
15511
  return join11(process.env.OPENSCOUT_HOME ?? join11(homedir9(), ".openscout"), "user.json");
14685
15512
  }
@@ -14695,7 +15522,7 @@ function loadUserConfig() {
14695
15522
  }
14696
15523
  function saveUserConfig(config) {
14697
15524
  const configPath = userConfigPath();
14698
- mkdirSync4(dirname8(configPath), { recursive: true });
15525
+ mkdirSync4(dirname9(configPath), { recursive: true });
14699
15526
  writeFileSync4(configPath, JSON.stringify(config, null, 2) + `
14700
15527
  `, "utf8");
14701
15528
  }
@@ -15478,7 +16305,10 @@ function querySessions(limit = 80) {
15478
16305
  a.id AS agent_id,
15479
16306
  ac.display_name,
15480
16307
  ep.harness,
16308
+ ep.transport,
15481
16309
  ep.project_root,
16310
+ ep.session_id,
16311
+ ep.metadata_json AS endpoint_metadata_json,
15482
16312
  a.metadata_json
15483
16313
  FROM conversation_members cm
15484
16314
  JOIN agents a ON a.id = cm.actor_id
@@ -15501,6 +16331,8 @@ function querySessions(limit = 80) {
15501
16331
  const stats = statsStmt.get(r.id);
15502
16332
  let agentName = null;
15503
16333
  let harness = null;
16334
+ let harnessSessionId = null;
16335
+ let harnessLogPath = null;
15504
16336
  let branch = null;
15505
16337
  let workspaceRoot = null;
15506
16338
  if (primaryAgent) {
@@ -15514,6 +16346,14 @@ function querySessions(limit = 80) {
15514
16346
  }
15515
16347
  branch = meta.branch ?? meta.workspaceQualifier ?? null;
15516
16348
  } catch {}
16349
+ try {
16350
+ const endpointMeta = primaryAgent.endpoint_metadata_json ? JSON.parse(primaryAgent.endpoint_metadata_json) : {};
16351
+ harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, endpointMeta);
16352
+ harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, endpointMeta);
16353
+ } catch {
16354
+ harnessSessionId = resolveHarnessSessionId(primaryAgent.transport, primaryAgent.session_id, undefined);
16355
+ harnessLogPath = resolveHarnessLogPath(primaryAgent.agent_id, primaryAgent.transport, primaryAgent.session_id, undefined);
16356
+ }
15517
16357
  }
15518
16358
  const preview = previewStmt.get(r.id)?.body ?? null;
15519
16359
  return [{
@@ -15524,6 +16364,8 @@ function querySessions(limit = 80) {
15524
16364
  agentId,
15525
16365
  agentName,
15526
16366
  harness,
16367
+ harnessSessionId,
16368
+ harnessLogPath,
15527
16369
  currentBranch: branch,
15528
16370
  preview: preview ? preview.slice(0, 200) : null,
15529
16371
  messageCount: stats?.cnt ?? 0,
@@ -15621,23 +16463,30 @@ function parseDirectConversationId(conversationId) {
15621
16463
  }
15622
16464
  function synthesizeDirectSession(conversationId, agentId, operatorId) {
15623
16465
  const agent = db().prepare(`SELECT
15624
- a.id AS agent_id,
15625
- ac.display_name,
15626
- ep.harness,
15627
- ep.project_root,
15628
- a.metadata_json
15629
- FROM agents a
15630
- JOIN actors ac ON ac.id = a.id
15631
- ${LATEST_AGENT_ENDPOINT_JOIN}
15632
- WHERE a.id = ?`).get(agentId);
16466
+ a.id AS agent_id,
16467
+ ac.display_name,
16468
+ ep.harness,
16469
+ ep.transport,
16470
+ ep.project_root,
16471
+ ep.session_id,
16472
+ ep.metadata_json AS endpoint_metadata_json,
16473
+ a.metadata_json
16474
+ FROM agents a
16475
+ JOIN actors ac ON ac.id = a.id
16476
+ ${LATEST_AGENT_ENDPOINT_JOIN}
16477
+ WHERE a.id = ?`).get(agentId);
15633
16478
  if (!agent) {
15634
16479
  return null;
15635
16480
  }
15636
16481
  let currentBranch = null;
16482
+ let endpointMeta = {};
15637
16483
  try {
15638
16484
  const metadata = agent.metadata_json ? JSON.parse(agent.metadata_json) : {};
15639
16485
  currentBranch = metadata.branch ?? metadata.workspaceQualifier ?? null;
15640
16486
  } catch {}
16487
+ try {
16488
+ endpointMeta = agent.endpoint_metadata_json ? JSON.parse(agent.endpoint_metadata_json) : {};
16489
+ } catch {}
15641
16490
  return {
15642
16491
  id: conversationId,
15643
16492
  kind: "direct",
@@ -15646,6 +16495,8 @@ function synthesizeDirectSession(conversationId, agentId, operatorId) {
15646
16495
  agentId,
15647
16496
  agentName: agent.display_name,
15648
16497
  harness: agent.harness,
16498
+ harnessSessionId: resolveHarnessSessionId(agent.transport, agent.session_id, endpointMeta),
16499
+ harnessLogPath: resolveHarnessLogPath(agentId, agent.transport, agent.session_id, endpointMeta),
15649
16500
  currentBranch,
15650
16501
  preview: null,
15651
16502
  messageCount: 0,
@@ -16026,7 +16877,7 @@ init_dist2();
16026
16877
  init_setup();
16027
16878
  init_support_paths();
16028
16879
  await init_local_agents();
16029
- import { basename as basename5, join as join17, resolve as resolve7 } from "path";
16880
+ import { basename as basename6, join as join17, resolve as resolve7 } from "path";
16030
16881
 
16031
16882
  // packages/web/server/core/broker/paths.ts
16032
16883
  var scoutBrokerPaths = {
@@ -16943,135 +17794,2913 @@ function whoEntryState(endpoints, registrationKind) {
16943
17794
  }, "offline");
16944
17795
  }
16945
17796
 
16946
- // packages/web/server/core/mesh/service.ts
16947
- await init_broker_service();
17797
+ // packages/web/server/core/observe/service.ts
17798
+ init_src();
17799
+ import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
17800
+ import { homedir as homedir12 } from "os";
17801
+ import { join as join18, resolve as resolve8 } from "path";
16948
17802
 
16949
- // packages/runtime/src/tailscale.ts
16950
- import { readFile as readFile7 } from "fs/promises";
16951
- import { execFile } from "child_process";
16952
- import { promisify } from "util";
16953
- var execFileAsync = promisify(execFile);
16954
- function parseStatusJson(raw2) {
16955
- return JSON.parse(raw2);
17803
+ // packages/runtime/src/registry.ts
17804
+ function createRuntimeRegistrySnapshot(value = {}) {
17805
+ return {
17806
+ nodes: value.nodes ?? {},
17807
+ actors: value.actors ?? {},
17808
+ agents: value.agents ?? {},
17809
+ endpoints: value.endpoints ?? {},
17810
+ conversations: value.conversations ?? {},
17811
+ bindings: value.bindings ?? {},
17812
+ messages: value.messages ?? {},
17813
+ flights: value.flights ?? {},
17814
+ collaborationRecords: value.collaborationRecords ?? {}
17815
+ };
16956
17816
  }
16957
- function parsePeers(status) {
16958
- const peers = Object.entries(status.Peer ?? {});
16959
- return peers.map(([fallbackId, peer]) => ({
16960
- id: peer.ID ?? fallbackId,
16961
- name: peer.HostName ?? peer.DNSName ?? fallbackId,
16962
- dnsName: peer.DNSName,
16963
- addresses: peer.TailscaleIPs ?? [],
16964
- online: peer.Online ?? false,
16965
- hostName: peer.HostName,
16966
- os: peer.OS,
16967
- tags: peer.Tags ?? []
16968
- }));
17817
+
17818
+ // packages/runtime/src/index.ts
17819
+ init_harness_catalog();
17820
+
17821
+ // packages/runtime/src/planner.ts
17822
+ function createDeliveryId(messageId, targetId, reason, transport) {
17823
+ return `del-${messageId}-${targetId}-${reason}-${transport}`;
16969
17824
  }
16970
- function parseSelf(status) {
16971
- const self = status.Self;
16972
- if (!self) {
16973
- return null;
17825
+ function unique(values) {
17826
+ return [...new Set(values)];
17827
+ }
17828
+ function resolveVisibilityAudience(message, conversation) {
17829
+ if (message.audience?.visibleTo?.length) {
17830
+ return unique(message.audience.visibleTo);
16974
17831
  }
17832
+ return unique(conversation.participantIds.filter((participantId) => participantId !== message.actorId));
17833
+ }
17834
+ function resolveNotifyAudience(message) {
17835
+ const fromMentions = (message.mentions ?? []).map((mention) => mention.actorId);
17836
+ const explicit = message.audience?.notify ?? [];
17837
+ return unique([...fromMentions, ...explicit].filter((actorId) => actorId !== message.actorId));
17838
+ }
17839
+ function resolveInvocationAudience(message) {
17840
+ return unique((message.audience?.invoke ?? []).filter((actorId) => actorId !== message.actorId));
17841
+ }
17842
+ function planTargetDelivery(message, route, reason, policy) {
16975
17843
  return {
16976
- id: self.ID ?? self.DNSName ?? self.HostName ?? "self",
16977
- name: self.HostName ?? self.DNSName ?? "self",
16978
- dnsName: self.DNSName,
16979
- addresses: self.TailscaleIPs ?? [],
16980
- online: self.Online ?? true,
16981
- hostName: self.HostName,
16982
- os: self.OS,
16983
- tailnetName: status.CurrentTailnet?.Name,
16984
- magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
17844
+ id: createDeliveryId(message.id, route.targetId, reason, route.transport),
17845
+ messageId: message.id,
17846
+ targetId: route.targetId,
17847
+ targetNodeId: route.nodeId,
17848
+ targetKind: route.targetKind,
17849
+ transport: route.transport,
17850
+ reason,
17851
+ policy,
17852
+ status: "pending",
17853
+ bindingId: route.bindingId
16985
17854
  };
16986
17855
  }
16987
- async function readStatusJsonFromFile(filePath) {
16988
- const raw2 = await readFile7(filePath, "utf8");
16989
- return parseStatusJson(raw2);
17856
+ function planMessageDeliveries(input) {
17857
+ const visibilityIds = new Set(resolveVisibilityAudience(input.message, input.conversation));
17858
+ const notifyIds = new Set(resolveNotifyAudience(input.message));
17859
+ const invokeIds = new Set(resolveInvocationAudience(input.message));
17860
+ const deliveries2 = new Map;
17861
+ for (const route of input.participantRoutes) {
17862
+ if (visibilityIds.has(route.targetId)) {
17863
+ const intent = planTargetDelivery(input.message, {
17864
+ ...route,
17865
+ transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
17866
+ }, input.conversation.kind === "direct" || input.conversation.kind === "group_direct" ? "direct_message" : "conversation_visibility", "durable");
17867
+ deliveries2.set(intent.id, intent);
17868
+ }
17869
+ if (notifyIds.has(route.targetId)) {
17870
+ const intent = planTargetDelivery(input.message, {
17871
+ ...route,
17872
+ transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
17873
+ }, "mention", "must_ack");
17874
+ deliveries2.set(intent.id, intent);
17875
+ }
17876
+ if (invokeIds.has(route.targetId)) {
17877
+ const intent = planTargetDelivery(input.message, {
17878
+ ...route,
17879
+ transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
17880
+ }, "invocation", "must_ack");
17881
+ deliveries2.set(intent.id, intent);
17882
+ }
17883
+ if (input.message.speech?.text && route.speechEnabled) {
17884
+ const speechIntent = planTargetDelivery(input.message, {
17885
+ ...route,
17886
+ transport: route.transport === "local_socket" ? "native_voice" : "tts",
17887
+ targetKind: route.targetKind === "device" ? "voice_session" : route.targetKind
17888
+ }, "speech", "best_effort");
17889
+ deliveries2.set(speechIntent.id, speechIntent);
17890
+ }
17891
+ }
17892
+ for (const route of input.bindingRoutes ?? []) {
17893
+ const intent = planTargetDelivery(input.message, route, "bridge_outbound", "durable");
17894
+ deliveries2.set(intent.id, intent);
17895
+ }
17896
+ return [...deliveries2.values()];
17897
+ }
17898
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/entity.js
17899
+ var entityKind = Symbol.for("drizzle:entityKind");
17900
+ var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
17901
+ function is(value, type) {
17902
+ if (!value || typeof value !== "object") {
17903
+ return false;
17904
+ }
17905
+ if (value instanceof type) {
17906
+ return true;
17907
+ }
17908
+ if (!Object.prototype.hasOwnProperty.call(type, entityKind)) {
17909
+ throw new Error(`Class "${type.name ?? "<unknown>"}" doesn't look like a Drizzle entity. If this is incorrect and the class is provided by Drizzle, please report this as a bug.`);
17910
+ }
17911
+ let cls = Object.getPrototypeOf(value).constructor;
17912
+ if (cls) {
17913
+ while (cls) {
17914
+ if (entityKind in cls && cls[entityKind] === type[entityKind]) {
17915
+ return true;
17916
+ }
17917
+ cls = Object.getPrototypeOf(cls);
17918
+ }
17919
+ }
17920
+ return false;
16990
17921
  }
16991
- async function readStatusJson() {
16992
- const fixturePath = process.env.OPENSCOUT_TAILSCALE_STATUS_JSON;
16993
- if (fixturePath) {
16994
- return readStatusJsonFromFile(fixturePath);
17922
+
17923
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column.js
17924
+ class Column {
17925
+ constructor(table, config) {
17926
+ this.table = table;
17927
+ this.config = config;
17928
+ this.name = config.name;
17929
+ this.keyAsName = config.keyAsName;
17930
+ this.notNull = config.notNull;
17931
+ this.default = config.default;
17932
+ this.defaultFn = config.defaultFn;
17933
+ this.onUpdateFn = config.onUpdateFn;
17934
+ this.hasDefault = config.hasDefault;
17935
+ this.primary = config.primaryKey;
17936
+ this.isUnique = config.isUnique;
17937
+ this.uniqueName = config.uniqueName;
17938
+ this.uniqueType = config.uniqueType;
17939
+ this.dataType = config.dataType;
17940
+ this.columnType = config.columnType;
17941
+ this.generated = config.generated;
17942
+ this.generatedIdentity = config.generatedIdentity;
17943
+ }
17944
+ static [entityKind] = "Column";
17945
+ name;
17946
+ keyAsName;
17947
+ primary;
17948
+ notNull;
17949
+ default;
17950
+ defaultFn;
17951
+ onUpdateFn;
17952
+ hasDefault;
17953
+ isUnique;
17954
+ uniqueName;
17955
+ uniqueType;
17956
+ dataType;
17957
+ columnType;
17958
+ enumValues = undefined;
17959
+ generated = undefined;
17960
+ generatedIdentity = undefined;
17961
+ config;
17962
+ mapFromDriverValue(value) {
17963
+ return value;
16995
17964
  }
16996
- try {
16997
- const tailscaleBin = process.env.OPENSCOUT_TAILSCALE_BIN ?? "tailscale";
16998
- const { stdout } = await execFileAsync(tailscaleBin, ["status", "--json"]);
16999
- return parseStatusJson(stdout);
17000
- } catch {
17001
- return null;
17965
+ mapToDriverValue(value) {
17966
+ return value;
17967
+ }
17968
+ shouldDisableInsert() {
17969
+ return this.config.generated !== undefined && this.config.generated.type !== "byDefault";
17002
17970
  }
17003
17971
  }
17004
- async function readTailscalePeers() {
17005
- const status = await readStatusJson();
17006
- if (!status) {
17007
- return [];
17972
+
17973
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column-builder.js
17974
+ class ColumnBuilder {
17975
+ static [entityKind] = "ColumnBuilder";
17976
+ config;
17977
+ constructor(name, dataType, columnType) {
17978
+ this.config = {
17979
+ name,
17980
+ keyAsName: name === "",
17981
+ notNull: false,
17982
+ default: undefined,
17983
+ hasDefault: false,
17984
+ primaryKey: false,
17985
+ isUnique: false,
17986
+ uniqueName: undefined,
17987
+ uniqueType: undefined,
17988
+ dataType,
17989
+ columnType,
17990
+ generated: undefined
17991
+ };
17992
+ }
17993
+ $type() {
17994
+ return this;
17995
+ }
17996
+ notNull() {
17997
+ this.config.notNull = true;
17998
+ return this;
17999
+ }
18000
+ default(value) {
18001
+ this.config.default = value;
18002
+ this.config.hasDefault = true;
18003
+ return this;
18004
+ }
18005
+ $defaultFn(fn) {
18006
+ this.config.defaultFn = fn;
18007
+ this.config.hasDefault = true;
18008
+ return this;
18009
+ }
18010
+ $default = this.$defaultFn;
18011
+ $onUpdateFn(fn) {
18012
+ this.config.onUpdateFn = fn;
18013
+ this.config.hasDefault = true;
18014
+ return this;
18015
+ }
18016
+ $onUpdate = this.$onUpdateFn;
18017
+ primaryKey() {
18018
+ this.config.primaryKey = true;
18019
+ this.config.notNull = true;
18020
+ return this;
18021
+ }
18022
+ setName(name) {
18023
+ if (this.config.name !== "")
18024
+ return;
18025
+ this.config.name = name;
17008
18026
  }
17009
- return parsePeers(status);
17010
18027
  }
17011
- async function readTailscaleSelf() {
17012
- const status = await readStatusJson();
17013
- if (!status) {
17014
- return null;
18028
+
18029
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.utils.js
18030
+ var TableName = Symbol.for("drizzle:Name");
18031
+
18032
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing-utils.js
18033
+ function iife(fn, ...args) {
18034
+ return fn(...args);
18035
+ }
18036
+
18037
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/unique-constraint.js
18038
+ function uniqueKeyName(table, columns) {
18039
+ return `${table[TableName]}_${columns.join("_")}_unique`;
18040
+ }
18041
+
18042
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/common.js
18043
+ class PgColumn extends Column {
18044
+ constructor(table, config) {
18045
+ if (!config.uniqueName) {
18046
+ config.uniqueName = uniqueKeyName(table, [config.name]);
18047
+ }
18048
+ super(table, config);
18049
+ this.table = table;
17015
18050
  }
17016
- return parseSelf(status);
18051
+ static [entityKind] = "PgColumn";
17017
18052
  }
17018
18053
 
17019
- // packages/web/server/core/mesh/service.ts
17020
- function normalizeHost(host) {
17021
- return host.trim().replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
18054
+ class ExtraConfigColumn extends PgColumn {
18055
+ static [entityKind] = "ExtraConfigColumn";
18056
+ getSQLType() {
18057
+ return this.getSQLType();
18058
+ }
18059
+ indexConfig = {
18060
+ order: this.config.order ?? "asc",
18061
+ nulls: this.config.nulls ?? "last",
18062
+ opClass: this.config.opClass
18063
+ };
18064
+ defaultConfig = {
18065
+ order: "asc",
18066
+ nulls: "last",
18067
+ opClass: undefined
18068
+ };
18069
+ asc() {
18070
+ this.indexConfig.order = "asc";
18071
+ return this;
18072
+ }
18073
+ desc() {
18074
+ this.indexConfig.order = "desc";
18075
+ return this;
18076
+ }
18077
+ nullsFirst() {
18078
+ this.indexConfig.nulls = "first";
18079
+ return this;
18080
+ }
18081
+ nullsLast() {
18082
+ this.indexConfig.nulls = "last";
18083
+ return this;
18084
+ }
18085
+ op(opClass) {
18086
+ this.indexConfig.opClass = opClass;
18087
+ return this;
18088
+ }
17022
18089
  }
17023
- function isLoopbackHost(host) {
17024
- const normalized = normalizeHost(host);
17025
- return normalized === "127.0.0.1" || normalized === "::1" || normalized === "localhost";
18090
+
18091
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/enum.js
18092
+ class PgEnumObjectColumn extends PgColumn {
18093
+ static [entityKind] = "PgEnumObjectColumn";
18094
+ enum;
18095
+ enumValues = this.config.enum.enumValues;
18096
+ constructor(table, config) {
18097
+ super(table, config);
18098
+ this.enum = config.enum;
18099
+ }
18100
+ getSQLType() {
18101
+ return this.enum.enumName;
18102
+ }
17026
18103
  }
17027
- function isWildcardHost(host) {
17028
- const normalized = normalizeHost(host);
17029
- return normalized === "0.0.0.0" || normalized === "::";
18104
+ var isPgEnumSym = Symbol.for("drizzle:isPgEnum");
18105
+ function isPgEnum(obj) {
18106
+ return !!obj && typeof obj === "function" && isPgEnumSym in obj && obj[isPgEnumSym] === true;
17030
18107
  }
17031
- function isPeerReachableBrokerUrl(url) {
17032
- if (!url) {
17033
- return false;
18108
+ class PgEnumColumn extends PgColumn {
18109
+ static [entityKind] = "PgEnumColumn";
18110
+ enum = this.config.enum;
18111
+ enumValues = this.config.enum.enumValues;
18112
+ constructor(table, config) {
18113
+ super(table, config);
18114
+ this.enum = config.enum;
17034
18115
  }
17035
- try {
17036
- const hostname2 = new URL(url).hostname;
17037
- return !isLoopbackHost(hostname2) && !isWildcardHost(hostname2);
17038
- } catch {
17039
- return false;
18116
+ getSQLType() {
18117
+ return this.enum.enumName;
17040
18118
  }
17041
18119
  }
17042
- function stripTrailingDot(value) {
17043
- const trimmed = value?.trim();
17044
- if (!trimmed) {
17045
- return null;
18120
+
18121
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/subquery.js
18122
+ class Subquery {
18123
+ static [entityKind] = "Subquery";
18124
+ constructor(sql, fields, alias, isWith = false, usedTables = []) {
18125
+ this._ = {
18126
+ brand: "Subquery",
18127
+ sql,
18128
+ selectedFields: fields,
18129
+ alias,
18130
+ isWith,
18131
+ usedTables
18132
+ };
17046
18133
  }
17047
- return trimmed.replace(/\.$/, "");
17048
18134
  }
17049
- async function readTailscaleStatus() {
17050
- const peers = await readTailscalePeers();
17051
- return {
17052
- available: peers.length > 0,
17053
- peers,
17054
- onlineCount: peers.filter((p) => p.online).length
18135
+
18136
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/version.js
18137
+ var version = "0.45.2";
18138
+
18139
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing.js
18140
+ var otel;
18141
+ var rawTracer;
18142
+ var tracer = {
18143
+ startActiveSpan(name, fn) {
18144
+ if (!otel) {
18145
+ return fn();
18146
+ }
18147
+ if (!rawTracer) {
18148
+ rawTracer = otel.trace.getTracer("drizzle-orm", version);
18149
+ }
18150
+ return iife((otel2, rawTracer2) => rawTracer2.startActiveSpan(name, (span) => {
18151
+ try {
18152
+ return fn(span);
18153
+ } catch (e) {
18154
+ span.setStatus({
18155
+ code: otel2.SpanStatusCode.ERROR,
18156
+ message: e instanceof Error ? e.message : "Unknown error"
18157
+ });
18158
+ throw e;
18159
+ } finally {
18160
+ span.end();
18161
+ }
18162
+ }), otel, rawTracer);
18163
+ }
18164
+ };
18165
+
18166
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/view-common.js
18167
+ var ViewBaseConfig = Symbol.for("drizzle:ViewBaseConfig");
18168
+
18169
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.js
18170
+ var Schema = Symbol.for("drizzle:Schema");
18171
+ var Columns = Symbol.for("drizzle:Columns");
18172
+ var ExtraConfigColumns = Symbol.for("drizzle:ExtraConfigColumns");
18173
+ var OriginalName = Symbol.for("drizzle:OriginalName");
18174
+ var BaseName = Symbol.for("drizzle:BaseName");
18175
+ var IsAlias = Symbol.for("drizzle:IsAlias");
18176
+ var ExtraConfigBuilder = Symbol.for("drizzle:ExtraConfigBuilder");
18177
+ var IsDrizzleTable = Symbol.for("drizzle:IsDrizzleTable");
18178
+
18179
+ class Table {
18180
+ static [entityKind] = "Table";
18181
+ static Symbol = {
18182
+ Name: TableName,
18183
+ Schema,
18184
+ OriginalName,
18185
+ Columns,
18186
+ ExtraConfigColumns,
18187
+ BaseName,
18188
+ IsAlias,
18189
+ ExtraConfigBuilder
17055
18190
  };
18191
+ [TableName];
18192
+ [OriginalName];
18193
+ [Schema];
18194
+ [Columns];
18195
+ [ExtraConfigColumns];
18196
+ [BaseName];
18197
+ [IsAlias] = false;
18198
+ [IsDrizzleTable] = true;
18199
+ [ExtraConfigBuilder] = undefined;
18200
+ constructor(name, schema, baseName) {
18201
+ this[TableName] = this[OriginalName] = name;
18202
+ this[Schema] = schema;
18203
+ this[BaseName] = baseName;
18204
+ }
17056
18205
  }
17057
- function formatIssueWarning(issue) {
17058
- return issue.action ? `${issue.title} \u2014 ${issue.summary} ${issue.action}` : `${issue.title} \u2014 ${issue.summary}`;
18206
+
18207
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/sql.js
18208
+ function isSQLWrapper(value) {
18209
+ return value !== null && value !== undefined && typeof value.getSQL === "function";
17059
18210
  }
17060
- function computeIssues(health, localNode, nodes, tailscale) {
17061
- const issues = [];
17062
- if (!health.reachable) {
17063
- issues.push({
17064
- code: "broker_unreachable",
17065
- severity: "error",
17066
- title: "Broker not reachable",
17067
- summary: "The mesh page cannot reach the local broker yet, so peer status is incomplete.",
17068
- action: "Start the broker, then reload this page.",
17069
- actionCommand: null
17070
- });
17071
- return issues;
18211
+ function mergeQueries(queries) {
18212
+ const result = { sql: "", params: [] };
18213
+ for (const query of queries) {
18214
+ result.sql += query.sql;
18215
+ result.params.push(...query.params);
18216
+ if (query.typings?.length) {
18217
+ if (!result.typings) {
18218
+ result.typings = [];
18219
+ }
18220
+ result.typings.push(...query.typings);
18221
+ }
17072
18222
  }
17073
- if (localNode?.advertiseScope === "local") {
17074
- issues.push({
18223
+ return result;
18224
+ }
18225
+
18226
+ class StringChunk {
18227
+ static [entityKind] = "StringChunk";
18228
+ value;
18229
+ constructor(value) {
18230
+ this.value = Array.isArray(value) ? value : [value];
18231
+ }
18232
+ getSQL() {
18233
+ return new SQL([this]);
18234
+ }
18235
+ }
18236
+
18237
+ class SQL {
18238
+ constructor(queryChunks) {
18239
+ this.queryChunks = queryChunks;
18240
+ for (const chunk of queryChunks) {
18241
+ if (is(chunk, Table)) {
18242
+ const schemaName = chunk[Table.Symbol.Schema];
18243
+ this.usedTables.push(schemaName === undefined ? chunk[Table.Symbol.Name] : schemaName + "." + chunk[Table.Symbol.Name]);
18244
+ }
18245
+ }
18246
+ }
18247
+ static [entityKind] = "SQL";
18248
+ decoder = noopDecoder;
18249
+ shouldInlineParams = false;
18250
+ usedTables = [];
18251
+ append(query) {
18252
+ this.queryChunks.push(...query.queryChunks);
18253
+ return this;
18254
+ }
18255
+ toQuery(config) {
18256
+ return tracer.startActiveSpan("drizzle.buildSQL", (span) => {
18257
+ const query = this.buildQueryFromSourceParams(this.queryChunks, config);
18258
+ span?.setAttributes({
18259
+ "drizzle.query.text": query.sql,
18260
+ "drizzle.query.params": JSON.stringify(query.params)
18261
+ });
18262
+ return query;
18263
+ });
18264
+ }
18265
+ buildQueryFromSourceParams(chunks, _config) {
18266
+ const config = Object.assign({}, _config, {
18267
+ inlineParams: _config.inlineParams || this.shouldInlineParams,
18268
+ paramStartIndex: _config.paramStartIndex || { value: 0 }
18269
+ });
18270
+ const {
18271
+ casing,
18272
+ escapeName,
18273
+ escapeParam,
18274
+ prepareTyping,
18275
+ inlineParams,
18276
+ paramStartIndex
18277
+ } = config;
18278
+ return mergeQueries(chunks.map((chunk) => {
18279
+ if (is(chunk, StringChunk)) {
18280
+ return { sql: chunk.value.join(""), params: [] };
18281
+ }
18282
+ if (is(chunk, Name)) {
18283
+ return { sql: escapeName(chunk.value), params: [] };
18284
+ }
18285
+ if (chunk === undefined) {
18286
+ return { sql: "", params: [] };
18287
+ }
18288
+ if (Array.isArray(chunk)) {
18289
+ const result = [new StringChunk("(")];
18290
+ for (const [i, p] of chunk.entries()) {
18291
+ result.push(p);
18292
+ if (i < chunk.length - 1) {
18293
+ result.push(new StringChunk(", "));
18294
+ }
18295
+ }
18296
+ result.push(new StringChunk(")"));
18297
+ return this.buildQueryFromSourceParams(result, config);
18298
+ }
18299
+ if (is(chunk, SQL)) {
18300
+ return this.buildQueryFromSourceParams(chunk.queryChunks, {
18301
+ ...config,
18302
+ inlineParams: inlineParams || chunk.shouldInlineParams
18303
+ });
18304
+ }
18305
+ if (is(chunk, Table)) {
18306
+ const schemaName = chunk[Table.Symbol.Schema];
18307
+ const tableName = chunk[Table.Symbol.Name];
18308
+ return {
18309
+ sql: schemaName === undefined || chunk[IsAlias] ? escapeName(tableName) : escapeName(schemaName) + "." + escapeName(tableName),
18310
+ params: []
18311
+ };
18312
+ }
18313
+ if (is(chunk, Column)) {
18314
+ const columnName = casing.getColumnCasing(chunk);
18315
+ if (_config.invokeSource === "indexes") {
18316
+ return { sql: escapeName(columnName), params: [] };
18317
+ }
18318
+ const schemaName = chunk.table[Table.Symbol.Schema];
18319
+ return {
18320
+ sql: chunk.table[IsAlias] || schemaName === undefined ? escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName) : escapeName(schemaName) + "." + escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName),
18321
+ params: []
18322
+ };
18323
+ }
18324
+ if (is(chunk, View)) {
18325
+ const schemaName = chunk[ViewBaseConfig].schema;
18326
+ const viewName = chunk[ViewBaseConfig].name;
18327
+ return {
18328
+ sql: schemaName === undefined || chunk[ViewBaseConfig].isAlias ? escapeName(viewName) : escapeName(schemaName) + "." + escapeName(viewName),
18329
+ params: []
18330
+ };
18331
+ }
18332
+ if (is(chunk, Param)) {
18333
+ if (is(chunk.value, Placeholder)) {
18334
+ return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
18335
+ }
18336
+ const mappedValue = chunk.value === null ? null : chunk.encoder.mapToDriverValue(chunk.value);
18337
+ if (is(mappedValue, SQL)) {
18338
+ return this.buildQueryFromSourceParams([mappedValue], config);
18339
+ }
18340
+ if (inlineParams) {
18341
+ return { sql: this.mapInlineParam(mappedValue, config), params: [] };
18342
+ }
18343
+ let typings = ["none"];
18344
+ if (prepareTyping) {
18345
+ typings = [prepareTyping(chunk.encoder)];
18346
+ }
18347
+ return { sql: escapeParam(paramStartIndex.value++, mappedValue), params: [mappedValue], typings };
18348
+ }
18349
+ if (is(chunk, Placeholder)) {
18350
+ return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
18351
+ }
18352
+ if (is(chunk, SQL.Aliased) && chunk.fieldAlias !== undefined) {
18353
+ return { sql: escapeName(chunk.fieldAlias), params: [] };
18354
+ }
18355
+ if (is(chunk, Subquery)) {
18356
+ if (chunk._.isWith) {
18357
+ return { sql: escapeName(chunk._.alias), params: [] };
18358
+ }
18359
+ return this.buildQueryFromSourceParams([
18360
+ new StringChunk("("),
18361
+ chunk._.sql,
18362
+ new StringChunk(") "),
18363
+ new Name(chunk._.alias)
18364
+ ], config);
18365
+ }
18366
+ if (isPgEnum(chunk)) {
18367
+ if (chunk.schema) {
18368
+ return { sql: escapeName(chunk.schema) + "." + escapeName(chunk.enumName), params: [] };
18369
+ }
18370
+ return { sql: escapeName(chunk.enumName), params: [] };
18371
+ }
18372
+ if (isSQLWrapper(chunk)) {
18373
+ if (chunk.shouldOmitSQLParens?.()) {
18374
+ return this.buildQueryFromSourceParams([chunk.getSQL()], config);
18375
+ }
18376
+ return this.buildQueryFromSourceParams([
18377
+ new StringChunk("("),
18378
+ chunk.getSQL(),
18379
+ new StringChunk(")")
18380
+ ], config);
18381
+ }
18382
+ if (inlineParams) {
18383
+ return { sql: this.mapInlineParam(chunk, config), params: [] };
18384
+ }
18385
+ return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
18386
+ }));
18387
+ }
18388
+ mapInlineParam(chunk, { escapeString }) {
18389
+ if (chunk === null) {
18390
+ return "null";
18391
+ }
18392
+ if (typeof chunk === "number" || typeof chunk === "boolean") {
18393
+ return chunk.toString();
18394
+ }
18395
+ if (typeof chunk === "string") {
18396
+ return escapeString(chunk);
18397
+ }
18398
+ if (typeof chunk === "object") {
18399
+ const mappedValueAsString = chunk.toString();
18400
+ if (mappedValueAsString === "[object Object]") {
18401
+ return escapeString(JSON.stringify(chunk));
18402
+ }
18403
+ return escapeString(mappedValueAsString);
18404
+ }
18405
+ throw new Error("Unexpected param value: " + chunk);
18406
+ }
18407
+ getSQL() {
18408
+ return this;
18409
+ }
18410
+ as(alias) {
18411
+ if (alias === undefined) {
18412
+ return this;
18413
+ }
18414
+ return new SQL.Aliased(this, alias);
18415
+ }
18416
+ mapWith(decoder) {
18417
+ this.decoder = typeof decoder === "function" ? { mapFromDriverValue: decoder } : decoder;
18418
+ return this;
18419
+ }
18420
+ inlineParams() {
18421
+ this.shouldInlineParams = true;
18422
+ return this;
18423
+ }
18424
+ if(condition) {
18425
+ return condition ? this : undefined;
18426
+ }
18427
+ }
18428
+
18429
+ class Name {
18430
+ constructor(value) {
18431
+ this.value = value;
18432
+ }
18433
+ static [entityKind] = "Name";
18434
+ brand;
18435
+ getSQL() {
18436
+ return new SQL([this]);
18437
+ }
18438
+ }
18439
+ var noopDecoder = {
18440
+ mapFromDriverValue: (value) => value
18441
+ };
18442
+ var noopEncoder = {
18443
+ mapToDriverValue: (value) => value
18444
+ };
18445
+ var noopMapper = {
18446
+ ...noopDecoder,
18447
+ ...noopEncoder
18448
+ };
18449
+
18450
+ class Param {
18451
+ constructor(value, encoder = noopEncoder) {
18452
+ this.value = value;
18453
+ this.encoder = encoder;
18454
+ }
18455
+ static [entityKind] = "Param";
18456
+ brand;
18457
+ getSQL() {
18458
+ return new SQL([this]);
18459
+ }
18460
+ }
18461
+ function sql(strings, ...params) {
18462
+ const queryChunks = [];
18463
+ if (params.length > 0 || strings.length > 0 && strings[0] !== "") {
18464
+ queryChunks.push(new StringChunk(strings[0]));
18465
+ }
18466
+ for (const [paramIndex, param2] of params.entries()) {
18467
+ queryChunks.push(param2, new StringChunk(strings[paramIndex + 1]));
18468
+ }
18469
+ return new SQL(queryChunks);
18470
+ }
18471
+ ((sql2) => {
18472
+ function empty() {
18473
+ return new SQL([]);
18474
+ }
18475
+ sql2.empty = empty;
18476
+ function fromList(list) {
18477
+ return new SQL(list);
18478
+ }
18479
+ sql2.fromList = fromList;
18480
+ function raw2(str) {
18481
+ return new SQL([new StringChunk(str)]);
18482
+ }
18483
+ sql2.raw = raw2;
18484
+ function join18(chunks, separator) {
18485
+ const result = [];
18486
+ for (const [i, chunk] of chunks.entries()) {
18487
+ if (i > 0 && separator !== undefined) {
18488
+ result.push(separator);
18489
+ }
18490
+ result.push(chunk);
18491
+ }
18492
+ return new SQL(result);
18493
+ }
18494
+ sql2.join = join18;
18495
+ function identifier(value) {
18496
+ return new Name(value);
18497
+ }
18498
+ sql2.identifier = identifier;
18499
+ function placeholder2(name2) {
18500
+ return new Placeholder(name2);
18501
+ }
18502
+ sql2.placeholder = placeholder2;
18503
+ function param2(value, encoder) {
18504
+ return new Param(value, encoder);
18505
+ }
18506
+ sql2.param = param2;
18507
+ })(sql || (sql = {}));
18508
+ ((SQL2) => {
18509
+
18510
+ class Aliased {
18511
+ constructor(sql2, fieldAlias) {
18512
+ this.sql = sql2;
18513
+ this.fieldAlias = fieldAlias;
18514
+ }
18515
+ static [entityKind] = "SQL.Aliased";
18516
+ isSelectionField = false;
18517
+ getSQL() {
18518
+ return this.sql;
18519
+ }
18520
+ clone() {
18521
+ return new Aliased(this.sql, this.fieldAlias);
18522
+ }
18523
+ }
18524
+ SQL2.Aliased = Aliased;
18525
+ })(SQL || (SQL = {}));
18526
+
18527
+ class Placeholder {
18528
+ constructor(name2) {
18529
+ this.name = name2;
18530
+ }
18531
+ static [entityKind] = "Placeholder";
18532
+ getSQL() {
18533
+ return new SQL([this]);
18534
+ }
18535
+ }
18536
+ var IsDrizzleView = Symbol.for("drizzle:IsDrizzleView");
18537
+
18538
+ class View {
18539
+ static [entityKind] = "View";
18540
+ [ViewBaseConfig];
18541
+ [IsDrizzleView] = true;
18542
+ constructor({ name: name2, schema, selectedFields, query }) {
18543
+ this[ViewBaseConfig] = {
18544
+ name: name2,
18545
+ originalName: name2,
18546
+ schema,
18547
+ selectedFields,
18548
+ query,
18549
+ isExisting: !query,
18550
+ isAlias: false
18551
+ };
18552
+ }
18553
+ getSQL() {
18554
+ return new SQL([this]);
18555
+ }
18556
+ }
18557
+ Column.prototype.getSQL = function() {
18558
+ return new SQL([this]);
18559
+ };
18560
+ Table.prototype.getSQL = function() {
18561
+ return new SQL([this]);
18562
+ };
18563
+ Subquery.prototype.getSQL = function() {
18564
+ return new SQL([this]);
18565
+ };
18566
+
18567
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/utils.js
18568
+ function getColumnNameAndConfig(a, b) {
18569
+ return {
18570
+ name: typeof a === "string" && a.length > 0 ? a : "",
18571
+ config: typeof a === "object" ? a : b
18572
+ };
18573
+ }
18574
+ var textDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder;
18575
+
18576
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
18577
+ class ForeignKeyBuilder {
18578
+ static [entityKind] = "SQLiteForeignKeyBuilder";
18579
+ reference;
18580
+ _onUpdate;
18581
+ _onDelete;
18582
+ constructor(config, actions) {
18583
+ this.reference = () => {
18584
+ const { name, columns, foreignColumns } = config();
18585
+ return { name, columns, foreignTable: foreignColumns[0].table, foreignColumns };
18586
+ };
18587
+ if (actions) {
18588
+ this._onUpdate = actions.onUpdate;
18589
+ this._onDelete = actions.onDelete;
18590
+ }
18591
+ }
18592
+ onUpdate(action) {
18593
+ this._onUpdate = action;
18594
+ return this;
18595
+ }
18596
+ onDelete(action) {
18597
+ this._onDelete = action;
18598
+ return this;
18599
+ }
18600
+ build(table) {
18601
+ return new ForeignKey(table, this);
18602
+ }
18603
+ }
18604
+
18605
+ class ForeignKey {
18606
+ constructor(table, builder) {
18607
+ this.table = table;
18608
+ this.reference = builder.reference;
18609
+ this.onUpdate = builder._onUpdate;
18610
+ this.onDelete = builder._onDelete;
18611
+ }
18612
+ static [entityKind] = "SQLiteForeignKey";
18613
+ reference;
18614
+ onUpdate;
18615
+ onDelete;
18616
+ getName() {
18617
+ const { name, columns, foreignColumns } = this.reference();
18618
+ const columnNames = columns.map((column) => column.name);
18619
+ const foreignColumnNames = foreignColumns.map((column) => column.name);
18620
+ const chunks = [
18621
+ this.table[TableName],
18622
+ ...columnNames,
18623
+ foreignColumns[0].table[TableName],
18624
+ ...foreignColumnNames
18625
+ ];
18626
+ return name ?? `${chunks.join("_")}_fk`;
18627
+ }
18628
+ }
18629
+
18630
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
18631
+ function uniqueKeyName2(table, columns) {
18632
+ return `${table[TableName]}_${columns.join("_")}_unique`;
18633
+ }
18634
+
18635
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/common.js
18636
+ class SQLiteColumnBuilder extends ColumnBuilder {
18637
+ static [entityKind] = "SQLiteColumnBuilder";
18638
+ foreignKeyConfigs = [];
18639
+ references(ref, actions = {}) {
18640
+ this.foreignKeyConfigs.push({ ref, actions });
18641
+ return this;
18642
+ }
18643
+ unique(name) {
18644
+ this.config.isUnique = true;
18645
+ this.config.uniqueName = name;
18646
+ return this;
18647
+ }
18648
+ generatedAlwaysAs(as, config) {
18649
+ this.config.generated = {
18650
+ as,
18651
+ type: "always",
18652
+ mode: config?.mode ?? "virtual"
18653
+ };
18654
+ return this;
18655
+ }
18656
+ buildForeignKeys(column, table) {
18657
+ return this.foreignKeyConfigs.map(({ ref, actions }) => {
18658
+ return ((ref2, actions2) => {
18659
+ const builder = new ForeignKeyBuilder(() => {
18660
+ const foreignColumn = ref2();
18661
+ return { columns: [column], foreignColumns: [foreignColumn] };
18662
+ });
18663
+ if (actions2.onUpdate) {
18664
+ builder.onUpdate(actions2.onUpdate);
18665
+ }
18666
+ if (actions2.onDelete) {
18667
+ builder.onDelete(actions2.onDelete);
18668
+ }
18669
+ return builder.build(table);
18670
+ })(ref, actions);
18671
+ });
18672
+ }
18673
+ }
18674
+
18675
+ class SQLiteColumn extends Column {
18676
+ constructor(table, config) {
18677
+ if (!config.uniqueName) {
18678
+ config.uniqueName = uniqueKeyName2(table, [config.name]);
18679
+ }
18680
+ super(table, config);
18681
+ this.table = table;
18682
+ }
18683
+ static [entityKind] = "SQLiteColumn";
18684
+ }
18685
+
18686
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/blob.js
18687
+ class SQLiteBigIntBuilder extends SQLiteColumnBuilder {
18688
+ static [entityKind] = "SQLiteBigIntBuilder";
18689
+ constructor(name) {
18690
+ super(name, "bigint", "SQLiteBigInt");
18691
+ }
18692
+ build(table) {
18693
+ return new SQLiteBigInt(table, this.config);
18694
+ }
18695
+ }
18696
+
18697
+ class SQLiteBigInt extends SQLiteColumn {
18698
+ static [entityKind] = "SQLiteBigInt";
18699
+ getSQLType() {
18700
+ return "blob";
18701
+ }
18702
+ mapFromDriverValue(value) {
18703
+ if (typeof Buffer !== "undefined" && Buffer.from) {
18704
+ const buf = Buffer.isBuffer(value) ? value : value instanceof ArrayBuffer ? Buffer.from(value) : value.buffer ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : Buffer.from(value);
18705
+ return BigInt(buf.toString("utf8"));
18706
+ }
18707
+ return BigInt(textDecoder.decode(value));
18708
+ }
18709
+ mapToDriverValue(value) {
18710
+ return Buffer.from(value.toString());
18711
+ }
18712
+ }
18713
+
18714
+ class SQLiteBlobJsonBuilder extends SQLiteColumnBuilder {
18715
+ static [entityKind] = "SQLiteBlobJsonBuilder";
18716
+ constructor(name) {
18717
+ super(name, "json", "SQLiteBlobJson");
18718
+ }
18719
+ build(table) {
18720
+ return new SQLiteBlobJson(table, this.config);
18721
+ }
18722
+ }
18723
+
18724
+ class SQLiteBlobJson extends SQLiteColumn {
18725
+ static [entityKind] = "SQLiteBlobJson";
18726
+ getSQLType() {
18727
+ return "blob";
18728
+ }
18729
+ mapFromDriverValue(value) {
18730
+ if (typeof Buffer !== "undefined" && Buffer.from) {
18731
+ const buf = Buffer.isBuffer(value) ? value : value instanceof ArrayBuffer ? Buffer.from(value) : value.buffer ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : Buffer.from(value);
18732
+ return JSON.parse(buf.toString("utf8"));
18733
+ }
18734
+ return JSON.parse(textDecoder.decode(value));
18735
+ }
18736
+ mapToDriverValue(value) {
18737
+ return Buffer.from(JSON.stringify(value));
18738
+ }
18739
+ }
18740
+
18741
+ class SQLiteBlobBufferBuilder extends SQLiteColumnBuilder {
18742
+ static [entityKind] = "SQLiteBlobBufferBuilder";
18743
+ constructor(name) {
18744
+ super(name, "buffer", "SQLiteBlobBuffer");
18745
+ }
18746
+ build(table) {
18747
+ return new SQLiteBlobBuffer(table, this.config);
18748
+ }
18749
+ }
18750
+
18751
+ class SQLiteBlobBuffer extends SQLiteColumn {
18752
+ static [entityKind] = "SQLiteBlobBuffer";
18753
+ mapFromDriverValue(value) {
18754
+ if (Buffer.isBuffer(value)) {
18755
+ return value;
18756
+ }
18757
+ return Buffer.from(value);
18758
+ }
18759
+ getSQLType() {
18760
+ return "blob";
18761
+ }
18762
+ }
18763
+ function blob(a, b) {
18764
+ const { name, config } = getColumnNameAndConfig(a, b);
18765
+ if (config?.mode === "json") {
18766
+ return new SQLiteBlobJsonBuilder(name);
18767
+ }
18768
+ if (config?.mode === "bigint") {
18769
+ return new SQLiteBigIntBuilder(name);
18770
+ }
18771
+ return new SQLiteBlobBufferBuilder(name);
18772
+ }
18773
+
18774
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/custom.js
18775
+ class SQLiteCustomColumnBuilder extends SQLiteColumnBuilder {
18776
+ static [entityKind] = "SQLiteCustomColumnBuilder";
18777
+ constructor(name, fieldConfig, customTypeParams) {
18778
+ super(name, "custom", "SQLiteCustomColumn");
18779
+ this.config.fieldConfig = fieldConfig;
18780
+ this.config.customTypeParams = customTypeParams;
18781
+ }
18782
+ build(table) {
18783
+ return new SQLiteCustomColumn(table, this.config);
18784
+ }
18785
+ }
18786
+
18787
+ class SQLiteCustomColumn extends SQLiteColumn {
18788
+ static [entityKind] = "SQLiteCustomColumn";
18789
+ sqlName;
18790
+ mapTo;
18791
+ mapFrom;
18792
+ constructor(table, config) {
18793
+ super(table, config);
18794
+ this.sqlName = config.customTypeParams.dataType(config.fieldConfig);
18795
+ this.mapTo = config.customTypeParams.toDriver;
18796
+ this.mapFrom = config.customTypeParams.fromDriver;
18797
+ }
18798
+ getSQLType() {
18799
+ return this.sqlName;
18800
+ }
18801
+ mapFromDriverValue(value) {
18802
+ return typeof this.mapFrom === "function" ? this.mapFrom(value) : value;
18803
+ }
18804
+ mapToDriverValue(value) {
18805
+ return typeof this.mapTo === "function" ? this.mapTo(value) : value;
18806
+ }
18807
+ }
18808
+ function customType(customTypeParams) {
18809
+ return (a, b) => {
18810
+ const { name, config } = getColumnNameAndConfig(a, b);
18811
+ return new SQLiteCustomColumnBuilder(name, config, customTypeParams);
18812
+ };
18813
+ }
18814
+
18815
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/integer.js
18816
+ class SQLiteBaseIntegerBuilder extends SQLiteColumnBuilder {
18817
+ static [entityKind] = "SQLiteBaseIntegerBuilder";
18818
+ constructor(name, dataType, columnType) {
18819
+ super(name, dataType, columnType);
18820
+ this.config.autoIncrement = false;
18821
+ }
18822
+ primaryKey(config) {
18823
+ if (config?.autoIncrement) {
18824
+ this.config.autoIncrement = true;
18825
+ }
18826
+ this.config.hasDefault = true;
18827
+ return super.primaryKey();
18828
+ }
18829
+ }
18830
+
18831
+ class SQLiteBaseInteger extends SQLiteColumn {
18832
+ static [entityKind] = "SQLiteBaseInteger";
18833
+ autoIncrement = this.config.autoIncrement;
18834
+ getSQLType() {
18835
+ return "integer";
18836
+ }
18837
+ }
18838
+
18839
+ class SQLiteIntegerBuilder extends SQLiteBaseIntegerBuilder {
18840
+ static [entityKind] = "SQLiteIntegerBuilder";
18841
+ constructor(name) {
18842
+ super(name, "number", "SQLiteInteger");
18843
+ }
18844
+ build(table) {
18845
+ return new SQLiteInteger(table, this.config);
18846
+ }
18847
+ }
18848
+
18849
+ class SQLiteInteger extends SQLiteBaseInteger {
18850
+ static [entityKind] = "SQLiteInteger";
18851
+ }
18852
+
18853
+ class SQLiteTimestampBuilder extends SQLiteBaseIntegerBuilder {
18854
+ static [entityKind] = "SQLiteTimestampBuilder";
18855
+ constructor(name, mode) {
18856
+ super(name, "date", "SQLiteTimestamp");
18857
+ this.config.mode = mode;
18858
+ }
18859
+ defaultNow() {
18860
+ return this.default(sql`(cast((julianday('now') - 2440587.5)*86400000 as integer))`);
18861
+ }
18862
+ build(table) {
18863
+ return new SQLiteTimestamp(table, this.config);
18864
+ }
18865
+ }
18866
+
18867
+ class SQLiteTimestamp extends SQLiteBaseInteger {
18868
+ static [entityKind] = "SQLiteTimestamp";
18869
+ mode = this.config.mode;
18870
+ mapFromDriverValue(value) {
18871
+ if (this.config.mode === "timestamp") {
18872
+ return new Date(value * 1000);
18873
+ }
18874
+ return new Date(value);
18875
+ }
18876
+ mapToDriverValue(value) {
18877
+ const unix = value.getTime();
18878
+ if (this.config.mode === "timestamp") {
18879
+ return Math.floor(unix / 1000);
18880
+ }
18881
+ return unix;
18882
+ }
18883
+ }
18884
+
18885
+ class SQLiteBooleanBuilder extends SQLiteBaseIntegerBuilder {
18886
+ static [entityKind] = "SQLiteBooleanBuilder";
18887
+ constructor(name, mode) {
18888
+ super(name, "boolean", "SQLiteBoolean");
18889
+ this.config.mode = mode;
18890
+ }
18891
+ build(table) {
18892
+ return new SQLiteBoolean(table, this.config);
18893
+ }
18894
+ }
18895
+
18896
+ class SQLiteBoolean extends SQLiteBaseInteger {
18897
+ static [entityKind] = "SQLiteBoolean";
18898
+ mode = this.config.mode;
18899
+ mapFromDriverValue(value) {
18900
+ return Number(value) === 1;
18901
+ }
18902
+ mapToDriverValue(value) {
18903
+ return value ? 1 : 0;
18904
+ }
18905
+ }
18906
+ function integer(a, b) {
18907
+ const { name, config } = getColumnNameAndConfig(a, b);
18908
+ if (config?.mode === "timestamp" || config?.mode === "timestamp_ms") {
18909
+ return new SQLiteTimestampBuilder(name, config.mode);
18910
+ }
18911
+ if (config?.mode === "boolean") {
18912
+ return new SQLiteBooleanBuilder(name, config.mode);
18913
+ }
18914
+ return new SQLiteIntegerBuilder(name);
18915
+ }
18916
+
18917
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
18918
+ class SQLiteNumericBuilder extends SQLiteColumnBuilder {
18919
+ static [entityKind] = "SQLiteNumericBuilder";
18920
+ constructor(name) {
18921
+ super(name, "string", "SQLiteNumeric");
18922
+ }
18923
+ build(table) {
18924
+ return new SQLiteNumeric(table, this.config);
18925
+ }
18926
+ }
18927
+
18928
+ class SQLiteNumeric extends SQLiteColumn {
18929
+ static [entityKind] = "SQLiteNumeric";
18930
+ mapFromDriverValue(value) {
18931
+ if (typeof value === "string")
18932
+ return value;
18933
+ return String(value);
18934
+ }
18935
+ getSQLType() {
18936
+ return "numeric";
18937
+ }
18938
+ }
18939
+
18940
+ class SQLiteNumericNumberBuilder extends SQLiteColumnBuilder {
18941
+ static [entityKind] = "SQLiteNumericNumberBuilder";
18942
+ constructor(name) {
18943
+ super(name, "number", "SQLiteNumericNumber");
18944
+ }
18945
+ build(table) {
18946
+ return new SQLiteNumericNumber(table, this.config);
18947
+ }
18948
+ }
18949
+
18950
+ class SQLiteNumericNumber extends SQLiteColumn {
18951
+ static [entityKind] = "SQLiteNumericNumber";
18952
+ mapFromDriverValue(value) {
18953
+ if (typeof value === "number")
18954
+ return value;
18955
+ return Number(value);
18956
+ }
18957
+ mapToDriverValue = String;
18958
+ getSQLType() {
18959
+ return "numeric";
18960
+ }
18961
+ }
18962
+
18963
+ class SQLiteNumericBigIntBuilder extends SQLiteColumnBuilder {
18964
+ static [entityKind] = "SQLiteNumericBigIntBuilder";
18965
+ constructor(name) {
18966
+ super(name, "bigint", "SQLiteNumericBigInt");
18967
+ }
18968
+ build(table) {
18969
+ return new SQLiteNumericBigInt(table, this.config);
18970
+ }
18971
+ }
18972
+
18973
+ class SQLiteNumericBigInt extends SQLiteColumn {
18974
+ static [entityKind] = "SQLiteNumericBigInt";
18975
+ mapFromDriverValue = BigInt;
18976
+ mapToDriverValue = String;
18977
+ getSQLType() {
18978
+ return "numeric";
18979
+ }
18980
+ }
18981
+ function numeric(a, b) {
18982
+ const { name, config } = getColumnNameAndConfig(a, b);
18983
+ const mode = config?.mode;
18984
+ return mode === "number" ? new SQLiteNumericNumberBuilder(name) : mode === "bigint" ? new SQLiteNumericBigIntBuilder(name) : new SQLiteNumericBuilder(name);
18985
+ }
18986
+
18987
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/real.js
18988
+ class SQLiteRealBuilder extends SQLiteColumnBuilder {
18989
+ static [entityKind] = "SQLiteRealBuilder";
18990
+ constructor(name) {
18991
+ super(name, "number", "SQLiteReal");
18992
+ }
18993
+ build(table) {
18994
+ return new SQLiteReal(table, this.config);
18995
+ }
18996
+ }
18997
+
18998
+ class SQLiteReal extends SQLiteColumn {
18999
+ static [entityKind] = "SQLiteReal";
19000
+ getSQLType() {
19001
+ return "real";
19002
+ }
19003
+ }
19004
+ function real(name) {
19005
+ return new SQLiteRealBuilder(name ?? "");
19006
+ }
19007
+
19008
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/text.js
19009
+ class SQLiteTextBuilder extends SQLiteColumnBuilder {
19010
+ static [entityKind] = "SQLiteTextBuilder";
19011
+ constructor(name, config) {
19012
+ super(name, "string", "SQLiteText");
19013
+ this.config.enumValues = config.enum;
19014
+ this.config.length = config.length;
19015
+ }
19016
+ build(table) {
19017
+ return new SQLiteText(table, this.config);
19018
+ }
19019
+ }
19020
+
19021
+ class SQLiteText extends SQLiteColumn {
19022
+ static [entityKind] = "SQLiteText";
19023
+ enumValues = this.config.enumValues;
19024
+ length = this.config.length;
19025
+ constructor(table, config) {
19026
+ super(table, config);
19027
+ }
19028
+ getSQLType() {
19029
+ return `text${this.config.length ? `(${this.config.length})` : ""}`;
19030
+ }
19031
+ }
19032
+
19033
+ class SQLiteTextJsonBuilder extends SQLiteColumnBuilder {
19034
+ static [entityKind] = "SQLiteTextJsonBuilder";
19035
+ constructor(name) {
19036
+ super(name, "json", "SQLiteTextJson");
19037
+ }
19038
+ build(table) {
19039
+ return new SQLiteTextJson(table, this.config);
19040
+ }
19041
+ }
19042
+
19043
+ class SQLiteTextJson extends SQLiteColumn {
19044
+ static [entityKind] = "SQLiteTextJson";
19045
+ getSQLType() {
19046
+ return "text";
19047
+ }
19048
+ mapFromDriverValue(value) {
19049
+ return JSON.parse(value);
19050
+ }
19051
+ mapToDriverValue(value) {
19052
+ return JSON.stringify(value);
19053
+ }
19054
+ }
19055
+ function text(a, b = {}) {
19056
+ const { name, config } = getColumnNameAndConfig(a, b);
19057
+ if (config.mode === "json") {
19058
+ return new SQLiteTextJsonBuilder(name);
19059
+ }
19060
+ return new SQLiteTextBuilder(name, config);
19061
+ }
19062
+
19063
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/all.js
19064
+ function getSQLiteColumnBuilders() {
19065
+ return {
19066
+ blob,
19067
+ customType,
19068
+ integer,
19069
+ numeric,
19070
+ real,
19071
+ text
19072
+ };
19073
+ }
19074
+
19075
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/table.js
19076
+ var InlineForeignKeys = Symbol.for("drizzle:SQLiteInlineForeignKeys");
19077
+
19078
+ class SQLiteTable extends Table {
19079
+ static [entityKind] = "SQLiteTable";
19080
+ static Symbol = Object.assign({}, Table.Symbol, {
19081
+ InlineForeignKeys
19082
+ });
19083
+ [Table.Symbol.Columns];
19084
+ [InlineForeignKeys] = [];
19085
+ [Table.Symbol.ExtraConfigBuilder] = undefined;
19086
+ }
19087
+ function sqliteTableBase(name, columns, extraConfig, schema, baseName = name) {
19088
+ const rawTable = new SQLiteTable(name, schema, baseName);
19089
+ const parsedColumns = typeof columns === "function" ? columns(getSQLiteColumnBuilders()) : columns;
19090
+ const builtColumns = Object.fromEntries(Object.entries(parsedColumns).map(([name2, colBuilderBase]) => {
19091
+ const colBuilder = colBuilderBase;
19092
+ colBuilder.setName(name2);
19093
+ const column = colBuilder.build(rawTable);
19094
+ rawTable[InlineForeignKeys].push(...colBuilder.buildForeignKeys(column, rawTable));
19095
+ return [name2, column];
19096
+ }));
19097
+ const table = Object.assign(rawTable, builtColumns);
19098
+ table[Table.Symbol.Columns] = builtColumns;
19099
+ table[Table.Symbol.ExtraConfigColumns] = builtColumns;
19100
+ if (extraConfig) {
19101
+ table[SQLiteTable.Symbol.ExtraConfigBuilder] = extraConfig;
19102
+ }
19103
+ return table;
19104
+ }
19105
+ var sqliteTable = (name, columns, extraConfig) => {
19106
+ return sqliteTableBase(name, columns, extraConfig);
19107
+ };
19108
+
19109
+ // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/indexes.js
19110
+ class IndexBuilderOn {
19111
+ constructor(name, unique2) {
19112
+ this.name = name;
19113
+ this.unique = unique2;
19114
+ }
19115
+ static [entityKind] = "SQLiteIndexBuilderOn";
19116
+ on(...columns) {
19117
+ return new IndexBuilder(this.name, columns, this.unique);
19118
+ }
19119
+ }
19120
+
19121
+ class IndexBuilder {
19122
+ static [entityKind] = "SQLiteIndexBuilder";
19123
+ config;
19124
+ constructor(name, columns, unique2) {
19125
+ this.config = {
19126
+ name,
19127
+ columns,
19128
+ unique: unique2,
19129
+ where: undefined
19130
+ };
19131
+ }
19132
+ where(condition) {
19133
+ this.config.where = condition;
19134
+ return this;
19135
+ }
19136
+ build(table) {
19137
+ return new Index(this.config, table);
19138
+ }
19139
+ }
19140
+
19141
+ class Index {
19142
+ static [entityKind] = "SQLiteIndex";
19143
+ config;
19144
+ constructor(config, table) {
19145
+ this.config = { ...config, table };
19146
+ }
19147
+ }
19148
+ function index(name) {
19149
+ return new IndexBuilderOn(name, false);
19150
+ }
19151
+
19152
+ // packages/runtime/src/drizzle-schema.ts
19153
+ var deliveriesTable = sqliteTable("deliveries", {
19154
+ id: text("id").primaryKey(),
19155
+ messageId: text("message_id"),
19156
+ invocationId: text("invocation_id"),
19157
+ targetId: text("target_id").notNull(),
19158
+ targetNodeId: text("target_node_id"),
19159
+ targetKind: text("target_kind").$type().notNull(),
19160
+ transport: text("transport").$type().notNull(),
19161
+ reason: text("reason").$type().notNull(),
19162
+ policy: text("policy").$type().notNull(),
19163
+ status: text("status").$type().notNull(),
19164
+ bindingId: text("binding_id"),
19165
+ leaseOwner: text("lease_owner"),
19166
+ leaseExpiresAt: integer("lease_expires_at"),
19167
+ metadataJson: text("metadata_json"),
19168
+ createdAt: integer("created_at").notNull().default(sql`(unixepoch())`)
19169
+ }, (table) => [
19170
+ index("idx_deliveries_status_transport").on(table.status, table.transport)
19171
+ ]);
19172
+ var deliveryAttemptsTable = sqliteTable("delivery_attempts", {
19173
+ id: text("id").primaryKey(),
19174
+ deliveryId: text("delivery_id").notNull(),
19175
+ attempt: integer("attempt").notNull(),
19176
+ status: text("status").$type().notNull(),
19177
+ error: text("error"),
19178
+ externalRef: text("external_ref"),
19179
+ metadataJson: text("metadata_json"),
19180
+ createdAt: integer("created_at").notNull()
19181
+ });
19182
+ // packages/runtime/src/broker.ts
19183
+ init_dist2();
19184
+ function createRuntimeId(prefix) {
19185
+ return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
19186
+ }
19187
+ function toTargetKind(actor) {
19188
+ if (!actor)
19189
+ return "participant";
19190
+ if (actor.kind === "agent")
19191
+ return "agent";
19192
+ if (actor.kind === "device")
19193
+ return "device";
19194
+ if (actor.kind === "bridge")
19195
+ return "bridge";
19196
+ return "participant";
19197
+ }
19198
+ function defaultTransportForActor(actor) {
19199
+ if (!actor)
19200
+ return "local_socket";
19201
+ switch (actor.kind) {
19202
+ case "bridge":
19203
+ return "webhook";
19204
+ case "device":
19205
+ return "local_socket";
19206
+ case "agent":
19207
+ return "local_socket";
19208
+ default:
19209
+ return "local_socket";
19210
+ }
19211
+ }
19212
+ function endpointTransportRank(transport) {
19213
+ switch (transport) {
19214
+ case "codex_app_server":
19215
+ return 0;
19216
+ case "claude_stream_json":
19217
+ return 1;
19218
+ case "tmux":
19219
+ return 2;
19220
+ case "local_socket":
19221
+ return 3;
19222
+ default:
19223
+ return 4;
19224
+ }
19225
+ }
19226
+ function shouldBridgeMessageToBinding(binding, message) {
19227
+ if (binding.platform !== "telegram") {
19228
+ return true;
19229
+ }
19230
+ const source = typeof message.metadata?.source === "string" ? String(message.metadata.source) : "";
19231
+ if (source === "telegram") {
19232
+ return false;
19233
+ }
19234
+ const outboundMode = typeof binding.metadata?.outboundMode === "string" ? String(binding.metadata.outboundMode) : "operator_only";
19235
+ const operatorId = typeof binding.metadata?.operatorId === "string" ? String(binding.metadata.operatorId) : "operator";
19236
+ const allowedActorIds = Array.isArray(binding.metadata?.allowedActorIds) ? binding.metadata.allowedActorIds.map((entry) => String(entry).trim()).filter(Boolean) : [];
19237
+ if (outboundMode === "all") {
19238
+ return true;
19239
+ }
19240
+ if (outboundMode === "allowlist") {
19241
+ return allowedActorIds.includes(message.actorId);
19242
+ }
19243
+ return message.actorId === operatorId;
19244
+ }
19245
+ function resolveBindingRoutes(bindings, message) {
19246
+ return bindings.filter((binding) => shouldBridgeMessageToBinding(binding, message)).map((binding) => ({
19247
+ targetId: binding.id,
19248
+ targetKind: "bridge",
19249
+ transport: binding.platform === "telegram" ? "telegram" : binding.platform === "discord" ? "discord" : "webhook",
19250
+ bindingId: binding.id
19251
+ }));
19252
+ }
19253
+ function preferredEndpoint(endpoints) {
19254
+ let preferred;
19255
+ let preferredRank = Number.POSITIVE_INFINITY;
19256
+ for (const endpoint of endpoints) {
19257
+ const rank = endpointTransportRank(endpoint.transport);
19258
+ if (!preferred || rank < preferredRank) {
19259
+ preferred = endpoint;
19260
+ preferredRank = rank;
19261
+ }
19262
+ }
19263
+ return preferred;
19264
+ }
19265
+
19266
+ class InMemoryControlRuntime {
19267
+ registry;
19268
+ listeners = new Set;
19269
+ eventBuffer = [];
19270
+ localNodeId;
19271
+ endpointIdsByAgentId = new Map;
19272
+ bindingIdsByConversationId = new Map;
19273
+ flightIdByInvocationId = new Map;
19274
+ constructor(initial = {}, options = {}) {
19275
+ this.registry = createRuntimeRegistrySnapshot(initial);
19276
+ this.localNodeId = options.localNodeId;
19277
+ this.rebuildIndexes();
19278
+ }
19279
+ snapshot() {
19280
+ return createRuntimeRegistrySnapshot({
19281
+ nodes: { ...this.registry.nodes },
19282
+ actors: { ...this.registry.actors },
19283
+ agents: { ...this.registry.agents },
19284
+ endpoints: { ...this.registry.endpoints },
19285
+ conversations: { ...this.registry.conversations },
19286
+ bindings: { ...this.registry.bindings },
19287
+ messages: { ...this.registry.messages },
19288
+ flights: { ...this.registry.flights },
19289
+ collaborationRecords: { ...this.registry.collaborationRecords }
19290
+ });
19291
+ }
19292
+ peek() {
19293
+ return this.registry;
19294
+ }
19295
+ node(nodeId) {
19296
+ return this.registry.nodes[nodeId];
19297
+ }
19298
+ agent(agentId) {
19299
+ return this.registry.agents[agentId];
19300
+ }
19301
+ conversation(conversationId) {
19302
+ return this.registry.conversations[conversationId];
19303
+ }
19304
+ message(messageId) {
19305
+ return this.registry.messages[messageId];
19306
+ }
19307
+ collaborationRecord(recordId) {
19308
+ return this.registry.collaborationRecords[recordId];
19309
+ }
19310
+ flightForInvocation(invocationId) {
19311
+ const flightId = this.flightIdByInvocationId.get(invocationId);
19312
+ return flightId ? this.registry.flights[flightId] : undefined;
19313
+ }
19314
+ bindingsForConversation(conversationId) {
19315
+ const bindingIds = this.bindingIdsByConversationId.get(conversationId);
19316
+ if (!bindingIds) {
19317
+ return [];
19318
+ }
19319
+ const bindings = [];
19320
+ for (const bindingId of bindingIds) {
19321
+ const binding = this.registry.bindings[bindingId];
19322
+ if (binding) {
19323
+ bindings.push(binding);
19324
+ }
19325
+ }
19326
+ return bindings;
19327
+ }
19328
+ endpointsForAgent(agentId, options = {}) {
19329
+ const endpointIds = this.endpointIdsByAgentId.get(agentId);
19330
+ if (!endpointIds) {
19331
+ return [];
19332
+ }
19333
+ const endpoints = [];
19334
+ for (const endpointId of endpointIds) {
19335
+ const endpoint = this.registry.endpoints[endpointId];
19336
+ if (!endpoint)
19337
+ continue;
19338
+ if (!options.includeOffline && endpoint.state === "offline")
19339
+ continue;
19340
+ if (options.nodeId && endpoint.nodeId !== options.nodeId)
19341
+ continue;
19342
+ if (options.harness && endpoint.harness !== options.harness)
19343
+ continue;
19344
+ endpoints.push(endpoint);
19345
+ }
19346
+ return endpoints;
19347
+ }
19348
+ recentEvents(limit = 100) {
19349
+ return this.eventBuffer.slice(-limit);
19350
+ }
19351
+ subscribe(listener) {
19352
+ this.listeners.add(listener);
19353
+ return () => {
19354
+ this.listeners.delete(listener);
19355
+ };
19356
+ }
19357
+ async dispatch(command) {
19358
+ switch (command.kind) {
19359
+ case "node.upsert":
19360
+ await this.upsertNode(command.node);
19361
+ return;
19362
+ case "actor.upsert":
19363
+ await this.upsertActor(command.actor);
19364
+ return;
19365
+ case "agent.upsert":
19366
+ await this.upsertAgent(command.agent);
19367
+ return;
19368
+ case "agent.endpoint.upsert":
19369
+ await this.upsertEndpoint(command.endpoint);
19370
+ return;
19371
+ case "conversation.upsert":
19372
+ await this.upsertConversation(command.conversation);
19373
+ return;
19374
+ case "binding.upsert":
19375
+ await this.upsertBinding(command.binding);
19376
+ return;
19377
+ case "collaboration.upsert":
19378
+ await this.upsertCollaboration(command.record);
19379
+ return;
19380
+ case "collaboration.event.append":
19381
+ await this.appendCollaborationEvent(command.event);
19382
+ return;
19383
+ case "conversation.post":
19384
+ await this.postMessage(command.message);
19385
+ return;
19386
+ case "agent.invoke":
19387
+ await this.invokeAgent(command.invocation);
19388
+ return;
19389
+ case "agent.ensure_awake":
19390
+ this.emit({
19391
+ id: createRuntimeId("evt"),
19392
+ kind: "flight.updated",
19393
+ ts: Date.now(),
19394
+ actorId: command.requesterId,
19395
+ nodeId: this.localNodeId,
19396
+ payload: {
19397
+ flight: {
19398
+ id: createRuntimeId("flt"),
19399
+ invocationId: command.agentId,
19400
+ requesterId: command.requesterId,
19401
+ targetAgentId: command.agentId,
19402
+ state: "waking",
19403
+ summary: command.reason
19404
+ }
19405
+ }
19406
+ });
19407
+ return;
19408
+ case "stream.subscribe":
19409
+ return;
19410
+ default: {
19411
+ const exhaustive = command;
19412
+ return exhaustive;
19413
+ }
19414
+ }
19415
+ }
19416
+ async upsertNode(node) {
19417
+ this.registry.nodes[node.id] = node;
19418
+ this.emit({
19419
+ id: createRuntimeId("evt"),
19420
+ kind: "node.upserted",
19421
+ ts: Date.now(),
19422
+ actorId: node.id,
19423
+ nodeId: node.id,
19424
+ payload: { node }
19425
+ });
19426
+ }
19427
+ async upsertActor(actor) {
19428
+ this.registry.actors[actor.id] = {
19429
+ id: actor.id,
19430
+ kind: actor.kind,
19431
+ displayName: actor.displayName,
19432
+ handle: actor.handle,
19433
+ labels: actor.labels,
19434
+ metadata: actor.metadata
19435
+ };
19436
+ this.emit({
19437
+ id: createRuntimeId("evt"),
19438
+ kind: "actor.registered",
19439
+ ts: Date.now(),
19440
+ actorId: actor.id,
19441
+ nodeId: this.localNodeId,
19442
+ payload: { actor }
19443
+ });
19444
+ }
19445
+ async upsertAgent(agent) {
19446
+ if (!this.registry.actors[agent.id]) {
19447
+ this.registry.actors[agent.id] = {
19448
+ id: agent.id,
19449
+ kind: agent.kind,
19450
+ displayName: agent.displayName,
19451
+ handle: agent.handle,
19452
+ labels: agent.labels,
19453
+ metadata: agent.metadata
19454
+ };
19455
+ }
19456
+ this.registry.agents[agent.id] = agent;
19457
+ this.emit({
19458
+ id: createRuntimeId("evt"),
19459
+ kind: "agent.registered",
19460
+ ts: Date.now(),
19461
+ actorId: agent.id,
19462
+ nodeId: agent.authorityNodeId,
19463
+ payload: { agent }
19464
+ });
19465
+ }
19466
+ async upsertEndpoint(endpoint) {
19467
+ const previous = this.registry.endpoints[endpoint.id];
19468
+ if (previous) {
19469
+ this.unindexEndpoint(previous);
19470
+ }
19471
+ this.registry.endpoints[endpoint.id] = endpoint;
19472
+ this.indexEndpoint(endpoint);
19473
+ this.emit({
19474
+ id: createRuntimeId("evt"),
19475
+ kind: "agent.endpoint.upserted",
19476
+ ts: Date.now(),
19477
+ actorId: endpoint.agentId,
19478
+ nodeId: endpoint.nodeId,
19479
+ payload: { endpoint }
19480
+ });
19481
+ }
19482
+ async upsertConversation(conversation) {
19483
+ this.registry.conversations[conversation.id] = conversation;
19484
+ this.emit({
19485
+ id: createRuntimeId("evt"),
19486
+ kind: "conversation.upserted",
19487
+ ts: Date.now(),
19488
+ actorId: "system",
19489
+ nodeId: conversation.authorityNodeId,
19490
+ payload: { conversation }
19491
+ });
19492
+ }
19493
+ async upsertBinding(binding) {
19494
+ const previous = this.registry.bindings[binding.id];
19495
+ if (previous) {
19496
+ this.unindexBinding(previous);
19497
+ }
19498
+ this.registry.bindings[binding.id] = binding;
19499
+ this.indexBinding(binding);
19500
+ this.emit({
19501
+ id: createRuntimeId("evt"),
19502
+ kind: "binding.upserted",
19503
+ ts: Date.now(),
19504
+ actorId: "system",
19505
+ nodeId: this.localNodeId,
19506
+ payload: { binding }
19507
+ });
19508
+ }
19509
+ async upsertCollaboration(record) {
19510
+ assertValidCollaborationRecord(record);
19511
+ this.registry.collaborationRecords[record.id] = record;
19512
+ this.emit({
19513
+ id: createRuntimeId("evt"),
19514
+ kind: "collaboration.upserted",
19515
+ ts: Date.now(),
19516
+ actorId: record.createdById,
19517
+ nodeId: this.localNodeId,
19518
+ payload: { record }
19519
+ });
19520
+ }
19521
+ async appendCollaborationEvent(event) {
19522
+ const record = this.registry.collaborationRecords[event.recordId];
19523
+ if (!record) {
19524
+ throw new Error(`unknown collaboration record: ${event.recordId}`);
19525
+ }
19526
+ assertValidCollaborationEvent(event, record);
19527
+ this.emit({
19528
+ id: createRuntimeId("evt"),
19529
+ kind: "collaboration.event.appended",
19530
+ ts: Date.now(),
19531
+ actorId: event.actorId,
19532
+ nodeId: this.localNodeId,
19533
+ payload: { event }
19534
+ });
19535
+ }
19536
+ async upsertFlight(flight) {
19537
+ const previous = this.registry.flights[flight.id];
19538
+ if (previous && this.flightIdByInvocationId.get(previous.invocationId) === previous.id) {
19539
+ this.flightIdByInvocationId.delete(previous.invocationId);
19540
+ }
19541
+ this.registry.flights[flight.id] = flight;
19542
+ this.flightIdByInvocationId.set(flight.invocationId, flight.id);
19543
+ this.emit({
19544
+ id: createRuntimeId("evt"),
19545
+ kind: "flight.updated",
19546
+ ts: Date.now(),
19547
+ actorId: flight.requesterId,
19548
+ nodeId: this.localNodeId,
19549
+ payload: { flight }
19550
+ });
19551
+ }
19552
+ async postMessage(message, options = {}) {
19553
+ const deliveries2 = this.planMessage(message, options);
19554
+ await this.commitMessage(message, deliveries2);
19555
+ return deliveries2;
19556
+ }
19557
+ planMessage(message, options = {}) {
19558
+ const conversation = this.registry.conversations[message.conversationId];
19559
+ if (!conversation) {
19560
+ throw new Error(`unknown conversation: ${message.conversationId}`);
19561
+ }
19562
+ const bindingRoutes = resolveBindingRoutes(this.bindingsForConversation(conversation.id), message);
19563
+ const plannedParticipantRoutes = this.resolveParticipantRoutes(conversation.participantIds);
19564
+ const participantRoutes = options.localOnly ? plannedParticipantRoutes.filter((route) => !route.nodeId || route.nodeId === this.localNodeId) : plannedParticipantRoutes;
19565
+ const deliveries2 = planMessageDeliveries({
19566
+ localNodeId: this.localNodeId,
19567
+ message,
19568
+ conversation,
19569
+ participantRoutes,
19570
+ bindingRoutes: options.localOnly ? [] : bindingRoutes
19571
+ });
19572
+ return deliveries2;
19573
+ }
19574
+ async commitMessage(message, deliveries2) {
19575
+ this.registry.messages[message.id] = message;
19576
+ this.emit({
19577
+ id: createRuntimeId("evt"),
19578
+ kind: "message.posted",
19579
+ ts: Date.now(),
19580
+ actorId: message.actorId,
19581
+ nodeId: message.originNodeId,
19582
+ payload: { message }
19583
+ });
19584
+ for (const delivery of deliveries2) {
19585
+ this.emit({
19586
+ id: createRuntimeId("evt"),
19587
+ kind: "delivery.planned",
19588
+ ts: Date.now(),
19589
+ actorId: message.actorId,
19590
+ nodeId: message.originNodeId,
19591
+ payload: { delivery }
19592
+ });
19593
+ }
19594
+ }
19595
+ async invokeAgent(invocation) {
19596
+ const flight = this.planInvocation(invocation);
19597
+ await this.commitInvocation(invocation, flight);
19598
+ return flight;
19599
+ }
19600
+ planInvocation(invocation) {
19601
+ const targetAgent = this.registry.agents[invocation.targetAgentId];
19602
+ if (!targetAgent) {
19603
+ throw new Error(`unknown agent: ${invocation.targetAgentId}`);
19604
+ }
19605
+ const targetEndpoints = this.endpointsForAgent(invocation.targetAgentId, {
19606
+ nodeId: targetAgent.authorityNodeId,
19607
+ harness: invocation.execution?.harness
19608
+ });
19609
+ const isLocalAuthority = !this.localNodeId || targetAgent.authorityNodeId === this.localNodeId;
19610
+ const startedAt = Date.now();
19611
+ let state = invocation.ensureAwake ? "waking" : "queued";
19612
+ let summary;
19613
+ let error;
19614
+ let completedAt;
19615
+ if (isLocalAuthority) {
19616
+ if (targetEndpoints.length == 0) {
19617
+ state = invocation.ensureAwake ? "waking" : "queued";
19618
+ summary = invocation.ensureAwake ? invocation.execution?.harness ? `${targetAgent.displayName} waking on ${invocation.execution.harness}.` : `${targetAgent.displayName} waking.` : `Message stored for ${targetAgent.displayName}. Will deliver when online.`;
19619
+ } else {
19620
+ state = "queued";
19621
+ summary = `${targetAgent.displayName} queued for local execution.`;
19622
+ }
19623
+ }
19624
+ const flight = {
19625
+ id: createRuntimeId("flt"),
19626
+ invocationId: invocation.id,
19627
+ requesterId: invocation.requesterId,
19628
+ targetAgentId: invocation.targetAgentId,
19629
+ state,
19630
+ summary,
19631
+ error,
19632
+ startedAt,
19633
+ completedAt,
19634
+ metadata: invocation.metadata
19635
+ };
19636
+ return flight;
19637
+ }
19638
+ async commitInvocation(invocation, flight) {
19639
+ const previous = this.registry.flights[flight.id];
19640
+ if (previous && this.flightIdByInvocationId.get(previous.invocationId) === previous.id) {
19641
+ this.flightIdByInvocationId.delete(previous.invocationId);
19642
+ }
19643
+ this.registry.flights[flight.id] = flight;
19644
+ this.flightIdByInvocationId.set(flight.invocationId, flight.id);
19645
+ const targetAgent = this.registry.agents[invocation.targetAgentId];
19646
+ this.emit({
19647
+ id: createRuntimeId("evt"),
19648
+ kind: "invocation.requested",
19649
+ ts: Date.now(),
19650
+ actorId: invocation.requesterId,
19651
+ nodeId: invocation.requesterNodeId,
19652
+ payload: { invocation }
19653
+ });
19654
+ this.emit({
19655
+ id: createRuntimeId("evt"),
19656
+ kind: "flight.updated",
19657
+ ts: Date.now(),
19658
+ actorId: invocation.requesterId,
19659
+ nodeId: targetAgent?.authorityNodeId,
19660
+ payload: { flight }
19661
+ });
19662
+ }
19663
+ rebuildIndexes() {
19664
+ for (const endpoint of Object.values(this.registry.endpoints)) {
19665
+ this.indexEndpoint(endpoint);
19666
+ }
19667
+ for (const binding of Object.values(this.registry.bindings)) {
19668
+ this.indexBinding(binding);
19669
+ }
19670
+ for (const flight of Object.values(this.registry.flights)) {
19671
+ this.flightIdByInvocationId.set(flight.invocationId, flight.id);
19672
+ }
19673
+ }
19674
+ indexEndpoint(endpoint) {
19675
+ this.addIndexedId(this.endpointIdsByAgentId, endpoint.agentId, endpoint.id);
19676
+ }
19677
+ unindexEndpoint(endpoint) {
19678
+ this.removeIndexedId(this.endpointIdsByAgentId, endpoint.agentId, endpoint.id);
19679
+ }
19680
+ indexBinding(binding) {
19681
+ this.addIndexedId(this.bindingIdsByConversationId, binding.conversationId, binding.id);
19682
+ }
19683
+ unindexBinding(binding) {
19684
+ this.removeIndexedId(this.bindingIdsByConversationId, binding.conversationId, binding.id);
19685
+ }
19686
+ addIndexedId(index2, key, value) {
19687
+ const ids = index2.get(key) ?? new Set;
19688
+ ids.add(value);
19689
+ index2.set(key, ids);
19690
+ }
19691
+ removeIndexedId(index2, key, value) {
19692
+ const ids = index2.get(key);
19693
+ if (!ids) {
19694
+ return;
19695
+ }
19696
+ ids.delete(value);
19697
+ if (ids.size === 0) {
19698
+ index2.delete(key);
19699
+ }
19700
+ }
19701
+ resolveParticipantRoutes(participantIds) {
19702
+ const routes = [];
19703
+ for (const participantId of participantIds) {
19704
+ const actor = this.registry.actors[participantId];
19705
+ const agent = this.registry.agents[participantId];
19706
+ const targetIdentity = actor ?? agent;
19707
+ const endpoints = this.endpointsForAgent(participantId);
19708
+ const endpoint = preferredEndpoint(endpoints);
19709
+ if (!endpoint) {
19710
+ if (agent?.authorityNodeId && agent.authorityNodeId !== this.localNodeId) {
19711
+ routes.push({
19712
+ targetId: participantId,
19713
+ nodeId: agent.authorityNodeId,
19714
+ targetKind: toTargetKind(targetIdentity),
19715
+ transport: defaultTransportForActor(targetIdentity),
19716
+ speechEnabled: false
19717
+ });
19718
+ } else if (agent) {
19719
+ routes.push({
19720
+ targetId: participantId,
19721
+ nodeId: agent.authorityNodeId ?? this.localNodeId,
19722
+ targetKind: toTargetKind(targetIdentity),
19723
+ transport: defaultTransportForActor(targetIdentity),
19724
+ speechEnabled: false
19725
+ });
19726
+ }
19727
+ continue;
19728
+ }
19729
+ routes.push({
19730
+ targetId: participantId,
19731
+ nodeId: endpoint.nodeId ?? agent?.authorityNodeId,
19732
+ targetKind: toTargetKind(targetIdentity),
19733
+ transport: endpoint.transport ?? defaultTransportForActor(targetIdentity),
19734
+ speechEnabled: Boolean(actor?.kind === "device" || endpoints.some((candidate) => candidate.transport === "local_socket" || candidate.transport === "websocket"))
19735
+ });
19736
+ }
19737
+ return routes;
19738
+ }
19739
+ emit(event) {
19740
+ this.eventBuffer.push(event);
19741
+ if (this.eventBuffer.length > 500) {
19742
+ this.eventBuffer.shift();
19743
+ }
19744
+ for (const listener of this.listeners) {
19745
+ listener(event);
19746
+ }
19747
+ }
19748
+ }
19749
+ // packages/runtime/src/tailscale.ts
19750
+ import { readFile as readFile7 } from "fs/promises";
19751
+ import { execFile } from "child_process";
19752
+ import { promisify } from "util";
19753
+ var execFileAsync = promisify(execFile);
19754
+ function parseStatusJson(raw2) {
19755
+ return JSON.parse(raw2);
19756
+ }
19757
+ function parsePeers(status) {
19758
+ const peers = Object.entries(status.Peer ?? {});
19759
+ return peers.map(([fallbackId, peer]) => ({
19760
+ id: peer.ID ?? fallbackId,
19761
+ name: peer.HostName ?? peer.DNSName ?? fallbackId,
19762
+ dnsName: peer.DNSName,
19763
+ addresses: peer.TailscaleIPs ?? [],
19764
+ online: peer.Online ?? false,
19765
+ hostName: peer.HostName,
19766
+ os: peer.OS,
19767
+ tags: peer.Tags ?? []
19768
+ }));
19769
+ }
19770
+ function parseSelf(status) {
19771
+ const self = status.Self;
19772
+ if (!self) {
19773
+ return null;
19774
+ }
19775
+ return {
19776
+ id: self.ID ?? self.DNSName ?? self.HostName ?? "self",
19777
+ name: self.HostName ?? self.DNSName ?? "self",
19778
+ dnsName: self.DNSName,
19779
+ addresses: self.TailscaleIPs ?? [],
19780
+ online: self.Online ?? true,
19781
+ hostName: self.HostName,
19782
+ os: self.OS,
19783
+ tailnetName: status.CurrentTailnet?.Name,
19784
+ magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
19785
+ };
19786
+ }
19787
+ async function readStatusJsonFromFile(filePath) {
19788
+ const raw2 = await readFile7(filePath, "utf8");
19789
+ return parseStatusJson(raw2);
19790
+ }
19791
+ async function readStatusJson() {
19792
+ const fixturePath = process.env.OPENSCOUT_TAILSCALE_STATUS_JSON;
19793
+ if (fixturePath) {
19794
+ return readStatusJsonFromFile(fixturePath);
19795
+ }
19796
+ try {
19797
+ const tailscaleBin = process.env.OPENSCOUT_TAILSCALE_BIN ?? "tailscale";
19798
+ const { stdout } = await execFileAsync(tailscaleBin, ["status", "--json"]);
19799
+ return parseStatusJson(stdout);
19800
+ } catch {
19801
+ return null;
19802
+ }
19803
+ }
19804
+ async function readTailscalePeers() {
19805
+ const status = await readStatusJson();
19806
+ if (!status) {
19807
+ return [];
19808
+ }
19809
+ return parsePeers(status);
19810
+ }
19811
+ async function readTailscaleSelf() {
19812
+ const status = await readStatusJson();
19813
+ if (!status) {
19814
+ return null;
19815
+ }
19816
+ return parseSelf(status);
19817
+ }
19818
+
19819
+ // packages/runtime/src/index.ts
19820
+ init_broker_service();
19821
+ init_local_agents();
19822
+
19823
+ // packages/runtime/src/scout-broker.ts
19824
+ init_dist2();
19825
+ init_setup();
19826
+ init_support_paths();
19827
+ await __promiseAll([
19828
+ init_local_agents(),
19829
+ init_broker_service()
19830
+ ]);
19831
+
19832
+ // packages/runtime/src/index.ts
19833
+ init_codex_app_server();
19834
+ init_setup();
19835
+ init_support_paths();
19836
+
19837
+ // packages/runtime/src/scout-agent-cards.ts
19838
+ init_dist2();
19839
+ // packages/runtime/src/thread-events.ts
19840
+ class ThreadWatchProtocolError extends Error {
19841
+ status;
19842
+ body;
19843
+ constructor(status, body) {
19844
+ super(body.message);
19845
+ this.status = status;
19846
+ this.body = body;
19847
+ }
19848
+ }
19849
+ function watchKey(conversationId, watcherNodeId, watcherId) {
19850
+ return `${conversationId}:${watcherNodeId}:${watcherId}`;
19851
+ }
19852
+ function writeSse(response, eventName, payload) {
19853
+ response.write(`event: ${eventName}
19854
+ data: ${JSON.stringify(payload)}
19855
+
19856
+ `);
19857
+ }
19858
+
19859
+ class ThreadEventPlane {
19860
+ options;
19861
+ watches = new Map;
19862
+ watchIdsByKey = new Map;
19863
+ watchIdsByConversation = new Map;
19864
+ constructor(options) {
19865
+ this.options = options;
19866
+ }
19867
+ async openWatch(request) {
19868
+ const conversation = this.requireConversationAuthority(request.conversationId);
19869
+ this.requireRemoteWatchAllowed(conversation);
19870
+ const afterSeq = Math.max(0, request.afterSeq ?? 0);
19871
+ const oldestSeq = await this.options.projection.oldestThreadSeq(conversation.id);
19872
+ const latestSeq = await this.options.projection.latestThreadSeq(conversation.id);
19873
+ if (latestSeq > 0 && oldestSeq > 0 && afterSeq < oldestSeq - 1) {
19874
+ throw new ThreadWatchProtocolError(409, {
19875
+ code: "cursor_out_of_range",
19876
+ message: `thread cursor ${afterSeq} is older than retained seq ${oldestSeq}`
19877
+ });
19878
+ }
19879
+ const key = watchKey(conversation.id, request.watcherNodeId, request.watcherId);
19880
+ const existingWatchId = this.watchIdsByKey.get(key);
19881
+ if (existingWatchId) {
19882
+ this.closeWatchInternal(existingWatchId);
19883
+ }
19884
+ const watchId = `thread-watch:${conversation.id}:${request.watcherNodeId}:${request.watcherId}`;
19885
+ const leaseExpiresAt = Date.now() + this.normalizeLeaseMs(request.leaseMs);
19886
+ const watch = {
19887
+ watchId,
19888
+ conversationId: conversation.id,
19889
+ watcherNodeId: request.watcherNodeId,
19890
+ watcherId: request.watcherId,
19891
+ acceptedAfterSeq: afterSeq,
19892
+ leaseExpiresAt,
19893
+ mode: conversation.shareMode === "summary" ? "summary" : "shared",
19894
+ clients: new Set
19895
+ };
19896
+ this.watches.set(watchId, watch);
19897
+ this.watchIdsByKey.set(key, watchId);
19898
+ this.indexWatch(watch);
19899
+ return {
19900
+ watchId,
19901
+ conversationId: conversation.id,
19902
+ authorityNodeId: conversation.authorityNodeId,
19903
+ acceptedAfterSeq: afterSeq,
19904
+ latestSeq,
19905
+ leaseExpiresAt,
19906
+ mode: watch.mode
19907
+ };
19908
+ }
19909
+ async renewWatch(request) {
19910
+ const watch = this.requireLiveWatch(request.watchId);
19911
+ watch.leaseExpiresAt = Date.now() + this.normalizeLeaseMs(request.leaseMs);
19912
+ return {
19913
+ watchId: watch.watchId,
19914
+ leaseExpiresAt: watch.leaseExpiresAt
19915
+ };
19916
+ }
19917
+ async closeWatch(request) {
19918
+ this.closeWatchInternal(request.watchId);
19919
+ }
19920
+ async replay(options) {
19921
+ const conversation = this.requireConversationAuthority(options.conversationId);
19922
+ this.requireRemoteWatchAllowed(conversation);
19923
+ const oldestSeq = await this.options.projection.oldestThreadSeq(conversation.id);
19924
+ const latestSeq = await this.options.projection.latestThreadSeq(conversation.id);
19925
+ if (latestSeq > 0 && oldestSeq > 0 && options.afterSeq < oldestSeq - 1) {
19926
+ throw new ThreadWatchProtocolError(409, {
19927
+ code: "cursor_out_of_range",
19928
+ message: `thread cursor ${options.afterSeq} is older than retained seq ${oldestSeq}`
19929
+ });
19930
+ }
19931
+ return this.options.projection.listThreadEvents({
19932
+ conversationId: conversation.id,
19933
+ afterSeq: options.afterSeq,
19934
+ limit: Math.min(options.limit ?? 500, this.options.maxReplayLimit ?? 2000)
19935
+ });
19936
+ }
19937
+ async snapshot(conversationId) {
19938
+ const conversation = this.requireConversationAuthority(conversationId);
19939
+ this.requireRemoteWatchAllowed(conversation);
19940
+ const snapshot = await this.options.projection.getThreadSnapshot(conversation.id);
19941
+ if (!snapshot) {
19942
+ throw new ThreadWatchProtocolError(404, {
19943
+ code: "unknown_conversation",
19944
+ message: `unknown conversation ${conversation.id}`
19945
+ });
19946
+ }
19947
+ return snapshot;
19948
+ }
19949
+ async streamWatch(watchId, request, response) {
19950
+ const watch = this.requireLiveWatch(watchId);
19951
+ const backlog = await this.options.projection.listThreadEvents({
19952
+ conversationId: watch.conversationId,
19953
+ afterSeq: watch.acceptedAfterSeq,
19954
+ limit: this.options.maxReplayLimit ?? 2000
19955
+ });
19956
+ response.writeHead(200, {
19957
+ "content-type": "text/event-stream",
19958
+ "cache-control": "no-cache, no-transform",
19959
+ connection: "keep-alive"
19960
+ });
19961
+ writeSse(response, "hello", {
19962
+ watchId: watch.watchId,
19963
+ conversationId: watch.conversationId,
19964
+ leaseExpiresAt: watch.leaseExpiresAt
19965
+ });
19966
+ for (const event of backlog) {
19967
+ writeSse(response, "thread.event", event);
19968
+ }
19969
+ watch.clients.add(response);
19970
+ request.on("close", () => {
19971
+ watch.clients.delete(response);
19972
+ response.end();
19973
+ });
19974
+ }
19975
+ publish(events2) {
19976
+ for (const event of events2) {
19977
+ const watchIds = this.watchIdsByConversation.get(event.conversationId);
19978
+ if (!watchIds || watchIds.size === 0) {
19979
+ continue;
19980
+ }
19981
+ for (const watchId of [...watchIds]) {
19982
+ const watch = this.watches.get(watchId);
19983
+ if (!watch) {
19984
+ watchIds.delete(watchId);
19985
+ continue;
19986
+ }
19987
+ if (watch.leaseExpiresAt <= Date.now()) {
19988
+ this.closeWatchInternal(watchId);
19989
+ continue;
19990
+ }
19991
+ for (const client of watch.clients) {
19992
+ writeSse(client, "thread.event", event);
19993
+ }
19994
+ }
19995
+ }
19996
+ }
19997
+ normalizeLeaseMs(requestedLeaseMs) {
19998
+ const defaultLeaseMs = this.options.defaultLeaseMs ?? 30000;
19999
+ const maxLeaseMs = this.options.maxLeaseMs ?? 300000;
20000
+ if (!requestedLeaseMs || !Number.isFinite(requestedLeaseMs) || requestedLeaseMs <= 0) {
20001
+ return defaultLeaseMs;
20002
+ }
20003
+ return Math.min(Math.max(requestedLeaseMs, 5000), maxLeaseMs);
20004
+ }
20005
+ requireConversationAuthority(conversationId) {
20006
+ const conversation = this.options.runtime.conversation(conversationId);
20007
+ if (!conversation) {
20008
+ throw new ThreadWatchProtocolError(404, {
20009
+ code: "unknown_conversation",
20010
+ message: `unknown conversation ${conversationId}`
20011
+ });
20012
+ }
20013
+ if (conversation.authorityNodeId !== this.options.nodeId) {
20014
+ throw new ThreadWatchProtocolError(409, {
20015
+ code: "no_responder",
20016
+ message: `conversation ${conversationId} is owned by ${conversation.authorityNodeId}`
20017
+ });
20018
+ }
20019
+ return conversation;
20020
+ }
20021
+ requireRemoteWatchAllowed(conversation) {
20022
+ if (conversation.shareMode === "local") {
20023
+ throw new ThreadWatchProtocolError(403, {
20024
+ code: "forbidden",
20025
+ message: `conversation ${conversation.id} does not allow remote watches`
20026
+ });
20027
+ }
20028
+ }
20029
+ requireLiveWatch(watchId) {
20030
+ const watch = this.watches.get(watchId);
20031
+ if (!watch) {
20032
+ throw new ThreadWatchProtocolError(404, {
20033
+ code: "invalid_request",
20034
+ message: `unknown watch ${watchId}`
20035
+ });
20036
+ }
20037
+ if (watch.leaseExpiresAt <= Date.now()) {
20038
+ this.closeWatchInternal(watchId);
20039
+ throw new ThreadWatchProtocolError(410, {
20040
+ code: "lease_expired",
20041
+ message: `watch ${watchId} lease expired`
20042
+ });
20043
+ }
20044
+ return watch;
20045
+ }
20046
+ indexWatch(watch) {
20047
+ const ids = this.watchIdsByConversation.get(watch.conversationId) ?? new Set;
20048
+ ids.add(watch.watchId);
20049
+ this.watchIdsByConversation.set(watch.conversationId, ids);
20050
+ }
20051
+ closeWatchInternal(watchId) {
20052
+ const watch = this.watches.get(watchId);
20053
+ if (!watch) {
20054
+ return;
20055
+ }
20056
+ this.watches.delete(watchId);
20057
+ this.watchIdsByKey.delete(watchKey(watch.conversationId, watch.watcherNodeId, watch.watcherId));
20058
+ const ids = this.watchIdsByConversation.get(watch.conversationId);
20059
+ if (ids) {
20060
+ ids.delete(watchId);
20061
+ if (ids.size === 0) {
20062
+ this.watchIdsByConversation.delete(watch.conversationId);
20063
+ }
20064
+ }
20065
+ for (const client of watch.clients) {
20066
+ client.end();
20067
+ }
20068
+ watch.clients.clear();
20069
+ }
20070
+ }
20071
+ // packages/runtime/src/mobile-push.ts
20072
+ init_support_paths();
20073
+ // packages/web/server/core/observe/service.ts
20074
+ function activeEndpoint(snapshot, agentId) {
20075
+ const candidates = Object.values(snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === agentId);
20076
+ const rank = (state) => {
20077
+ switch (state) {
20078
+ case "active":
20079
+ return 0;
20080
+ case "idle":
20081
+ return 1;
20082
+ case "waiting":
20083
+ return 2;
20084
+ case "offline":
20085
+ return 5;
20086
+ default:
20087
+ return 4;
20088
+ }
20089
+ };
20090
+ return [...candidates].sort((left, right) => rank(left.state) - rank(right.state))[0] ?? null;
20091
+ }
20092
+ function expandHome(value) {
20093
+ if (!value) {
20094
+ return null;
20095
+ }
20096
+ if (value === "~") {
20097
+ return homedir12();
20098
+ }
20099
+ if (value.startsWith("~/")) {
20100
+ return join18(homedir12(), value.slice(2));
20101
+ }
20102
+ return value;
20103
+ }
20104
+ function encodeClaudeProjectsSlug2(absolutePath) {
20105
+ const normalized = resolve8(absolutePath);
20106
+ return `-${normalized.replace(/^\//u, "").replace(/\//gu, "-")}`;
20107
+ }
20108
+ function resolveClaudeHistoryPath(cwd, sessionId) {
20109
+ const normalizedCwd = expandHome(cwd)?.trim();
20110
+ const normalizedSessionId = sessionId?.trim().replace(/\.jsonl$/u, "") || "";
20111
+ if (!normalizedCwd || !normalizedSessionId) {
20112
+ return null;
20113
+ }
20114
+ return join18(homedir12(), ".claude", "projects", encodeClaudeProjectsSlug2(normalizedCwd), `${normalizedSessionId}.jsonl`);
20115
+ }
20116
+ function historyAdapterAlias(value) {
20117
+ const normalized = value?.trim().toLowerCase();
20118
+ if (!normalized) {
20119
+ return null;
20120
+ }
20121
+ if (normalized === "claude" || normalized === "claude-code" || normalized === "claude_stream_json") {
20122
+ return "claude-code";
20123
+ }
20124
+ if (normalized === "codex" || normalized === "codex_app_server") {
20125
+ return "codex";
20126
+ }
20127
+ return null;
20128
+ }
20129
+ function snapshotProviderMeta(snapshot) {
20130
+ const providerMeta = snapshot.session.providerMeta;
20131
+ return providerMeta && typeof providerMeta === "object" ? providerMeta : {};
20132
+ }
20133
+ function firstString(...values) {
20134
+ for (const value of values) {
20135
+ if (typeof value === "string" && value.trim().length > 0) {
20136
+ return value.trim();
20137
+ }
20138
+ }
20139
+ return null;
20140
+ }
20141
+ function resolveHistoryCandidate(agent, snapshot) {
20142
+ const snapshotAdapter = historyAdapterAlias(snapshot?.session.adapterType);
20143
+ const agentAdapter = historyAdapterAlias(agent.harness);
20144
+ const providerMeta = snapshot ? snapshotProviderMeta(snapshot) : {};
20145
+ const directPath = firstString(providerMeta.resumeSessionPath, providerMeta.threadPath);
20146
+ if (directPath) {
20147
+ const adapterType = snapshotAdapter ?? agentAdapter;
20148
+ if (adapterType) {
20149
+ return { path: directPath, adapterType };
20150
+ }
20151
+ }
20152
+ const claudeSessionId = firstString(providerMeta.transportSessionId, agent.harnessSessionId);
20153
+ const claudeCwd = firstString(snapshot?.session.cwd, agent.cwd, agent.projectRoot);
20154
+ if ((snapshotAdapter ?? agentAdapter) === "claude-code") {
20155
+ const path = resolveClaudeHistoryPath(claudeCwd, claudeSessionId);
20156
+ if (path) {
20157
+ return { path, adapterType: "claude-code" };
20158
+ }
20159
+ }
20160
+ return null;
20161
+ }
20162
+ function normalizeTimedEvents(events2) {
20163
+ if (!events2) {
20164
+ return [];
20165
+ }
20166
+ return events2.map((entry) => ({
20167
+ timestamp: entry.capturedAt,
20168
+ event: entry.event
20169
+ }));
20170
+ }
20171
+ function readHistorySnapshot(candidate) {
20172
+ if (!candidate) {
20173
+ return null;
20174
+ }
20175
+ if (!supportsHistorySessionSnapshotForPath(candidate.path, candidate.adapterType)) {
20176
+ return null;
20177
+ }
20178
+ if (!existsSync11(candidate.path)) {
20179
+ return null;
20180
+ }
20181
+ const stat5 = statSync4(candidate.path);
20182
+ const replay = createHistorySessionSnapshot({
20183
+ path: candidate.path,
20184
+ content: readFileSync7(candidate.path, "utf8"),
20185
+ adapterType: candidate.adapterType,
20186
+ baseTimestampMs: stat5.mtimeMs
20187
+ });
20188
+ return {
20189
+ historyPath: candidate.path,
20190
+ snapshot: replay.snapshot,
20191
+ timedEvents: normalizeTimedEvents(replay.events)
20192
+ };
20193
+ }
20194
+ async function readLiveSnapshot(endpoint) {
20195
+ if (!endpoint?.sessionId) {
20196
+ return null;
20197
+ }
20198
+ if (endpoint.transport === "pairing_bridge") {
20199
+ return await getScoutWebPairingSessionSnapshot(endpoint.sessionId);
20200
+ }
20201
+ if (endpoint.transport === "codex_app_server" || endpoint.transport === "claude_stream_json") {
20202
+ return await getLocalAgentEndpointSessionSnapshot(endpoint);
20203
+ }
20204
+ return null;
20205
+ }
20206
+ function summarizeCommandOutput(output) {
20207
+ const lines = output.split(/\r?\n/u).map((line) => line.trimEnd()).filter((line) => line.length > 0);
20208
+ if (lines.length === 0) {
20209
+ return;
20210
+ }
20211
+ return lines.slice(-12);
20212
+ }
20213
+ function truncatePreview(value, maxLines = 6, maxChars = 400) {
20214
+ const lines = value.split(/\r?\n/u).map((line) => line.trimEnd()).filter((line) => line.length > 0).slice(0, maxLines);
20215
+ const joined = lines.join(`
20216
+ `);
20217
+ return joined.length <= maxChars ? joined : `${joined.slice(0, maxChars - 1).trimEnd()}\u2026`;
20218
+ }
20219
+ function countDiffStats(value) {
20220
+ let add = 0;
20221
+ let del = 0;
20222
+ for (const line of value.split(/\r?\n/u)) {
20223
+ if (line.startsWith("+++ ") || line.startsWith("--- ")) {
20224
+ continue;
20225
+ }
20226
+ if (line.startsWith("+")) {
20227
+ add += 1;
20228
+ } else if (line.startsWith("-")) {
20229
+ del += 1;
20230
+ }
20231
+ }
20232
+ return { add, del };
20233
+ }
20234
+ function toolArgSummary(input) {
20235
+ if (typeof input === "string") {
20236
+ return input;
20237
+ }
20238
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
20239
+ return "";
20240
+ }
20241
+ const record = input;
20242
+ const preferred = firstString(record.file_path, record.path, record.command, record.pattern, record.query, record.glob, record.prompt, record.description);
20243
+ if (preferred) {
20244
+ return preferred;
20245
+ }
20246
+ try {
20247
+ return JSON.stringify(record);
20248
+ } catch {
20249
+ return "";
20250
+ }
20251
+ }
20252
+ function normalizeToolName(toolName) {
20253
+ switch (toolName.trim().toLowerCase()) {
20254
+ case "read":
20255
+ case "view":
20256
+ return "read";
20257
+ case "grep":
20258
+ case "glob":
20259
+ case "search":
20260
+ return "grep";
20261
+ case "edit":
20262
+ case "multiedit":
20263
+ return "edit";
20264
+ case "write":
20265
+ return "write";
20266
+ case "bash":
20267
+ return "bash";
20268
+ case "agent":
20269
+ return "agent";
20270
+ default:
20271
+ return toolName.trim().toLowerCase() || "tool";
20272
+ }
20273
+ }
20274
+ function fileStatePriority(state) {
20275
+ switch (state) {
20276
+ case "created":
20277
+ return 3;
20278
+ case "modified":
20279
+ return 2;
20280
+ case "read":
20281
+ return 1;
20282
+ }
20283
+ }
20284
+ function addTouchedFile(files, path, state, lastT) {
20285
+ const normalizedPath = path.trim();
20286
+ if (!normalizedPath) {
20287
+ return;
20288
+ }
20289
+ const existing = files.get(normalizedPath);
20290
+ if (!existing) {
20291
+ files.set(normalizedPath, {
20292
+ path: normalizedPath,
20293
+ state,
20294
+ touches: 1,
20295
+ lastT
20296
+ });
20297
+ return;
20298
+ }
20299
+ existing.touches += 1;
20300
+ existing.lastT = Math.max(existing.lastT, lastT);
20301
+ if (fileStatePriority(state) > fileStatePriority(existing.state)) {
20302
+ existing.state = state;
20303
+ }
20304
+ }
20305
+ function detectFileTouches(block, t, files) {
20306
+ if (block.type !== "action") {
20307
+ return;
20308
+ }
20309
+ const { action } = block;
20310
+ if (action.kind === "file_change" && action.path.trim()) {
20311
+ addTouchedFile(files, action.path, "modified", t);
20312
+ return;
20313
+ }
20314
+ if (action.kind !== "tool_call") {
20315
+ return;
20316
+ }
20317
+ const toolName = normalizeToolName(action.toolName);
20318
+ const input = action.input;
20319
+ const path = firstString(input?.file_path, input?.path);
20320
+ if (!path) {
20321
+ return;
20322
+ }
20323
+ if (toolName === "read" || toolName === "grep") {
20324
+ addTouchedFile(files, path, "read", t);
20325
+ return;
20326
+ }
20327
+ if (toolName === "write") {
20328
+ addTouchedFile(files, path, "created", t);
20329
+ return;
20330
+ }
20331
+ if (toolName === "edit") {
20332
+ addTouchedFile(files, path, "modified", t);
20333
+ }
20334
+ }
20335
+ function buildTimingLookup(snapshot, timedEvents) {
20336
+ const turnStartedAtMs = new Map;
20337
+ const turnEndedAtMs = new Map;
20338
+ const blockStartedAtMs = new Map;
20339
+ const questionAnsweredAtMs = new Map;
20340
+ let bootTimestampMs = Number.POSITIVE_INFINITY;
20341
+ for (const entry of timedEvents) {
20342
+ if (entry.event.event === "session:update") {
20343
+ bootTimestampMs = Math.min(bootTimestampMs, entry.timestamp);
20344
+ continue;
20345
+ }
20346
+ if (entry.event.event === "turn:start") {
20347
+ turnStartedAtMs.set(entry.event.turn.id, entry.timestamp);
20348
+ bootTimestampMs = Math.min(bootTimestampMs, entry.timestamp);
20349
+ continue;
20350
+ }
20351
+ if (entry.event.event === "turn:end" || entry.event.event === "turn:error") {
20352
+ turnEndedAtMs.set(entry.event.turnId, entry.timestamp);
20353
+ continue;
20354
+ }
20355
+ if (entry.event.event === "block:start") {
20356
+ blockStartedAtMs.set(entry.event.block.id, entry.timestamp);
20357
+ continue;
20358
+ }
20359
+ if (entry.event.event === "block:question:answer") {
20360
+ questionAnsweredAtMs.set(entry.event.blockId, entry.timestamp);
20361
+ }
20362
+ }
20363
+ for (const turn of snapshot.turns) {
20364
+ if (!turnStartedAtMs.has(turn.id) && Number.isFinite(turn.startedAt)) {
20365
+ turnStartedAtMs.set(turn.id, turn.startedAt);
20366
+ }
20367
+ if (!turnEndedAtMs.has(turn.id) && Number.isFinite(turn.endedAt ?? NaN)) {
20368
+ turnEndedAtMs.set(turn.id, turn.endedAt);
20369
+ }
20370
+ bootTimestampMs = Math.min(bootTimestampMs, turnStartedAtMs.get(turn.id) ?? Number.POSITIVE_INFINITY);
20371
+ }
20372
+ if (!Number.isFinite(bootTimestampMs)) {
20373
+ bootTimestampMs = Date.now();
20374
+ }
20375
+ return {
20376
+ baseTimestampMs: bootTimestampMs,
20377
+ bootTimestampMs,
20378
+ turnStartedAtMs,
20379
+ turnEndedAtMs,
20380
+ blockStartedAtMs,
20381
+ questionAnsweredAtMs
20382
+ };
20383
+ }
20384
+ function blockTimestampMs(turnId, block, turnBlockCount, timing) {
20385
+ const explicit = timing.blockStartedAtMs.get(block.id);
20386
+ if (typeof explicit === "number" && Number.isFinite(explicit)) {
20387
+ return explicit;
20388
+ }
20389
+ const turnStart = timing.turnStartedAtMs.get(turnId) ?? timing.baseTimestampMs;
20390
+ const turnEnd = timing.turnEndedAtMs.get(turnId) ?? turnStart + Math.max(3000, turnBlockCount * 2000);
20391
+ const duration = Math.max(1000, turnEnd - turnStart);
20392
+ const step = duration / Math.max(1, turnBlockCount + 1);
20393
+ return Math.round(turnStart + step * (block.index + 1));
20394
+ }
20395
+ function timelineSeconds(values, baseTimestampMs) {
20396
+ let last = 0;
20397
+ return values.map((timestamp) => {
20398
+ const seconds = Math.max(0, Math.round((timestamp - baseTimestampMs) / 1000));
20399
+ last = Math.max(last, seconds);
20400
+ return last;
20401
+ });
20402
+ }
20403
+ function syntheticContextUsage(eventCount) {
20404
+ if (eventCount <= 0) {
20405
+ return [];
20406
+ }
20407
+ const length = Math.max(2, Math.min(eventCount, 24));
20408
+ return Array.from({ length }, (_, index2) => {
20409
+ const progress = length <= 1 ? 1 : index2 / (length - 1);
20410
+ return Math.min(0.92, 0.08 + progress * 0.66);
20411
+ });
20412
+ }
20413
+ function buildObserveDataFromSnapshot(snapshot, timedEvents = [], live = false) {
20414
+ const timing = buildTimingLookup(snapshot, timedEvents);
20415
+ const files = new Map;
20416
+ const eventDrafts = [
20417
+ {
20418
+ timestampMs: timing.bootTimestampMs,
20419
+ build: (t) => ({
20420
+ id: `${snapshot.session.id}:boot`,
20421
+ t,
20422
+ kind: "boot",
20423
+ text: `Session started \xB7 ${snapshot.session.model ?? snapshot.session.adapterType}`,
20424
+ detail: [
20425
+ snapshot.session.cwd ? `workspace: ${snapshot.session.cwd}` : null,
20426
+ `turns: ${snapshot.turns.length}`,
20427
+ `status: ${snapshot.session.status}`
20428
+ ].filter(Boolean).join(" \xB7 ")
20429
+ })
20430
+ }
20431
+ ];
20432
+ for (const turn of snapshot.turns) {
20433
+ const turnBlockCount = turn.blocks.length;
20434
+ for (const blockState of turn.blocks) {
20435
+ const { block } = blockState;
20436
+ const timestampMs = blockTimestampMs(turn.id, block, turnBlockCount, timing);
20437
+ eventDrafts.push({
20438
+ timestampMs,
20439
+ block,
20440
+ build: (t) => {
20441
+ switch (block.type) {
20442
+ case "reasoning":
20443
+ return {
20444
+ id: block.id,
20445
+ t,
20446
+ kind: "think",
20447
+ text: block.text.trim(),
20448
+ live: live && snapshot.currentTurnId === turn.id && blockState.status === "streaming"
20449
+ };
20450
+ case "text":
20451
+ return {
20452
+ id: block.id,
20453
+ t,
20454
+ kind: "message",
20455
+ text: block.text.trim(),
20456
+ to: "human"
20457
+ };
20458
+ case "question": {
20459
+ const answeredAt = timing.questionAnsweredAtMs.get(block.id);
20460
+ return {
20461
+ id: block.id,
20462
+ t,
20463
+ kind: "ask",
20464
+ text: block.question.trim(),
20465
+ to: "human",
20466
+ answer: block.answer?.join(", "),
20467
+ answerT: answeredAt ? Math.max(t, Math.round((answeredAt - timing.baseTimestampMs) / 1000)) : undefined
20468
+ };
20469
+ }
20470
+ case "error":
20471
+ return {
20472
+ id: block.id,
20473
+ t,
20474
+ kind: "system",
20475
+ text: block.message,
20476
+ detail: block.code
20477
+ };
20478
+ case "file":
20479
+ return {
20480
+ id: block.id,
20481
+ t,
20482
+ kind: "note",
20483
+ text: block.name ? `Generated file ${block.name}` : "Generated file artifact"
20484
+ };
20485
+ case "action": {
20486
+ if (block.action.kind === "command") {
20487
+ return {
20488
+ id: block.id,
20489
+ t,
20490
+ kind: "tool",
20491
+ text: "",
20492
+ tool: "bash",
20493
+ arg: block.action.command,
20494
+ stream: summarizeCommandOutput(block.action.output)
20495
+ };
20496
+ }
20497
+ if (block.action.kind === "file_change") {
20498
+ const diffBody = block.action.diff?.trim() || block.action.output.trim();
20499
+ const diff = diffBody ? {
20500
+ ...countDiffStats(diffBody),
20501
+ preview: truncatePreview(diffBody)
20502
+ } : undefined;
20503
+ return {
20504
+ id: block.id,
20505
+ t,
20506
+ kind: "tool",
20507
+ text: "",
20508
+ tool: block.action.path ? "edit" : "write",
20509
+ arg: block.action.path,
20510
+ ...diff ? { diff } : {}
20511
+ };
20512
+ }
20513
+ if (block.action.kind === "subagent") {
20514
+ return {
20515
+ id: block.id,
20516
+ t,
20517
+ kind: "tool",
20518
+ text: "",
20519
+ tool: "agent",
20520
+ arg: block.action.agentName ?? block.action.agentId,
20521
+ stream: summarizeCommandOutput(block.action.output)
20522
+ };
20523
+ }
20524
+ return {
20525
+ id: block.id,
20526
+ t,
20527
+ kind: "tool",
20528
+ text: "",
20529
+ tool: normalizeToolName(block.action.toolName),
20530
+ arg: toolArgSummary(block.action.input),
20531
+ stream: summarizeCommandOutput(block.action.output)
20532
+ };
20533
+ }
20534
+ }
20535
+ }
20536
+ });
20537
+ }
20538
+ }
20539
+ const seconds = timelineSeconds(eventDrafts.map((draft) => draft.timestampMs), timing.baseTimestampMs);
20540
+ const builtEntries = eventDrafts.map((draft, index2) => ({
20541
+ draft,
20542
+ event: draft.build(seconds[index2] ?? 0)
20543
+ })).filter(({ event }) => event.text.length > 0 || event.kind === "tool" || event.kind === "boot");
20544
+ const events2 = builtEntries.map((entry) => entry.event);
20545
+ for (const { draft, event } of builtEntries) {
20546
+ if (draft.block) {
20547
+ detectFileTouches(draft.block, event.t, files);
20548
+ }
20549
+ }
20550
+ return {
20551
+ events: events2,
20552
+ files: [...files.values()].sort((left, right) => right.lastT - left.lastT || left.path.localeCompare(right.path)),
20553
+ contextUsage: syntheticContextUsage(events2.length),
20554
+ live
20555
+ };
20556
+ }
20557
+ function unavailableObserveData(agent) {
20558
+ return {
20559
+ events: [
20560
+ {
20561
+ id: `${agent.id}:unavailable`,
20562
+ t: 0,
20563
+ kind: "system",
20564
+ text: "No session trace is available for this agent yet.",
20565
+ detail: agent.harness ? `${agent.harness} \xB7 waiting for a live session or a readable history file` : "waiting for a live session or a readable history file"
20566
+ }
20567
+ ],
20568
+ files: [],
20569
+ contextUsage: [],
20570
+ live: false
20571
+ };
20572
+ }
20573
+ async function resolveSnapshotSource(agent) {
20574
+ const broker2 = await loadScoutBrokerContext();
20575
+ const endpoint = broker2 ? activeEndpoint(broker2.snapshot, agent.id) : null;
20576
+ const liveSnapshot = await readLiveSnapshot(endpoint);
20577
+ const live = Boolean(liveSnapshot && (liveSnapshot.currentTurnId || liveSnapshot.session.status === "active"));
20578
+ const historyCandidate = resolveHistoryCandidate(agent, liveSnapshot);
20579
+ const historySnapshot = readHistorySnapshot(historyCandidate);
20580
+ if (historySnapshot) {
20581
+ return {
20582
+ source: "history",
20583
+ historyPath: historySnapshot.historyPath,
20584
+ snapshot: historySnapshot.snapshot,
20585
+ timedEvents: historySnapshot.timedEvents,
20586
+ live,
20587
+ sessionId: liveSnapshot?.session.id ?? endpoint?.sessionId ?? null
20588
+ };
20589
+ }
20590
+ if (liveSnapshot) {
20591
+ return {
20592
+ source: "live",
20593
+ historyPath: historyCandidate?.path ?? null,
20594
+ snapshot: liveSnapshot,
20595
+ timedEvents: [],
20596
+ live,
20597
+ sessionId: liveSnapshot.session.id
20598
+ };
20599
+ }
20600
+ const agentHistorySnapshot = readHistorySnapshot(resolveHistoryCandidate(agent, null));
20601
+ if (agentHistorySnapshot) {
20602
+ return {
20603
+ source: "history",
20604
+ historyPath: agentHistorySnapshot.historyPath,
20605
+ snapshot: agentHistorySnapshot.snapshot,
20606
+ timedEvents: agentHistorySnapshot.timedEvents,
20607
+ live: false,
20608
+ sessionId: null
20609
+ };
20610
+ }
20611
+ return {
20612
+ source: "unavailable",
20613
+ historyPath: null,
20614
+ live: false,
20615
+ sessionId: null
20616
+ };
20617
+ }
20618
+ async function loadAgentObservePayload(agentId) {
20619
+ const agent = queryAgents(200).find((entry) => entry.id === agentId) ?? null;
20620
+ if (!agent) {
20621
+ return null;
20622
+ }
20623
+ const source = await resolveSnapshotSource(agent);
20624
+ if (source.source === "unavailable") {
20625
+ return {
20626
+ agentId,
20627
+ source: "unavailable",
20628
+ fidelity: "synthetic",
20629
+ historyPath: null,
20630
+ sessionId: null,
20631
+ updatedAt: Date.now(),
20632
+ data: unavailableObserveData(agent)
20633
+ };
20634
+ }
20635
+ const fidelity = source.timedEvents.length > 0 ? "timestamped" : "synthetic";
20636
+ return {
20637
+ agentId,
20638
+ source: source.source,
20639
+ fidelity,
20640
+ historyPath: source.historyPath,
20641
+ sessionId: source.sessionId,
20642
+ updatedAt: Date.now(),
20643
+ data: buildObserveDataFromSnapshot(source.snapshot, source.timedEvents, source.live)
20644
+ };
20645
+ }
20646
+
20647
+ // packages/web/server/core/mesh/service.ts
20648
+ await init_broker_service();
20649
+ function normalizeHost(host) {
20650
+ return host.trim().replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
20651
+ }
20652
+ function isLoopbackHost(host) {
20653
+ const normalized = normalizeHost(host);
20654
+ return normalized === "127.0.0.1" || normalized === "::1" || normalized === "localhost";
20655
+ }
20656
+ function isWildcardHost(host) {
20657
+ const normalized = normalizeHost(host);
20658
+ return normalized === "0.0.0.0" || normalized === "::";
20659
+ }
20660
+ function isPeerReachableBrokerUrl(url) {
20661
+ if (!url) {
20662
+ return false;
20663
+ }
20664
+ try {
20665
+ const hostname2 = new URL(url).hostname;
20666
+ return !isLoopbackHost(hostname2) && !isWildcardHost(hostname2);
20667
+ } catch {
20668
+ return false;
20669
+ }
20670
+ }
20671
+ function stripTrailingDot(value) {
20672
+ const trimmed = value?.trim();
20673
+ if (!trimmed) {
20674
+ return null;
20675
+ }
20676
+ return trimmed.replace(/\.$/, "");
20677
+ }
20678
+ async function readTailscaleStatus() {
20679
+ const peers = await readTailscalePeers();
20680
+ return {
20681
+ available: peers.length > 0,
20682
+ peers,
20683
+ onlineCount: peers.filter((p) => p.online).length
20684
+ };
20685
+ }
20686
+ function formatIssueWarning(issue) {
20687
+ return issue.action ? `${issue.title} \u2014 ${issue.summary} ${issue.action}` : `${issue.title} \u2014 ${issue.summary}`;
20688
+ }
20689
+ function computeIssues(health, localNode, nodes, tailscale2) {
20690
+ const issues = [];
20691
+ if (!health.reachable) {
20692
+ issues.push({
20693
+ code: "broker_unreachable",
20694
+ severity: "error",
20695
+ title: "Broker not reachable",
20696
+ summary: "The mesh page cannot reach the local broker yet, so peer status is incomplete.",
20697
+ action: "Start the broker, then reload this page.",
20698
+ actionCommand: null
20699
+ });
20700
+ return issues;
20701
+ }
20702
+ if (localNode?.advertiseScope === "local") {
20703
+ issues.push({
17075
20704
  code: "local_only",
17076
20705
  severity: "warning",
17077
20706
  title: "Not announced to peers",
@@ -17090,7 +20719,7 @@ function computeIssues(health, localNode, nodes, tailscale) {
17090
20719
  });
17091
20720
  }
17092
20721
  const remoteNodes = Object.values(nodes).filter((n) => n.id !== localNode?.id);
17093
- if (!tailscale.available && remoteNodes.length === 0) {
20722
+ if (!tailscale2.available && remoteNodes.length === 0) {
17094
20723
  issues.push({
17095
20724
  code: "discovery_unconfigured",
17096
20725
  severity: "warning",
@@ -17179,7 +20808,7 @@ function filterCurrentMeshNodes(allNodes, meshId, localNodeId, now) {
17179
20808
  }
17180
20809
  async function loadMeshStatus() {
17181
20810
  const brokerUrl = resolveScoutBrokerUrl();
17182
- const [health, context, tailscale] = await Promise.all([
20811
+ const [health, context, tailscale2] = await Promise.all([
17183
20812
  readScoutBrokerHealth(brokerUrl),
17184
20813
  loadScoutBrokerContext(brokerUrl),
17185
20814
  readTailscaleStatus()
@@ -17189,9 +20818,9 @@ async function loadMeshStatus() {
17189
20818
  const meshId = health.meshId ?? localNode?.meshId ?? null;
17190
20819
  const identity = computeIdentitySummary(health, localNode, meshId);
17191
20820
  const nodes = filterCurrentMeshNodes(allNodes, meshId, localNode?.id, Date.now());
17192
- const issues = computeIssues(health, localNode, nodes, tailscale);
20821
+ const issues = computeIssues(health, localNode, nodes, tailscale2);
17193
20822
  const warnings = issues.map(formatIssueWarning);
17194
- return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale, issues, warnings };
20823
+ return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale: tailscale2, issues, warnings };
17195
20824
  }
17196
20825
  function preferredAnnounceHost(self, currentBrokerUrl) {
17197
20826
  const dnsName = stripTrailingDot(self?.dnsName);
@@ -17316,10 +20945,10 @@ function resolveConversationRouting(conversationId) {
17316
20945
  return { directAgentId, senderId };
17317
20946
  }
17318
20947
  function resolveBundledStaticClientRoot(moduleUrl = import.meta.url) {
17319
- return resolve8(dirname11(fileURLToPath7(moduleUrl)), "client");
20948
+ return resolve9(dirname12(fileURLToPath7(moduleUrl)), "client");
17320
20949
  }
17321
20950
  function resolveSourceStaticClientRoot(moduleUrl = import.meta.url) {
17322
- return resolve8(dirname11(fileURLToPath7(moduleUrl)), "../dist/client");
20951
+ return resolve9(dirname12(fileURLToPath7(moduleUrl)), "../dist/client");
17323
20952
  }
17324
20953
  function resolveStaticRoot(staticRoot) {
17325
20954
  const configured = staticRoot?.trim();
@@ -17327,7 +20956,7 @@ function resolveStaticRoot(staticRoot) {
17327
20956
  return configured;
17328
20957
  }
17329
20958
  const bundled = resolveBundledStaticClientRoot(import.meta.url);
17330
- if (existsSync11(resolve8(bundled, "index.html"))) {
20959
+ if (existsSync12(resolve9(bundled, "index.html"))) {
17331
20960
  return bundled;
17332
20961
  }
17333
20962
  return resolveSourceStaticClientRoot(import.meta.url);
@@ -17365,6 +20994,10 @@ async function createOpenScoutWebServer(options) {
17365
20994
  app.get("/api/shell-state", async (c) => c.json(await shellStateCache.get()));
17366
20995
  app.get("/api/shell-state/refresh", async (c) => c.json(await shellStateCache.refresh()));
17367
20996
  app.get("/api/agents", (c) => c.json(queryAgents()));
20997
+ app.get("/api/agents/:id/observe", async (c) => {
20998
+ const payload = await loadAgentObservePayload(c.req.param("id"));
20999
+ return payload ? c.json(payload) : c.json({ error: "not found" }, 404);
21000
+ });
17368
21001
  app.get("/api/activity", (c) => c.json(queryActivity()));
17369
21002
  app.get("/api/heartrate", (c) => c.json(queryHeartrate()));
17370
21003
  app.get("/api/fleet", (c) => c.json(queryFleet({
@@ -17608,8 +21241,8 @@ async function createOpenScoutWebServer(options) {
17608
21241
  // ../hudson/packages/hudson-relay/src/relay/session.ts
17609
21242
  import { createRequire } from "module";
17610
21243
  import { execSync as execSync2 } from "child_process";
17611
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
17612
- import { join as join18, dirname as pathDirname } from "path";
21244
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
21245
+ import { join as join19, dirname as pathDirname } from "path";
17613
21246
  var require2 = createRequire(import.meta.url);
17614
21247
  var pty = require2("node-pty");
17615
21248
  var DEFAULT_ORPHAN_TTL_MS = 30 * 60 * 1000;
@@ -17626,7 +21259,7 @@ function send(ws, data) {
17626
21259
  function resolveCwd(raw2) {
17627
21260
  const home = process.env.HOME || "/tmp";
17628
21261
  const expanded = (raw2 || home).replace(/^~/, home);
17629
- if (existsSync12(expanded))
21262
+ if (existsSync13(expanded))
17630
21263
  return expanded;
17631
21264
  try {
17632
21265
  mkdirSync6(expanded, { recursive: true });
@@ -17662,11 +21295,11 @@ function tmuxSessionExists2(name) {
17662
21295
  }
17663
21296
  function bootstrapFiles(cwd, files, sessionId) {
17664
21297
  for (const [relPath, content] of Object.entries(files)) {
17665
- const absPath = join18(cwd, relPath);
17666
- if (!existsSync12(absPath)) {
21298
+ const absPath = join19(cwd, relPath);
21299
+ if (!existsSync13(absPath)) {
17667
21300
  try {
17668
21301
  const dir = pathDirname(absPath);
17669
- if (!existsSync12(dir))
21302
+ if (!existsSync13(dir))
17670
21303
  mkdirSync6(dir, { recursive: true });
17671
21304
  writeFileSync6(absPath, content, "utf-8");
17672
21305
  console.log(`[relay] Session ${sessionId}: bootstrapped ${relPath}`);
@@ -17721,7 +21354,7 @@ function createSession(ws, msg) {
17721
21354
  return null;
17722
21355
  }
17723
21356
  }
17724
- if (!existsSync12(agentBin)) {
21357
+ if (!existsSync13(agentBin)) {
17725
21358
  const reason = `${agent} binary not found at ${agentBin}`;
17726
21359
  console.error(`[relay] Session ${id} failed: ${reason}`);
17727
21360
  send(ws, { type: "session:error", error: reason });
@@ -17881,7 +21514,7 @@ function destroy(sessionId) {
17881
21514
 
17882
21515
  // packages/web/server/relay.ts
17883
21516
  import { mkdir as mkdir7 } from "fs/promises";
17884
- import { join as join19 } from "path";
21517
+ import { join as join20 } from "path";
17885
21518
  import { randomUUID as randomUUID3 } from "crypto";
17886
21519
  var UPLOAD_DIR = "/tmp/scout-uploads";
17887
21520
  async function handleRelayUpload(req) {
@@ -17891,7 +21524,7 @@ async function handleRelayUpload(req) {
17891
21524
  }
17892
21525
  await mkdir7(UPLOAD_DIR, { recursive: true });
17893
21526
  const filename = `${randomUUID3()}-${name}`;
17894
- const filepath = join19(UPLOAD_DIR, filename);
21527
+ const filepath = join20(UPLOAD_DIR, filename);
17895
21528
  await Bun.write(filepath, Buffer.from(data, "base64"));
17896
21529
  return Response.json({ path: filepath });
17897
21530
  }
@@ -17996,13 +21629,13 @@ function resolveStaticRoot2() {
17996
21629
  if (process.env.OPENSCOUT_WEB_STATIC_ROOT?.trim()) {
17997
21630
  return process.env.OPENSCOUT_WEB_STATIC_ROOT.trim();
17998
21631
  }
17999
- const selfDir = dirname12(fileURLToPath8(import.meta.url));
18000
- const siblingClientRoot = join20(selfDir, "client");
18001
- if (existsSync13(join20(siblingClientRoot, "index.html"))) {
21632
+ const selfDir = dirname13(fileURLToPath8(import.meta.url));
21633
+ const siblingClientRoot = join21(selfDir, "client");
21634
+ if (existsSync14(join21(siblingClientRoot, "index.html"))) {
18002
21635
  return siblingClientRoot;
18003
21636
  }
18004
- const sourceDistClientRoot = resolve9(selfDir, "../dist/client");
18005
- if (existsSync13(join20(sourceDistClientRoot, "index.html"))) {
21637
+ const sourceDistClientRoot = resolve10(selfDir, "../dist/client");
21638
+ if (existsSync14(join21(sourceDistClientRoot, "index.html"))) {
18006
21639
  return sourceDistClientRoot;
18007
21640
  }
18008
21641
  return;