@openscout/scout 0.2.54 → 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();
@@ -4388,7 +5094,14 @@ var init_dist = __esm(() => {
4388
5094
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4389
5095
  */
4390
5096
  });
4391
- // packages/protocol/src/agent-identity.ts
5097
+
5098
+ // packages/protocol/dist/common.js
5099
+ var init_common = () => {};
5100
+
5101
+ // packages/protocol/dist/actors.js
5102
+ var init_actors = () => {};
5103
+
5104
+ // packages/protocol/dist/agent-identity.js
4392
5105
  function trimIdentityPrefix(value) {
4393
5106
  return value.trim().replace(/^@+/, "").replace(/[.,!?;)\]]+$/, "");
4394
5107
  }
@@ -4738,17 +5451,17 @@ var init_agent_identity = __esm(() => {
4738
5451
  resolveAgentSelector = resolveAgentIdentity;
4739
5452
  });
4740
5453
 
4741
- // packages/protocol/src/agent-address.ts
5454
+ // packages/protocol/dist/agent-address.js
4742
5455
  var init_agent_address = __esm(() => {
4743
5456
  init_agent_identity();
4744
5457
  });
4745
5458
 
4746
- // packages/protocol/src/agent-selectors.ts
5459
+ // packages/protocol/dist/agent-selectors.js
4747
5460
  var init_agent_selectors = __esm(() => {
4748
5461
  init_agent_address();
4749
5462
  });
4750
5463
 
4751
- // packages/protocol/src/scout-agent-card.ts
5464
+ // packages/protocol/dist/scout-agent-card.js
4752
5465
  function buildScoutReturnAddress(input) {
4753
5466
  const next = {
4754
5467
  actorId: input.actorId,
@@ -4783,16 +5496,163 @@ function buildScoutReturnAddress(input) {
4783
5496
  }
4784
5497
  return next;
4785
5498
  }
4786
- // packages/protocol/src/index.ts
4787
- var init_src2 = __esm(() => {
4788
- init_agent_identity();
4789
- init_agent_address();
4790
- init_agent_selectors();
4791
- });
5499
+ // packages/protocol/dist/mesh.js
5500
+ var init_mesh = () => {};
4792
5501
 
4793
- // packages/runtime/src/support-paths.ts
4794
- import { existsSync as existsSync3, mkdirSync, rmSync, writeFileSync } from "fs";
4795
- import { homedir as homedir4 } from "os";
5502
+ // packages/protocol/dist/conversations.js
5503
+ var init_conversations = () => {};
5504
+
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 = () => {};
5625
+
5626
+ // packages/protocol/dist/transports.js
5627
+ var init_transports = () => {};
5628
+
5629
+ // packages/protocol/dist/events.js
5630
+ var init_events = () => {};
5631
+
5632
+ // packages/protocol/dist/thread-events.js
5633
+ var init_thread_events = () => {};
5634
+
5635
+ // packages/protocol/dist/index.js
5636
+ var init_dist2 = __esm(() => {
5637
+ init_common();
5638
+ init_actors();
5639
+ init_agent_identity();
5640
+ init_agent_address();
5641
+ init_agent_selectors();
5642
+ init_mesh();
5643
+ init_conversations();
5644
+ init_messages();
5645
+ init_invocations();
5646
+ init_scout_dispatch();
5647
+ init_deliveries();
5648
+ init_transports();
5649
+ init_events();
5650
+ init_thread_events();
5651
+ });
5652
+
5653
+ // packages/runtime/src/support-paths.ts
5654
+ import { existsSync as existsSync3, mkdirSync, rmSync, writeFileSync } from "fs";
5655
+ import { homedir as homedir4 } from "os";
4796
5656
  import { join as join4 } from "path";
4797
5657
  function resolveOpenScoutSupportPaths() {
4798
5658
  const home = homedir4();
@@ -4842,9 +5702,9 @@ var init_support_paths = () => {};
4842
5702
  // packages/runtime/src/harness-catalog.ts
4843
5703
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
4844
5704
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
4845
- import { dirname as dirname2, join as join5 } from "path";
5705
+ import { dirname as dirname3, join as join5 } from "path";
4846
5706
  async function writeHarnessCatalogOverrides(overrides, overridePath = resolveOpenScoutSupportPaths().harnessCatalogPath) {
4847
- await mkdir2(dirname2(overridePath), { recursive: true });
5707
+ await mkdir2(dirname3(overridePath), { recursive: true });
4848
5708
  const payload = {
4849
5709
  version: HARNESS_CATALOG_VERSION,
4850
5710
  entries: overrides,
@@ -4940,9 +5800,9 @@ var init_harness_catalog = __esm(() => {
4940
5800
  // packages/runtime/src/user-project-hints.ts
4941
5801
  import { readdir, readFile as readFile3, stat } from "fs/promises";
4942
5802
  import { homedir as homedir5 } from "os";
4943
- 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";
4944
5804
  import { fileURLToPath as fileURLToPath2 } from "url";
4945
- function decodeClaudeProjectsSlug2(name) {
5805
+ function decodeClaudeProjectsSlug3(name) {
4946
5806
  if (!name || name === "." || name === "..") {
4947
5807
  return null;
4948
5808
  }
@@ -4967,7 +5827,7 @@ async function resolveExistingDirectoryHint(pathLike) {
4967
5827
  return absolutePath;
4968
5828
  }
4969
5829
  if (info.isFile()) {
4970
- return dirname3(absolutePath);
5830
+ return dirname4(absolutePath);
4971
5831
  }
4972
5832
  } catch {
4973
5833
  return null;
@@ -4994,7 +5854,7 @@ async function findLikelyProjectRoot(absolutePath) {
4994
5854
  }
4995
5855
  }
4996
5856
  }
4997
- const parent = dirname3(current);
5857
+ const parent = dirname4(current);
4998
5858
  if (parent === current) {
4999
5859
  return lastCandidate ?? absolutePath;
5000
5860
  }
@@ -5135,7 +5995,7 @@ async function appendCursorWorkspacePaths(home, roots) {
5135
5995
  }
5136
5996
  const wsRaw = await readFile3(wsFile, "utf8");
5137
5997
  const wsDoc = JSON.parse(wsRaw);
5138
- const wsDir = dirname3(wsFile);
5998
+ const wsDir = dirname4(wsFile);
5139
5999
  for (const f of wsDoc.folders ?? []) {
5140
6000
  if (typeof f.path !== "string" || !f.path) {
5141
6001
  continue;
@@ -5156,7 +6016,7 @@ async function appendClaudeProjectSlugs(home, roots) {
5156
6016
  return;
5157
6017
  }
5158
6018
  for (const name of names) {
5159
- const decoded = decodeClaudeProjectsSlug2(name);
6019
+ const decoded = decodeClaudeProjectsSlug3(name);
5160
6020
  if (!decoded) {
5161
6021
  continue;
5162
6022
  }
@@ -5292,7 +6152,7 @@ import { execFileSync } from "child_process";
5292
6152
  import { existsSync as existsSync5, readFileSync } from "fs";
5293
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";
5294
6154
  import { homedir as homedir6, hostname, userInfo } from "os";
5295
- 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";
5296
6156
  import { fileURLToPath as fileURLToPath3 } from "url";
5297
6157
  function titleCaseWords(value) {
5298
6158
  return value.split(/[\s._-]+/).map((part) => part.trim()).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
@@ -5767,7 +6627,7 @@ async function readJsonFile(filePath) {
5767
6627
  }
5768
6628
  }
5769
6629
  async function writeJsonFile(filePath, value) {
5770
- await mkdir3(dirname4(filePath), { recursive: true });
6630
+ await mkdir3(dirname5(filePath), { recursive: true });
5771
6631
  await writeFile3(filePath, JSON.stringify(value, null, 2) + `
5772
6632
  `, "utf8");
5773
6633
  }
@@ -6012,7 +6872,7 @@ function applyImportedEnvironmentSeeds(config, codexImport, projectRoot) {
6012
6872
  ...(config.environment?.actions?.length ?? 0) === 0 && importedEnvironment.actions ? { actions: importedEnvironment.actions } : {}
6013
6873
  };
6014
6874
  }
6015
- const defaultProjectName = titleCase(basename2(projectRoot));
6875
+ const defaultProjectName = titleCase(basename3(projectRoot));
6016
6876
  if (codexImport?.name?.trim() && (!config.project.name?.trim() || config.project.name === defaultProjectName)) {
6017
6877
  next.project = {
6018
6878
  ...config.project,
@@ -6180,7 +7040,7 @@ async function seedWorkspaceRoots(options) {
6180
7040
  const roots = new Set;
6181
7041
  const currentProjectRoot = options.currentDirectory ? await findNearestProjectRoot(options.currentDirectory) : null;
6182
7042
  if (currentProjectRoot) {
6183
- roots.add(dirname4(currentProjectRoot));
7043
+ roots.add(dirname5(currentProjectRoot));
6184
7044
  }
6185
7045
  const legacyRoot = options.legacyRelayConfig?.projectRoot?.trim();
6186
7046
  if (legacyRoot) {
@@ -6188,7 +7048,7 @@ async function seedWorkspaceRoots(options) {
6188
7048
  }
6189
7049
  for (const record of Object.values(options.legacyAgents ?? {})) {
6190
7050
  if (record.cwd?.trim()) {
6191
- roots.add(dirname4(normalizePath(record.cwd)));
7051
+ roots.add(dirname5(normalizePath(record.cwd)));
6192
7052
  }
6193
7053
  }
6194
7054
  if (roots.size === 0) {
@@ -6279,7 +7139,7 @@ async function installScoutSkillToHarnesses() {
6279
7139
  continue;
6280
7140
  }
6281
7141
  try {
6282
- await mkdir3(dirname4(target), { recursive: true });
7142
+ await mkdir3(dirname5(target), { recursive: true });
6283
7143
  await writeFile3(target, content, "utf8");
6284
7144
  entries.push({ harness, target, status: "installed" });
6285
7145
  } catch (error) {
@@ -6380,7 +7240,7 @@ async function findNearestProjectRoot(startDirectory) {
6380
7240
  if (await pathExists2(projectConfigPath(current)) || await pathExists2(join7(current, "package.json")) || await pathExists2(join7(current, "AGENTS.md")) || await pathExists2(join7(current, "CLAUDE.md"))) {
6381
7241
  lastCandidate = current;
6382
7242
  }
6383
- const parent = dirname4(current);
7243
+ const parent = dirname5(current);
6384
7244
  if (parent === current) {
6385
7245
  return lastCandidate;
6386
7246
  }
@@ -6388,7 +7248,7 @@ async function findNearestProjectRoot(startDirectory) {
6388
7248
  }
6389
7249
  }
6390
7250
  function defaultProjectConfig(projectRoot, settings, preferredHarness) {
6391
- const projectName = basename2(projectRoot);
7251
+ const projectName = basename3(projectRoot);
6392
7252
  const definitionId = normalizeAgentId(projectName);
6393
7253
  const relativeRoot = relative(projectRoot, projectRoot) || ".";
6394
7254
  return {
@@ -6694,8 +7554,8 @@ function selectorFromInput(value) {
6694
7554
  }
6695
7555
  async function resolveManifestBackedAgent(projectRoot, config, settings, override) {
6696
7556
  const resolvedProjectRoot = resolveProjectRootFromConfig(projectRoot, config);
6697
- const projectName = config.project.name?.trim() || titleCase(config.project.id || basename2(resolvedProjectRoot));
6698
- 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));
6699
7559
  const definitionId = normalizeAgentId(config.agent?.id || override?.definitionId || fallbackDefinitionId);
6700
7560
  const detectedHarness = await detectPreferredHarness(resolvedProjectRoot, settings.agents.defaultHarness);
6701
7561
  const runtimeDefaults = config.agent?.runtime?.defaults;
@@ -6732,7 +7592,7 @@ async function resolveManifestBackedAgent(projectRoot, config, settings, overrid
6732
7592
  return mergeResolvedAgentConfig(base, override, settings);
6733
7593
  }
6734
7594
  async function resolveInferredAgent(projectRoot, settings, override) {
6735
- const projectName = basename2(projectRoot);
7595
+ const projectName = basename3(projectRoot);
6736
7596
  const definitionId = normalizeAgentId(override?.definitionId?.trim() || projectName);
6737
7597
  const detectedHarness = await detectPreferredHarness(projectRoot, settings.agents.defaultHarness);
6738
7598
  const defaultHarness = normalizeManagedHarness(override?.defaultHarness ?? override?.runtime?.harness, normalizeManagedHarness(detectedHarness, "claude"));
@@ -6768,7 +7628,7 @@ async function resolveInferredAgent(projectRoot, settings, override) {
6768
7628
  return mergeResolvedAgentConfig(base, override, settings);
6769
7629
  }
6770
7630
  async function buildProjectInventoryEntry(agent, sourceRoot) {
6771
- const manifestRoot = agent.projectConfigPath ? normalizePath(join7(dirname4(agent.projectConfigPath), "..")) : null;
7631
+ const manifestRoot = agent.projectConfigPath ? normalizePath(join7(dirname5(agent.projectConfigPath), "..")) : null;
6772
7632
  const manifest = manifestRoot ? await readProjectConfig(manifestRoot) : null;
6773
7633
  const markers = await detectHarnessMarkers(agent.projectRoot);
6774
7634
  const harnesses = new Map;
@@ -7067,7 +7927,7 @@ async function initializeOpenScoutSetup(options = {}) {
7067
7927
  var SCOUT_AGENT_ID = "scout", SCOUT_PRIMARY_CONVERSATION_ID = "dm.scout.primary", MANAGED_AGENT_HARNESSES, DEFAULT_OPERATOR_NAME, DEFAULT_CAPABILITIES, DEFAULT_TRANSPORT = "claude_stream_json", DEFAULT_TELEGRAM_MODE = "polling", LEGACY_DEFAULT_TELEGRAM_CONVERSATION_ID = "channel.shared", DEFAULT_TELEGRAM_CONVERSATION_ID, SETTINGS_VERSION = 1, PROJECT_CONFIG_VERSION = 1, PROJECT_SCAN_SKIP_DIRECTORIES, PROJECT_STRONG_MARKERS, PROJECT_WEAK_MARKERS, PROJECT_HARNESS_MARKERS, PROJECT_STRONG_MARKERS_FLAT_NESTED, PROJECT_WEAK_MARKERS_FLAT_NESTED, GIT_BRANCH_CACHE_TTL_MS = 15000, gitBranchCache, SCOUT_SKILL_FILE_NAME = "SKILL.md", SETUP_MODULE_DIRECTORY, SCOUT_SKILL_REPO_ROOT, SCOUT_SKILL_INSTALL_PATHS, PROJECT_SCAN_MAX_DEPTH = 64;
7068
7928
  var init_setup = __esm(() => {
7069
7929
  init_dist();
7070
- init_src2();
7930
+ init_dist2();
7071
7931
  init_harness_catalog();
7072
7932
  init_support_paths();
7073
7933
  init_user_project_hints();
@@ -7161,7 +8021,7 @@ var init_setup = __esm(() => {
7161
8021
  PROJECT_STRONG_MARKERS_FLAT_NESTED = partitionFlatAndNestedMarkers(PROJECT_STRONG_MARKERS);
7162
8022
  PROJECT_WEAK_MARKERS_FLAT_NESTED = partitionFlatAndNestedMarkers(PROJECT_WEAK_MARKERS);
7163
8023
  gitBranchCache = new Map;
7164
- SETUP_MODULE_DIRECTORY = dirname4(fileURLToPath3(import.meta.url));
8024
+ SETUP_MODULE_DIRECTORY = dirname5(fileURLToPath3(import.meta.url));
7165
8025
  SCOUT_SKILL_REPO_ROOT = resolve3(SETUP_MODULE_DIRECTORY, "..", "..", "..");
7166
8026
  SCOUT_SKILL_INSTALL_PATHS = {
7167
8027
  claude: join7(homedir6(), ".claude", "skills", "scout", SCOUT_SKILL_FILE_NAME),
@@ -7246,7 +8106,7 @@ function updateBlockCompletion(blockState, completed) {
7246
8106
  blockState.status = completed ? "completed" : "streaming";
7247
8107
  blockState.block.status = completed ? "completed" : "streaming";
7248
8108
  }
7249
- function parseQuestionAnswer(value) {
8109
+ function parseQuestionAnswer2(value) {
7250
8110
  if (Array.isArray(value)) {
7251
8111
  return value.map((entry) => typeof entry === "string" ? entry.trim() : String(entry).trim()).filter(Boolean);
7252
8112
  }
@@ -7564,7 +8424,7 @@ function buildClaudeStreamJsonSessionSnapshot(raw2, options, targetClaudeSession
7564
8424
  if (blockState.block.type === "question") {
7565
8425
  const questionBlock = blockState.block;
7566
8426
  questionBlock.questionStatus = result.is_error ? "denied" : "answered";
7567
- const answer = parseQuestionAnswer(result.content);
8427
+ const answer = parseQuestionAnswer2(result.content);
7568
8428
  questionBlock.answer = answer.length > 0 ? answer : undefined;
7569
8429
  updateBlockCompletion(blockState, true);
7570
8430
  continue;
@@ -7783,6 +8643,7 @@ class ClaudeStreamJsonSession {
7783
8643
  await writeFile4(join13(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
7784
8644
  this.claudeSessionId = await readOptionalFile2(this.sessionStatePath);
7785
8645
  const args = [
8646
+ "--verbose",
7786
8647
  "--print",
7787
8648
  "--input-format",
7788
8649
  "stream-json",
@@ -9576,7 +10437,7 @@ function buildInvocationCollaborationContextPrompt(invocation) {
9576
10437
  import { spawnSync } from "child_process";
9577
10438
  import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
9578
10439
  import { homedir as homedir11 } from "os";
9579
- 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";
9580
10441
  import { fileURLToPath as fileURLToPath5 } from "url";
9581
10442
  function isTmpPath(p) {
9582
10443
  return /^\/(?:private\/)?tmp\//.test(p);
@@ -9609,7 +10470,7 @@ function runtimePackageDir() {
9609
10470
  const fromCwd = findWorkspaceRuntimeDir(process.cwd());
9610
10471
  if (fromCwd)
9611
10472
  return fromCwd;
9612
- const moduleDir = dirname9(fileURLToPath5(import.meta.url));
10473
+ const moduleDir = dirname10(fileURLToPath5(import.meta.url));
9613
10474
  return resolve5(moduleDir, "..");
9614
10475
  }
9615
10476
  function isInstalledRuntimePackageDir(candidate) {
@@ -9647,7 +10508,7 @@ function findWorkspaceRuntimeDir(startDir) {
9647
10508
  if (existsSync9(join15(candidate, "package.json")) && existsSync9(join15(candidate, "src"))) {
9648
10509
  return candidate;
9649
10510
  }
9650
- const parent = dirname9(current);
10511
+ const parent = dirname10(current);
9651
10512
  if (parent === current)
9652
10513
  return null;
9653
10514
  current = parent;
@@ -9658,7 +10519,7 @@ function resolveBunExecutable2() {
9658
10519
  if (explicit && explicit.trim().length > 0) {
9659
10520
  return explicit;
9660
10521
  }
9661
- if (basename3(process.execPath).startsWith("bun") && existsSync9(process.execPath)) {
10522
+ if (basename4(process.execPath).startsWith("bun") && existsSync9(process.execPath)) {
9662
10523
  return process.execPath;
9663
10524
  }
9664
10525
  const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
@@ -9811,8 +10672,8 @@ function collectOptionalEnvVars(keys) {
9811
10672
  }
9812
10673
  function resolveLaunchAgentPATH() {
9813
10674
  const entries = [
9814
- ...(process.env.PATH ?? "").split(":").filter(Boolean),
9815
10675
  join15(homedir11(), ".bun", "bin"),
10676
+ ...(process.env.PATH ?? "").split(":").filter(Boolean),
9816
10677
  "/opt/homebrew/bin",
9817
10678
  "/usr/local/bin",
9818
10679
  "/usr/bin",
@@ -9826,7 +10687,7 @@ function xmlEscape(value) {
9826
10687
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
9827
10688
  }
9828
10689
  function ensureParentDirectory(filePath) {
9829
- mkdirSync5(dirname9(filePath), { recursive: true });
10690
+ mkdirSync5(dirname10(filePath), { recursive: true });
9830
10691
  }
9831
10692
  function ensureServiceDirectories(config) {
9832
10693
  ensureOpenScoutCleanSlateSync();
@@ -10083,7 +10944,7 @@ var DEFAULT_BROKER_HOST = "127.0.0.1", DEFAULT_BROKER_HOST_MESH = "0.0.0.0", DEF
10083
10944
  var init_broker_service = __esm(async () => {
10084
10945
  init_support_paths();
10085
10946
  DEFAULT_BROKER_URL = buildDefaultBrokerUrl();
10086
- 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")) {
10087
10948
  await main();
10088
10949
  }
10089
10950
  });
@@ -10139,7 +11000,7 @@ import { randomUUID as randomUUID2 } from "crypto";
10139
11000
  import { execFileSync as execFileSync2, execSync } from "child_process";
10140
11001
  import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
10141
11002
  import { mkdir as mkdir6, rm as rm5, stat as stat4, writeFile as writeFile6 } from "fs/promises";
10142
- 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";
10143
11004
  import { fileURLToPath as fileURLToPath6 } from "url";
10144
11005
  function resolveRelayHub() {
10145
11006
  return resolveOpenScoutSupportPaths().relayHubDirectory;
@@ -10151,13 +11012,13 @@ function resolveProjectsRoot(projectPath) {
10151
11012
  try {
10152
11013
  const supportPaths = resolveOpenScoutSupportPaths();
10153
11014
  if (!existsSync10(supportPaths.settingsPath)) {
10154
- return dirname10(projectPath);
11015
+ return dirname11(projectPath);
10155
11016
  }
10156
11017
  const raw2 = JSON.parse(readFileSync6(supportPaths.settingsPath, "utf8"));
10157
11018
  const workspaceRoot = raw2.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
10158
- return workspaceRoot ? resolve6(workspaceRoot) : dirname10(projectPath);
11019
+ return workspaceRoot ? resolve6(workspaceRoot) : dirname11(projectPath);
10159
11020
  } catch {
10160
- return dirname10(projectPath);
11021
+ return dirname11(projectPath);
10161
11022
  }
10162
11023
  }
10163
11024
  function resolveBrokerUrl() {
@@ -10584,7 +11445,7 @@ function recordForHarness(record, harnessOverride) {
10584
11445
  function normalizeLocalAgentRecord(agentId, record) {
10585
11446
  const cwd = normalizeProjectPath(record.cwd || process.cwd());
10586
11447
  const projectRoot = normalizeProjectPath(record.projectRoot || cwd);
10587
- const project = record.project?.trim() || basename4(projectRoot);
11448
+ const project = record.project?.trim() || basename5(projectRoot);
10588
11449
  const definitionId = record.definitionId?.trim() || agentId;
10589
11450
  const defaultHarness = activeLocalHarness(record);
10590
11451
  const harnessProfiles = normalizeLocalHarnessProfiles(agentId, {
@@ -10649,7 +11510,7 @@ function localAgentRecordFromResolvedConfig(config) {
10649
11510
  function localAgentRecordFromRelayAgentOverride(agentId, override) {
10650
11511
  return normalizeLocalAgentRecord(agentId, {
10651
11512
  definitionId: override.definitionId ?? agentId,
10652
- project: override.projectName ?? basename4(override.projectRoot || override.runtime?.cwd || agentId),
11513
+ project: override.projectName ?? basename5(override.projectRoot || override.runtime?.cwd || agentId),
10653
11514
  projectRoot: override.projectRoot ?? override.runtime?.cwd,
10654
11515
  tmuxSession: override.runtime?.sessionId ?? `relay-${agentId}`,
10655
11516
  cwd: override.runtime?.cwd ?? override.projectRoot,
@@ -11090,7 +11951,7 @@ async function ensureLocalAgentOnline(agentName, record) {
11090
11951
  return normalizedRecord;
11091
11952
  }
11092
11953
  const projectPath = normalizedRecord.cwd;
11093
- const projectName = normalizedRecord.project || basename4(projectPath);
11954
+ const projectName = normalizedRecord.project || basename5(projectPath);
11094
11955
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
11095
11956
  const systemPrompt = renderLocalAgentSystemPromptTemplate(systemPromptTemplate, buildLocalAgentTemplateContext(agentName, projectName, projectPath), { transport: normalizedRecord.transport });
11096
11957
  const agentRuntimeDir = relayAgentRuntimeDirectory(agentName);
@@ -11312,7 +12173,7 @@ async function resolveLocalAgentIdentity(input) {
11312
12173
  nodeQualifier: instance2.nodeQualifier,
11313
12174
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11314
12175
  projectRoot: normalizeProjectPath(override.projectRoot),
11315
- projectName: override.projectName ?? basename4(override.projectRoot),
12176
+ projectName: override.projectName ?? basename5(override.projectRoot),
11316
12177
  source: "existing"
11317
12178
  };
11318
12179
  }
@@ -11331,12 +12192,12 @@ async function resolveLocalAgentIdentity(input) {
11331
12192
  nodeQualifier: instance2.nodeQualifier,
11332
12193
  harness: normalizeManagedHarness2(preferredHarness ?? override.defaultHarness, "claude"),
11333
12194
  projectRoot: normalizeProjectPath(override.projectRoot),
11334
- projectName: override.projectName ?? basename4(override.projectRoot),
12195
+ projectName: override.projectName ?? basename5(override.projectRoot),
11335
12196
  source: "existing"
11336
12197
  };
11337
12198
  }
11338
12199
  const configDefinitionId = config?.agent?.id?.trim() ? normalizeAgentSelectorSegment(config.agent.id.trim()) : "";
11339
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12200
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11340
12201
  const configDisplayName = config?.agent?.displayName?.trim() || "";
11341
12202
  const displayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11342
12203
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11350,7 +12211,7 @@ async function resolveLocalAgentIdentity(input) {
11350
12211
  nodeQualifier: instance.nodeQualifier,
11351
12212
  harness: effectiveHarness,
11352
12213
  projectRoot,
11353
- projectName: basename4(projectRoot),
12214
+ projectName: basename5(projectRoot),
11354
12215
  source: configDefinitionId || configDisplayName ? "config" : "new"
11355
12216
  };
11356
12217
  }
@@ -11406,7 +12267,7 @@ async function startLocalAgent(input) {
11406
12267
  }
11407
12268
  if (!matchingOverride) {
11408
12269
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
11409
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename4(projectRoot)) || "agent";
12270
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename5(projectRoot)) || "agent";
11410
12271
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
11411
12272
  const effectiveDisplayName = input.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
11412
12273
  const instance = buildRelayAgentInstance(definitionId, projectRoot);
@@ -11422,7 +12283,7 @@ async function startLocalAgent(input) {
11422
12283
  agentId: instance.id,
11423
12284
  definitionId,
11424
12285
  displayName: effectiveDisplayName,
11425
- projectName: basename4(projectRoot),
12286
+ projectName: basename5(projectRoot),
11426
12287
  projectRoot,
11427
12288
  projectConfigPath: coldProjectConfigPath,
11428
12289
  source: "manual",
@@ -11458,7 +12319,7 @@ async function startLocalAgent(input) {
11458
12319
  agentId: instance.id,
11459
12320
  definitionId: requestedDefinitionId,
11460
12321
  displayName: input.displayName || titleCaseLocalAgentName(requestedDefinitionId),
11461
- projectName: matchingOverride.projectName ?? basename4(matchingProjectRoot),
12322
+ projectName: matchingOverride.projectName ?? basename5(matchingProjectRoot),
11462
12323
  projectRoot: matchingProjectRoot,
11463
12324
  projectConfigPath: matchingOverride.projectConfigPath ?? null,
11464
12325
  source: "manual",
@@ -11763,7 +12624,7 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
11763
12624
  const requestedHarness = invocation.execution?.harness;
11764
12625
  const record = existing ?? (projectRoot ? {
11765
12626
  definitionId,
11766
- project: basename4(projectRoot),
12627
+ project: basename5(projectRoot),
11767
12628
  projectRoot,
11768
12629
  tmuxSession: String(endpoint.metadata?.tmuxSession ?? `relay-${agentRuntimeId}`),
11769
12630
  cwd: projectRoot,
@@ -11836,27 +12697,27 @@ function shouldDisableGeneratedCodexEndpoint(endpoint) {
11836
12697
  }
11837
12698
  var MODULE_DIRECTORY, OPENSCOUT_REPO_ROOT, DEFAULT_LOCAL_AGENT_CAPABILITIES, DEFAULT_LOCAL_AGENT_HARNESS = "claude", SUPPORTED_LOCAL_AGENT_HARNESSES;
11838
12699
  var init_local_agents = __esm(async () => {
11839
- init_src2();
12700
+ init_dist2();
11840
12701
  init_claude_stream_json();
11841
12702
  init_codex_app_server();
11842
12703
  init_setup();
11843
12704
  init_support_paths();
11844
12705
  init_local_agent_template();
11845
12706
  await init_broker_service();
11846
- MODULE_DIRECTORY = dirname10(fileURLToPath6(import.meta.url));
12707
+ MODULE_DIRECTORY = dirname11(fileURLToPath6(import.meta.url));
11847
12708
  OPENSCOUT_REPO_ROOT = resolve6(MODULE_DIRECTORY, "..", "..", "..");
11848
12709
  DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
11849
12710
  SUPPORTED_LOCAL_AGENT_HARNESSES = ["claude", "codex"];
11850
12711
  });
11851
12712
 
11852
12713
  // packages/web/server/index.ts
11853
- import { existsSync as existsSync13 } from "fs";
11854
- 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";
11855
12716
  import { fileURLToPath as fileURLToPath8 } from "url";
11856
12717
 
11857
12718
  // packages/web/server/create-openscout-web-server.ts
11858
- import { existsSync as existsSync11, rmSync as rmSync3 } from "fs";
11859
- 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";
11860
12721
  import { fileURLToPath as fileURLToPath7 } from "url";
11861
12722
 
11862
12723
  // node_modules/.bun/hono@4.12.10/node_modules/hono/dist/compose.js
@@ -13412,7 +14273,7 @@ import {
13412
14273
  writeFileSync as writeFileSync3
13413
14274
  } from "fs";
13414
14275
  import { homedir as homedir8 } from "os";
13415
- 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";
13416
14277
  import { fileURLToPath as fileURLToPath4 } from "url";
13417
14278
 
13418
14279
  // packages/runtime/src/local-config.ts
@@ -13424,7 +14285,7 @@ import {
13424
14285
  writeFileSync as writeFileSync2
13425
14286
  } from "fs";
13426
14287
  import { homedir as homedir7 } from "os";
13427
- import { dirname as dirname5, join as join8 } from "path";
14288
+ import { dirname as dirname6, join as join8 } from "path";
13428
14289
  var LOCAL_CONFIG_VERSION = 1;
13429
14290
  var DEFAULT_LOCAL_CONFIG = {
13430
14291
  version: LOCAL_CONFIG_VERSION,
@@ -13478,7 +14339,7 @@ function isValidPort(value) {
13478
14339
  }
13479
14340
  function writeLocalConfig(config) {
13480
14341
  const configPath = localConfigPath();
13481
- mkdirSync2(dirname5(configPath), { recursive: true });
14342
+ mkdirSync2(dirname6(configPath), { recursive: true });
13482
14343
  const body = JSON.stringify({ ...config, version: LOCAL_CONFIG_VERSION }, null, 2) + `
13483
14344
  `;
13484
14345
  const tmp = `${configPath}.tmp`;
@@ -13938,7 +14799,7 @@ function resolveScoutPairingRuntimeScriptPath() {
13938
14799
  throw new Error("Unable to locate the Scout pair supervisor entrypoint.");
13939
14800
  }
13940
14801
  function resolveOpenScoutWebServerDirectory(moduleUrl = import.meta.url) {
13941
- return dirname6(fileURLToPath4(moduleUrl));
14802
+ return dirname7(fileURLToPath4(moduleUrl));
13942
14803
  }
13943
14804
  function resolveOpenScoutWebPackageRoot(moduleUrl = import.meta.url) {
13944
14805
  return resolve4(resolveOpenScoutWebServerDirectory(moduleUrl), "..");
@@ -14096,6 +14957,17 @@ async function getScoutWebPairingState(currentDirectory) {
14096
14957
  async function refreshScoutWebPairingState(currentDirectory) {
14097
14958
  return readScoutPairingState(currentDirectory);
14098
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
+ }
14099
14971
  async function controlScoutWebPairingService(action, currentDirectory) {
14100
14972
  return serializePairingMutation(async () => {
14101
14973
  switch (action) {
@@ -14634,7 +15506,7 @@ import { join as join12 } from "path";
14634
15506
  // packages/runtime/src/user-config.ts
14635
15507
  import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
14636
15508
  import { homedir as homedir9 } from "os";
14637
- import { dirname as dirname8, join as join11 } from "path";
15509
+ import { dirname as dirname9, join as join11 } from "path";
14638
15510
  function userConfigPath() {
14639
15511
  return join11(process.env.OPENSCOUT_HOME ?? join11(homedir9(), ".openscout"), "user.json");
14640
15512
  }
@@ -14650,7 +15522,7 @@ function loadUserConfig() {
14650
15522
  }
14651
15523
  function saveUserConfig(config) {
14652
15524
  const configPath = userConfigPath();
14653
- mkdirSync4(dirname8(configPath), { recursive: true });
15525
+ mkdirSync4(dirname9(configPath), { recursive: true });
14654
15526
  writeFileSync4(configPath, JSON.stringify(config, null, 2) + `
14655
15527
  `, "utf8");
14656
15528
  }
@@ -15433,7 +16305,10 @@ function querySessions(limit = 80) {
15433
16305
  a.id AS agent_id,
15434
16306
  ac.display_name,
15435
16307
  ep.harness,
16308
+ ep.transport,
15436
16309
  ep.project_root,
16310
+ ep.session_id,
16311
+ ep.metadata_json AS endpoint_metadata_json,
15437
16312
  a.metadata_json
15438
16313
  FROM conversation_members cm
15439
16314
  JOIN agents a ON a.id = cm.actor_id
@@ -15456,6 +16331,8 @@ function querySessions(limit = 80) {
15456
16331
  const stats = statsStmt.get(r.id);
15457
16332
  let agentName = null;
15458
16333
  let harness = null;
16334
+ let harnessSessionId = null;
16335
+ let harnessLogPath = null;
15459
16336
  let branch = null;
15460
16337
  let workspaceRoot = null;
15461
16338
  if (primaryAgent) {
@@ -15469,6 +16346,14 @@ function querySessions(limit = 80) {
15469
16346
  }
15470
16347
  branch = meta.branch ?? meta.workspaceQualifier ?? null;
15471
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
+ }
15472
16357
  }
15473
16358
  const preview = previewStmt.get(r.id)?.body ?? null;
15474
16359
  return [{
@@ -15479,6 +16364,8 @@ function querySessions(limit = 80) {
15479
16364
  agentId,
15480
16365
  agentName,
15481
16366
  harness,
16367
+ harnessSessionId,
16368
+ harnessLogPath,
15482
16369
  currentBranch: branch,
15483
16370
  preview: preview ? preview.slice(0, 200) : null,
15484
16371
  messageCount: stats?.cnt ?? 0,
@@ -15576,23 +16463,30 @@ function parseDirectConversationId(conversationId) {
15576
16463
  }
15577
16464
  function synthesizeDirectSession(conversationId, agentId, operatorId) {
15578
16465
  const agent = db().prepare(`SELECT
15579
- a.id AS agent_id,
15580
- ac.display_name,
15581
- ep.harness,
15582
- ep.project_root,
15583
- a.metadata_json
15584
- FROM agents a
15585
- JOIN actors ac ON ac.id = a.id
15586
- ${LATEST_AGENT_ENDPOINT_JOIN}
15587
- 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);
15588
16478
  if (!agent) {
15589
16479
  return null;
15590
16480
  }
15591
16481
  let currentBranch = null;
16482
+ let endpointMeta = {};
15592
16483
  try {
15593
16484
  const metadata = agent.metadata_json ? JSON.parse(agent.metadata_json) : {};
15594
16485
  currentBranch = metadata.branch ?? metadata.workspaceQualifier ?? null;
15595
16486
  } catch {}
16487
+ try {
16488
+ endpointMeta = agent.endpoint_metadata_json ? JSON.parse(agent.endpoint_metadata_json) : {};
16489
+ } catch {}
15596
16490
  return {
15597
16491
  id: conversationId,
15598
16492
  kind: "direct",
@@ -15601,6 +16495,8 @@ function synthesizeDirectSession(conversationId, agentId, operatorId) {
15601
16495
  agentId,
15602
16496
  agentName: agent.display_name,
15603
16497
  harness: agent.harness,
16498
+ harnessSessionId: resolveHarnessSessionId(agent.transport, agent.session_id, endpointMeta),
16499
+ harnessLogPath: resolveHarnessLogPath(agentId, agent.transport, agent.session_id, endpointMeta),
15604
16500
  currentBranch,
15605
16501
  preview: null,
15606
16502
  messageCount: 0,
@@ -15977,11 +16873,11 @@ function queryHeartrate(numBuckets = 48) {
15977
16873
  }
15978
16874
 
15979
16875
  // packages/web/server/core/broker/service.ts
15980
- init_src2();
16876
+ init_dist2();
15981
16877
  init_setup();
15982
16878
  init_support_paths();
15983
16879
  await init_local_agents();
15984
- 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";
15985
16881
 
15986
16882
  // packages/web/server/core/broker/paths.ts
15987
16883
  var scoutBrokerPaths = {
@@ -16898,131 +17794,2909 @@ function whoEntryState(endpoints, registrationKind) {
16898
17794
  }, "offline");
16899
17795
  }
16900
17796
 
16901
- // packages/web/server/core/mesh/service.ts
16902
- 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";
16903
17802
 
16904
- // packages/runtime/src/tailscale.ts
16905
- import { readFile as readFile7 } from "fs/promises";
16906
- import { execFile } from "child_process";
16907
- import { promisify } from "util";
16908
- var execFileAsync = promisify(execFile);
16909
- function parseStatusJson(raw2) {
16910
- 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
+ };
16911
17816
  }
16912
- function parsePeers(status) {
16913
- const peers = Object.entries(status.Peer ?? {});
16914
- return peers.map(([fallbackId, peer]) => ({
16915
- id: peer.ID ?? fallbackId,
16916
- name: peer.HostName ?? peer.DNSName ?? fallbackId,
16917
- dnsName: peer.DNSName,
16918
- addresses: peer.TailscaleIPs ?? [],
16919
- online: peer.Online ?? false,
16920
- hostName: peer.HostName,
16921
- os: peer.OS,
16922
- tags: peer.Tags ?? []
16923
- }));
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}`;
16924
17824
  }
16925
- function parseSelf(status) {
16926
- const self = status.Self;
16927
- if (!self) {
16928
- 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);
16929
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) {
16930
17843
  return {
16931
- id: self.ID ?? self.DNSName ?? self.HostName ?? "self",
16932
- name: self.HostName ?? self.DNSName ?? "self",
16933
- dnsName: self.DNSName,
16934
- addresses: self.TailscaleIPs ?? [],
16935
- online: self.Online ?? true,
16936
- hostName: self.HostName,
16937
- os: self.OS,
16938
- tailnetName: status.CurrentTailnet?.Name,
16939
- 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
16940
17854
  };
16941
17855
  }
16942
- async function readStatusJsonFromFile(filePath) {
16943
- const raw2 = await readFile7(filePath, "utf8");
16944
- 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;
16945
17921
  }
16946
- async function readStatusJson() {
16947
- const fixturePath = process.env.OPENSCOUT_TAILSCALE_STATUS_JSON;
16948
- if (fixturePath) {
16949
- 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;
16950
17964
  }
16951
- try {
16952
- const tailscaleBin = process.env.OPENSCOUT_TAILSCALE_BIN ?? "tailscale";
16953
- const { stdout } = await execFileAsync(tailscaleBin, ["status", "--json"]);
16954
- return parseStatusJson(stdout);
16955
- } catch {
16956
- return null;
17965
+ mapToDriverValue(value) {
17966
+ return value;
17967
+ }
17968
+ shouldDisableInsert() {
17969
+ return this.config.generated !== undefined && this.config.generated.type !== "byDefault";
16957
17970
  }
16958
17971
  }
16959
- async function readTailscalePeers() {
16960
- const status = await readStatusJson();
16961
- if (!status) {
16962
- 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;
16963
18026
  }
16964
- return parsePeers(status);
16965
18027
  }
16966
- async function readTailscaleSelf() {
16967
- const status = await readStatusJson();
16968
- if (!status) {
16969
- 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;
16970
18050
  }
16971
- return parseSelf(status);
18051
+ static [entityKind] = "PgColumn";
16972
18052
  }
16973
18053
 
16974
- // packages/web/server/core/mesh/service.ts
16975
- function normalizeHost(host) {
16976
- 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
+ }
16977
18089
  }
16978
- function isLoopbackHost(host) {
16979
- const normalized = normalizeHost(host);
16980
- 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
+ }
16981
18103
  }
16982
- function isWildcardHost(host) {
16983
- const normalized = normalizeHost(host);
16984
- 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;
16985
18107
  }
16986
- function isPeerReachableBrokerUrl(url) {
16987
- if (!url) {
16988
- 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;
16989
18115
  }
16990
- try {
16991
- const hostname2 = new URL(url).hostname;
16992
- return !isLoopbackHost(hostname2) && !isWildcardHost(hostname2);
16993
- } catch {
16994
- return false;
18116
+ getSQLType() {
18117
+ return this.enum.enumName;
16995
18118
  }
16996
18119
  }
16997
- function stripTrailingDot(value) {
16998
- const trimmed = value?.trim();
16999
- if (!trimmed) {
17000
- 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
+ };
17001
18133
  }
17002
- return trimmed.replace(/\.$/, "");
17003
18134
  }
17004
- async function readTailscaleStatus() {
17005
- const peers = await readTailscalePeers();
17006
- return {
17007
- available: peers.length > 0,
17008
- peers,
17009
- 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
17010
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
+ }
17011
18205
  }
17012
- function formatIssueWarning(issue) {
17013
- 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";
17014
18210
  }
17015
- function computeIssues(health, localNode, nodes, tailscale) {
17016
- const issues = [];
17017
- if (!health.reachable) {
17018
- issues.push({
17019
- code: "broker_unreachable",
17020
- severity: "error",
17021
- title: "Broker not reachable",
17022
- summary: "The mesh page cannot reach the local broker yet, so peer status is incomplete.",
17023
- action: "Start the broker, then reload this page.",
17024
- actionCommand: null
17025
- });
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
+ }
18222
+ }
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
+ });
17026
20700
  return issues;
17027
20701
  }
17028
20702
  if (localNode?.advertiseScope === "local") {
@@ -17045,7 +20719,7 @@ function computeIssues(health, localNode, nodes, tailscale) {
17045
20719
  });
17046
20720
  }
17047
20721
  const remoteNodes = Object.values(nodes).filter((n) => n.id !== localNode?.id);
17048
- if (!tailscale.available && remoteNodes.length === 0) {
20722
+ if (!tailscale2.available && remoteNodes.length === 0) {
17049
20723
  issues.push({
17050
20724
  code: "discovery_unconfigured",
17051
20725
  severity: "warning",
@@ -17134,7 +20808,7 @@ function filterCurrentMeshNodes(allNodes, meshId, localNodeId, now) {
17134
20808
  }
17135
20809
  async function loadMeshStatus() {
17136
20810
  const brokerUrl = resolveScoutBrokerUrl();
17137
- const [health, context, tailscale] = await Promise.all([
20811
+ const [health, context, tailscale2] = await Promise.all([
17138
20812
  readScoutBrokerHealth(brokerUrl),
17139
20813
  loadScoutBrokerContext(brokerUrl),
17140
20814
  readTailscaleStatus()
@@ -17144,9 +20818,9 @@ async function loadMeshStatus() {
17144
20818
  const meshId = health.meshId ?? localNode?.meshId ?? null;
17145
20819
  const identity = computeIdentitySummary(health, localNode, meshId);
17146
20820
  const nodes = filterCurrentMeshNodes(allNodes, meshId, localNode?.id, Date.now());
17147
- const issues = computeIssues(health, localNode, nodes, tailscale);
20821
+ const issues = computeIssues(health, localNode, nodes, tailscale2);
17148
20822
  const warnings = issues.map(formatIssueWarning);
17149
- return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale, issues, warnings };
20823
+ return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale: tailscale2, issues, warnings };
17150
20824
  }
17151
20825
  function preferredAnnounceHost(self, currentBrokerUrl) {
17152
20826
  const dnsName = stripTrailingDot(self?.dnsName);
@@ -17271,10 +20945,10 @@ function resolveConversationRouting(conversationId) {
17271
20945
  return { directAgentId, senderId };
17272
20946
  }
17273
20947
  function resolveBundledStaticClientRoot(moduleUrl = import.meta.url) {
17274
- return resolve8(dirname11(fileURLToPath7(moduleUrl)), "client");
20948
+ return resolve9(dirname12(fileURLToPath7(moduleUrl)), "client");
17275
20949
  }
17276
20950
  function resolveSourceStaticClientRoot(moduleUrl = import.meta.url) {
17277
- return resolve8(dirname11(fileURLToPath7(moduleUrl)), "../dist/client");
20951
+ return resolve9(dirname12(fileURLToPath7(moduleUrl)), "../dist/client");
17278
20952
  }
17279
20953
  function resolveStaticRoot(staticRoot) {
17280
20954
  const configured = staticRoot?.trim();
@@ -17282,7 +20956,7 @@ function resolveStaticRoot(staticRoot) {
17282
20956
  return configured;
17283
20957
  }
17284
20958
  const bundled = resolveBundledStaticClientRoot(import.meta.url);
17285
- if (existsSync11(resolve8(bundled, "index.html"))) {
20959
+ if (existsSync12(resolve9(bundled, "index.html"))) {
17286
20960
  return bundled;
17287
20961
  }
17288
20962
  return resolveSourceStaticClientRoot(import.meta.url);
@@ -17320,6 +20994,10 @@ async function createOpenScoutWebServer(options) {
17320
20994
  app.get("/api/shell-state", async (c) => c.json(await shellStateCache.get()));
17321
20995
  app.get("/api/shell-state/refresh", async (c) => c.json(await shellStateCache.refresh()));
17322
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
+ });
17323
21001
  app.get("/api/activity", (c) => c.json(queryActivity()));
17324
21002
  app.get("/api/heartrate", (c) => c.json(queryHeartrate()));
17325
21003
  app.get("/api/fleet", (c) => c.json(queryFleet({
@@ -17563,8 +21241,8 @@ async function createOpenScoutWebServer(options) {
17563
21241
  // ../hudson/packages/hudson-relay/src/relay/session.ts
17564
21242
  import { createRequire } from "module";
17565
21243
  import { execSync as execSync2 } from "child_process";
17566
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
17567
- 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";
17568
21246
  var require2 = createRequire(import.meta.url);
17569
21247
  var pty = require2("node-pty");
17570
21248
  var DEFAULT_ORPHAN_TTL_MS = 30 * 60 * 1000;
@@ -17581,7 +21259,7 @@ function send(ws, data) {
17581
21259
  function resolveCwd(raw2) {
17582
21260
  const home = process.env.HOME || "/tmp";
17583
21261
  const expanded = (raw2 || home).replace(/^~/, home);
17584
- if (existsSync12(expanded))
21262
+ if (existsSync13(expanded))
17585
21263
  return expanded;
17586
21264
  try {
17587
21265
  mkdirSync6(expanded, { recursive: true });
@@ -17617,11 +21295,11 @@ function tmuxSessionExists2(name) {
17617
21295
  }
17618
21296
  function bootstrapFiles(cwd, files, sessionId) {
17619
21297
  for (const [relPath, content] of Object.entries(files)) {
17620
- const absPath = join18(cwd, relPath);
17621
- if (!existsSync12(absPath)) {
21298
+ const absPath = join19(cwd, relPath);
21299
+ if (!existsSync13(absPath)) {
17622
21300
  try {
17623
21301
  const dir = pathDirname(absPath);
17624
- if (!existsSync12(dir))
21302
+ if (!existsSync13(dir))
17625
21303
  mkdirSync6(dir, { recursive: true });
17626
21304
  writeFileSync6(absPath, content, "utf-8");
17627
21305
  console.log(`[relay] Session ${sessionId}: bootstrapped ${relPath}`);
@@ -17676,7 +21354,7 @@ function createSession(ws, msg) {
17676
21354
  return null;
17677
21355
  }
17678
21356
  }
17679
- if (!existsSync12(agentBin)) {
21357
+ if (!existsSync13(agentBin)) {
17680
21358
  const reason = `${agent} binary not found at ${agentBin}`;
17681
21359
  console.error(`[relay] Session ${id} failed: ${reason}`);
17682
21360
  send(ws, { type: "session:error", error: reason });
@@ -17836,7 +21514,7 @@ function destroy(sessionId) {
17836
21514
 
17837
21515
  // packages/web/server/relay.ts
17838
21516
  import { mkdir as mkdir7 } from "fs/promises";
17839
- import { join as join19 } from "path";
21517
+ import { join as join20 } from "path";
17840
21518
  import { randomUUID as randomUUID3 } from "crypto";
17841
21519
  var UPLOAD_DIR = "/tmp/scout-uploads";
17842
21520
  async function handleRelayUpload(req) {
@@ -17846,7 +21524,7 @@ async function handleRelayUpload(req) {
17846
21524
  }
17847
21525
  await mkdir7(UPLOAD_DIR, { recursive: true });
17848
21526
  const filename = `${randomUUID3()}-${name}`;
17849
- const filepath = join19(UPLOAD_DIR, filename);
21527
+ const filepath = join20(UPLOAD_DIR, filename);
17850
21528
  await Bun.write(filepath, Buffer.from(data, "base64"));
17851
21529
  return Response.json({ path: filepath });
17852
21530
  }
@@ -17951,13 +21629,13 @@ function resolveStaticRoot2() {
17951
21629
  if (process.env.OPENSCOUT_WEB_STATIC_ROOT?.trim()) {
17952
21630
  return process.env.OPENSCOUT_WEB_STATIC_ROOT.trim();
17953
21631
  }
17954
- const selfDir = dirname12(fileURLToPath8(import.meta.url));
17955
- const siblingClientRoot = join20(selfDir, "client");
17956
- 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"))) {
17957
21635
  return siblingClientRoot;
17958
21636
  }
17959
- const sourceDistClientRoot = resolve9(selfDir, "../dist/client");
17960
- if (existsSync13(join20(sourceDistClientRoot, "index.html"))) {
21637
+ const sourceDistClientRoot = resolve10(selfDir, "../dist/client");
21638
+ if (existsSync14(join21(sourceDistClientRoot, "index.html"))) {
17961
21639
  return sourceDistClientRoot;
17962
21640
  }
17963
21641
  return;