@openscout/scout 0.2.69 → 0.2.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,12 +16,13 @@ var __export = (target, all) => {
16
16
 
17
17
  // packages/runtime/src/broker-daemon.ts
18
18
  import { spawn as spawn5 } from "child_process";
19
+ import { randomUUID as randomUUID4 } from "crypto";
19
20
  import { createServer } from "http";
20
21
  import { existsSync as existsSync19, mkdirSync as mkdirSync8, openSync as openSync3 } from "fs";
21
22
  import { lstat, mkdir as mkdir8, stat as stat5, unlink } from "fs/promises";
22
23
  import { createRequire } from "module";
23
24
  import { hostname as hostname4 } from "os";
24
- import { basename as basename11, dirname as dirname15, join as join25, resolve as resolve8 } from "path";
25
+ import { basename as basename12, dirname as dirname16, join as join25, resolve as resolve8 } from "path";
25
26
  import { fileURLToPath as fileURLToPath7 } from "url";
26
27
 
27
28
  // node_modules/.bun/@trpc+server@11.16.0+1fb4c65d43e298b9/node_modules/@trpc/server/dist/codes-DagpWZLc.mjs
@@ -16320,6 +16321,7 @@ var RAW_MAX_STRING_LEN = 1000;
16320
16321
  var RAW_MAX_ARRAY_ITEMS = 25;
16321
16322
  var RAW_MAX_OBJECT_KEYS = 50;
16322
16323
  var RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
16324
+ var RECENT_TRANSCRIPT_LINES_PER_FILE = 200;
16323
16325
  var RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv3("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
16324
16326
  var sources = [ClaudeSource, CodexSource];
16325
16327
  var watchers = new Map;
@@ -16646,6 +16648,15 @@ function stopLoopIfIdle() {
16646
16648
  }
16647
16649
  watchers.clear();
16648
16650
  }
16651
+ async function getTailDiscovery(force = false) {
16652
+ if (force) {
16653
+ return runDiscovery("deep", { pruneMissing: true });
16654
+ }
16655
+ if (lastDiscovery && Date.now() - lastDiscovery.generatedAt <= DISCOVERY_CACHE_MAX_AGE_MS) {
16656
+ return lastDiscovery;
16657
+ }
16658
+ return runDiscovery("shallow", { pruneMissing: true });
16659
+ }
16649
16660
  function subscribeTail(handler) {
16650
16661
  subscribers2.add(handler);
16651
16662
  ensureLoopRunning();
@@ -16664,6 +16675,78 @@ function subscribeTail(handler) {
16664
16675
  function snapshotRecentEvents(limit = 500) {
16665
16676
  return aggregateBuffer.slice(-limit);
16666
16677
  }
16678
+ async function readRecentLiveEvents(limit = 500) {
16679
+ if (watchers.size === 0) {
16680
+ await runDiscovery("shallow", { pruneMissing: true });
16681
+ } else if (!lastDiscovery || Date.now() - lastDiscovery.generatedAt > DISCOVERY_CACHE_MAX_AGE_MS) {
16682
+ await runDiscovery("hot", { pruneMissing: false });
16683
+ }
16684
+ await pumpAllWatchers();
16685
+ return snapshotRecentEvents(limit);
16686
+ }
16687
+ async function readRecentTranscriptLines(path, maxLines = RECENT_TRANSCRIPT_LINES_PER_FILE) {
16688
+ let handle = null;
16689
+ try {
16690
+ const stats = await stat(path);
16691
+ if (stats.size <= 0)
16692
+ return [];
16693
+ const start = Math.max(0, stats.size - RECENT_TRANSCRIPT_READ_BYTES);
16694
+ const length = stats.size - start;
16695
+ const buffer = Buffer.alloc(length);
16696
+ handle = await open(path, "r");
16697
+ await handle.read(buffer, 0, length, start);
16698
+ const lines = buffer.toString("utf8").split(`
16699
+ `);
16700
+ if (start > 0) {
16701
+ lines.shift();
16702
+ }
16703
+ return lines.filter(Boolean).slice(-maxLines);
16704
+ } catch {
16705
+ return [];
16706
+ } finally {
16707
+ await handle?.close().catch(() => {});
16708
+ }
16709
+ }
16710
+ async function readRecentTranscriptEvents(limit = 50, options) {
16711
+ const discovery = options?.discovery ?? await getTailDiscovery();
16712
+ const events = [];
16713
+ const seenTranscripts = new Set;
16714
+ const seenEvents = new Set;
16715
+ const transcriptReadLimit = Math.min(RECENT_TRANSCRIPT_MAX_FILES, Math.max(12, limit));
16716
+ for (const transcript of discovery.transcripts.slice(0, transcriptReadLimit)) {
16717
+ const key = transcriptKey(transcript);
16718
+ if (seenTranscripts.has(key))
16719
+ continue;
16720
+ seenTranscripts.add(key);
16721
+ const source = sources.find((candidate) => candidate.name === transcript.source);
16722
+ if (!source)
16723
+ continue;
16724
+ const process3 = processForTranscript(transcript, discovery.processes);
16725
+ const lines = await readRecentTranscriptLines(transcript.transcriptPath, options?.perTranscriptLineLimit);
16726
+ lines.forEach((line, index) => {
16727
+ const event = source.parseLine(line, {
16728
+ process: process3,
16729
+ transcript,
16730
+ transcriptPath: transcript.transcriptPath,
16731
+ lineOffset: index
16732
+ });
16733
+ if (event) {
16734
+ const compacted = compactEvent(event);
16735
+ const eventKey = [
16736
+ compacted.source,
16737
+ compacted.sessionId,
16738
+ compacted.kind,
16739
+ compacted.summary
16740
+ ].join("\x00");
16741
+ if (!seenEvents.has(eventKey)) {
16742
+ seenEvents.add(eventKey);
16743
+ events.push(compacted);
16744
+ }
16745
+ }
16746
+ });
16747
+ }
16748
+ return events.sort((left, right) => right.ts - left.ts).slice(0, limit);
16749
+ }
16667
16750
  // packages/runtime/src/harness-topology/service.ts
16668
16751
  import { createHash } from "crypto";
16669
16752
  import { homedir as homedir5 } from "os";
@@ -17798,6 +17881,16 @@ var tailEventsInput = exports_external.object({
17798
17881
  since: exports_external.string().optional(),
17799
17882
  sources: exports_external.array(exports_external.string()).optional()
17800
17883
  }).optional();
17884
+ var tailRecentInput = exports_external.object({
17885
+ limit: exports_external.number().int().min(1).max(1000).optional(),
17886
+ sources: exports_external.array(exports_external.string()).optional(),
17887
+ kinds: exports_external.array(exports_external.string()).optional(),
17888
+ sessionId: exports_external.string().optional(),
17889
+ project: exports_external.string().optional(),
17890
+ cwd: exports_external.string().optional(),
17891
+ query: exports_external.string().optional(),
17892
+ transcripts: exports_external.boolean().optional()
17893
+ }).optional();
17801
17894
  var topologyEventsInput = exports_external.object({
17802
17895
  since: exports_external.string().optional(),
17803
17896
  sources: exports_external.array(exports_external.string()).optional()
@@ -17946,6 +18039,48 @@ function topologyEventIterable(sources2, signal) {
17946
18039
  }
17947
18040
  };
17948
18041
  }
18042
+ function normalizedFilterSet(values) {
18043
+ const normalized = values?.map((value) => value.trim().toLowerCase()).filter(Boolean);
18044
+ return normalized && normalized.length > 0 ? new Set(normalized) : null;
18045
+ }
18046
+ function textMatches(value, query) {
18047
+ if (!query)
18048
+ return true;
18049
+ return (value ?? "").toLowerCase().includes(query);
18050
+ }
18051
+ function tailEventMatches(event, input) {
18052
+ const sources2 = normalizedFilterSet(input?.sources);
18053
+ if (sources2 && !sources2.has(event.source.toLowerCase()))
18054
+ return false;
18055
+ const kinds = normalizedFilterSet(input?.kinds);
18056
+ if (kinds && !kinds.has(event.kind.toLowerCase()))
18057
+ return false;
18058
+ const sessionId = input?.sessionId?.trim();
18059
+ if (sessionId && event.sessionId !== sessionId)
18060
+ return false;
18061
+ const project = input?.project?.trim().toLowerCase();
18062
+ if (project && !textMatches(event.project, project))
18063
+ return false;
18064
+ const cwd = input?.cwd?.trim().toLowerCase();
18065
+ if (cwd && !textMatches(event.cwd, cwd))
18066
+ return false;
18067
+ const query = input?.query?.trim().toLowerCase();
18068
+ if (query) {
18069
+ const haystack = [
18070
+ event.source,
18071
+ event.kind,
18072
+ event.sessionId,
18073
+ event.project,
18074
+ event.cwd,
18075
+ event.harness,
18076
+ event.summary
18077
+ ].join(`
18078
+ `).toLowerCase();
18079
+ if (!haystack.includes(query))
18080
+ return false;
18081
+ }
18082
+ return true;
18083
+ }
17949
18084
  var controlRouter = t.router({
17950
18085
  events: t.procedure.input(controlEventsInput).subscription(async function* ({ input, signal }) {
17951
18086
  const kinds = input?.kinds;
@@ -17971,6 +18106,33 @@ var controlRouter = t.router({
17971
18106
  })
17972
18107
  });
17973
18108
  var tailRouter = t.router({
18109
+ recent: t.procedure.input(tailRecentInput).query(async ({ input }) => {
18110
+ const limit = input?.limit ?? TAIL_BACKLOG_LIMIT;
18111
+ const bufferedEvents = await readRecentLiveEvents(limit);
18112
+ const eventsById = new Map;
18113
+ if (input?.transcripts) {
18114
+ const transcriptEvents = await readRecentTranscriptEvents(limit, {
18115
+ perTranscriptLineLimit: Math.min(200, Math.max(50, limit))
18116
+ });
18117
+ for (const event of transcriptEvents) {
18118
+ eventsById.set(event.id, event);
18119
+ }
18120
+ }
18121
+ for (const event of bufferedEvents) {
18122
+ eventsById.set(event.id, event);
18123
+ }
18124
+ const events = [...eventsById.values()].filter((event) => tailEventMatches(event, input)).sort((left, right) => {
18125
+ if (left.ts === right.ts)
18126
+ return left.id.localeCompare(right.id);
18127
+ return left.ts - right.ts;
18128
+ }).slice(-limit);
18129
+ return {
18130
+ generatedAt: Date.now(),
18131
+ limit,
18132
+ cursor: events.at(-1)?.id ?? null,
18133
+ events
18134
+ };
18135
+ }),
17974
18136
  events: t.procedure.input(tailEventsInput).subscription(async function* ({ input, signal }) {
17975
18137
  const sources2 = input?.sources;
17976
18138
  const filter = sources2 && sources2.length > 0 ? new Set(sources2) : null;
@@ -18512,6 +18674,31 @@ function buildUnsignedMeshPresence(input) {
18512
18674
  ...input.metadata ? { metadata: input.metadata } : {}
18513
18675
  };
18514
18676
  }
18677
+ // packages/protocol/src/channel-identity.ts
18678
+ var CHANNEL_ID_PREFIX = "c.";
18679
+ var CHANNEL_NATURAL_KEY_METADATA = "naturalKey";
18680
+ function mintChannelId(randomUuid) {
18681
+ return `${CHANNEL_ID_PREFIX}${randomUuid().toLowerCase()}`;
18682
+ }
18683
+ function directChannelNaturalKey(participantIds) {
18684
+ return `direct:${stableIdentityParts(participantIds).join(",")}`;
18685
+ }
18686
+ function namedChannelNaturalKey(channel) {
18687
+ return `channel:${encodeIdentityPart(channel.trim().toLowerCase() || "shared")}`;
18688
+ }
18689
+ function systemChannelNaturalKey(name) {
18690
+ return `system:${encodeIdentityPart(name.trim().toLowerCase() || "system")}`;
18691
+ }
18692
+ function channelNaturalKeyFromMetadata(metadata) {
18693
+ const value = metadata?.[CHANNEL_NATURAL_KEY_METADATA];
18694
+ return typeof value === "string" && value.trim() ? value.trim() : null;
18695
+ }
18696
+ function stableIdentityParts(values) {
18697
+ return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean))).sort().map(encodeIdentityPart);
18698
+ }
18699
+ function encodeIdentityPart(value) {
18700
+ return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
18701
+ }
18515
18702
  // packages/protocol/src/collaboration.ts
18516
18703
  function isQuestionTerminalState(state) {
18517
18704
  return state === "closed" || state === "declined";
@@ -20273,29 +20460,6 @@ function resolveAgentLabel(snapshot, label, options) {
20273
20460
  return agnosticResolution;
20274
20461
  }
20275
20462
  }
20276
- const fallbackCandidates = buildAgentLabelCandidates(snapshot, options.helpers, {
20277
- includeStale: true
20278
- });
20279
- const fallbackDiagnosis = diagnoseAgentIdentity(identity2, fallbackCandidates);
20280
- if (fallbackDiagnosis.kind === "resolved") {
20281
- return {
20282
- kind: "resolved",
20283
- agent: fallbackDiagnosis.match.agent
20284
- };
20285
- }
20286
- if (fallbackDiagnosis.kind === "ambiguous") {
20287
- return {
20288
- kind: "ambiguous",
20289
- label: identity2.label,
20290
- candidates: fallbackDiagnosis.candidates.map((candidate) => candidate.agent)
20291
- };
20292
- }
20293
- if (harnessAgnosticIdentity) {
20294
- const agnosticFallbackResolution = resolutionFromDiagnosis(diagnoseAgentIdentity(harnessAgnosticIdentity, fallbackCandidates), identity2.label);
20295
- if (agnosticFallbackResolution) {
20296
- return agnosticFallbackResolution;
20297
- }
20298
- }
20299
20463
  return { kind: "unknown", label: identity2.label };
20300
20464
  }
20301
20465
  const preferLocal = options.preferLocalNodeId?.trim();
@@ -21734,7 +21898,7 @@ import { randomUUID as randomUUID2 } from "crypto";
21734
21898
  import { execFileSync as execFileSync3, execSync } from "child_process";
21735
21899
  import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
21736
21900
  import { mkdir as mkdir7, rm as rm5, stat as stat4, writeFile as writeFile7 } from "fs/promises";
21737
- import { basename as basename9, dirname as dirname10, join as join19, resolve as resolve7 } from "path";
21901
+ import { basename as basename10, dirname as dirname11, join as join19, resolve as resolve7 } from "path";
21738
21902
  import { fileURLToPath as fileURLToPath5 } from "url";
21739
21903
 
21740
21904
  // packages/runtime/src/dispatch-stalled.ts
@@ -22945,6 +23109,7 @@ class SessionRegistry {
22945
23109
  }
22946
23110
  }
22947
23111
  // packages/agent-sessions/src/history.ts
23112
+ import { basename as basename6, dirname as dirname4 } from "path";
22948
23113
  function normalizeTimestamp(value) {
22949
23114
  if (typeof value === "number") {
22950
23115
  if (!Number.isFinite(value)) {
@@ -23033,6 +23198,59 @@ function renderToolResultContent(content) {
23033
23198
  }
23034
23199
  return stringifyUnknown(content);
23035
23200
  }
23201
+ function renderContentPartsText(content) {
23202
+ if (typeof content === "string") {
23203
+ return content;
23204
+ }
23205
+ if (!Array.isArray(content)) {
23206
+ return "";
23207
+ }
23208
+ return content.map((entry) => {
23209
+ if (typeof entry === "string") {
23210
+ return entry;
23211
+ }
23212
+ if (!isRecord(entry)) {
23213
+ return "";
23214
+ }
23215
+ if (typeof entry.text === "string") {
23216
+ return entry.text;
23217
+ }
23218
+ if (typeof entry.content === "string") {
23219
+ return entry.content;
23220
+ }
23221
+ return "";
23222
+ }).filter(Boolean).join(`
23223
+ `);
23224
+ }
23225
+ function extractReasoningText(item) {
23226
+ const summary = Array.isArray(item.summary) ? item.summary : [];
23227
+ const content = Array.isArray(item.content) ? item.content : [];
23228
+ const summaryText = summary.map((entry) => {
23229
+ if (typeof entry === "string") {
23230
+ return entry;
23231
+ }
23232
+ const record2 = entry;
23233
+ if (typeof record2.text === "string") {
23234
+ return record2.text;
23235
+ }
23236
+ if (typeof record2.summary === "string") {
23237
+ return record2.summary;
23238
+ }
23239
+ return "";
23240
+ }).filter(Boolean).join(`
23241
+ `);
23242
+ const contentText = content.map((entry) => {
23243
+ if (typeof entry === "string") {
23244
+ return entry;
23245
+ }
23246
+ const record2 = entry;
23247
+ return typeof record2.text === "string" ? record2.text : "";
23248
+ }).filter(Boolean).join(`
23249
+ `);
23250
+ return [summaryText, contentText].filter(Boolean).join(`
23251
+
23252
+ `).trim();
23253
+ }
23036
23254
  function extractQuestionOptions(firstQuestion) {
23037
23255
  const options = Array.isArray(firstQuestion.options) ? firstQuestion.options : [];
23038
23256
  return options.map((option) => {
@@ -23709,6 +23927,505 @@ class ClaudeCodeHistoryParser {
23709
23927
  });
23710
23928
  }
23711
23929
  }
23930
+
23931
+ class CodexHistoryParser {
23932
+ session;
23933
+ baseTimestampMs;
23934
+ events = [];
23935
+ currentTurn = null;
23936
+ turnCounter = 0;
23937
+ blockIndex = 0;
23938
+ blockById = new Map;
23939
+ toolBlockMap = new Map;
23940
+ assistantMessageTextThisTurn = new Set;
23941
+ inputTokens = 0;
23942
+ outputTokens = 0;
23943
+ reasoningOutputTokens = 0;
23944
+ cachedInputTokens = 0;
23945
+ tokenEventCount = 0;
23946
+ constructor(session, baseTimestampMs) {
23947
+ this.session = session;
23948
+ this.baseTimestampMs = baseTimestampMs;
23949
+ }
23950
+ parse(content) {
23951
+ const lines = content.split(/\r?\n/u);
23952
+ let parsedLineCount = 0;
23953
+ let skippedLineCount = 0;
23954
+ let lineCount = 0;
23955
+ let lastCapturedAt = this.baseTimestampMs;
23956
+ for (let index = 0;index < lines.length; index += 1) {
23957
+ const rawLine = lines[index];
23958
+ const trimmed = rawLine.trim();
23959
+ if (!trimmed) {
23960
+ continue;
23961
+ }
23962
+ lineCount += 1;
23963
+ let record2;
23964
+ try {
23965
+ const parsed = JSON.parse(trimmed);
23966
+ if (!isRecord(parsed)) {
23967
+ skippedLineCount += 1;
23968
+ continue;
23969
+ }
23970
+ record2 = parsed;
23971
+ } catch {
23972
+ skippedLineCount += 1;
23973
+ continue;
23974
+ }
23975
+ const capturedAt = extractRecordTimestamp(record2) ?? this.baseTimestampMs + index;
23976
+ lastCapturedAt = capturedAt;
23977
+ if (this.handleRecord(record2, capturedAt)) {
23978
+ parsedLineCount += 1;
23979
+ } else {
23980
+ skippedLineCount += 1;
23981
+ }
23982
+ }
23983
+ if (this.persistUsageMetadata()) {
23984
+ this.emitSessionUpdate(lastCapturedAt);
23985
+ }
23986
+ return {
23987
+ events: this.events,
23988
+ lineCount,
23989
+ parsedLineCount,
23990
+ skippedLineCount
23991
+ };
23992
+ }
23993
+ handleRecord(record2, capturedAt) {
23994
+ const type = maybeString(record2.type);
23995
+ if (!type) {
23996
+ return false;
23997
+ }
23998
+ switch (type) {
23999
+ case "session_meta":
24000
+ this.handleSessionMeta(record2, capturedAt);
24001
+ return true;
24002
+ case "turn_context":
24003
+ this.handleTurnContext(record2, capturedAt);
24004
+ return true;
24005
+ case "event_msg":
24006
+ this.handleEventMessage(record2, capturedAt);
24007
+ return true;
24008
+ case "response_item":
24009
+ this.handleResponseItem(record2, capturedAt);
24010
+ return true;
24011
+ case "compacted":
24012
+ return true;
24013
+ default:
24014
+ return false;
24015
+ }
24016
+ }
24017
+ handleSessionMeta(record2, capturedAt) {
24018
+ const payload = isRecord(record2.payload) ? record2.payload : {};
24019
+ let changed = false;
24020
+ const providerMeta = this.ensureProviderMeta();
24021
+ const externalSessionId = maybeString(payload.id);
24022
+ if (externalSessionId && providerMeta.externalSessionId !== externalSessionId) {
24023
+ providerMeta.externalSessionId = externalSessionId;
24024
+ changed = true;
24025
+ }
24026
+ const cwd = maybeString(payload.cwd);
24027
+ if (cwd && this.session.cwd !== cwd) {
24028
+ this.session.cwd = cwd;
24029
+ if (!this.session.name || /^\d+$/u.test(this.session.name)) {
24030
+ this.session.name = basename6(cwd) || "Codex Session";
24031
+ }
24032
+ changed = true;
24033
+ }
24034
+ const git = isRecord(payload.git) ? payload.git : null;
24035
+ const runtime = this.ensureObserveMetaRecord("observeRuntime");
24036
+ changed = this.assignObserveString(runtime, "originator", payload.originator) || changed;
24037
+ changed = this.assignObserveString(runtime, "cliVersion", payload.cli_version) || changed;
24038
+ changed = this.assignObserveString(runtime, "source", payload.source) || changed;
24039
+ changed = this.assignObserveString(runtime, "threadSource", payload.thread_source) || changed;
24040
+ changed = this.assignObserveString(runtime, "modelProvider", payload.model_provider) || changed;
24041
+ changed = this.assignObserveString(runtime, "gitBranch", git?.branch) || changed;
24042
+ changed = this.assignObserveString(runtime, "gitCommitHash", git?.commit_hash) || changed;
24043
+ changed = this.assignObserveString(runtime, "repositoryUrl", git?.repository_url) || changed;
24044
+ if (changed) {
24045
+ this.emitSessionUpdate(capturedAt);
24046
+ }
24047
+ }
24048
+ handleTurnContext(record2, capturedAt) {
24049
+ const payload = isRecord(record2.payload) ? record2.payload : {};
24050
+ let changed = false;
24051
+ const cwd = maybeString(payload.cwd);
24052
+ if (cwd && this.session.cwd !== cwd) {
24053
+ this.session.cwd = cwd;
24054
+ changed = true;
24055
+ }
24056
+ const model = maybeString(payload.model);
24057
+ if (model && this.session.model !== model) {
24058
+ this.session.model = model;
24059
+ changed = true;
24060
+ }
24061
+ const runtime = this.ensureObserveMetaRecord("observeRuntime");
24062
+ changed = this.assignObserveString(runtime, "approvalPolicy", payload.approval_policy) || changed;
24063
+ changed = this.assignObserveString(runtime, "currentDate", payload.current_date) || changed;
24064
+ changed = this.assignObserveString(runtime, "timezone", payload.timezone) || changed;
24065
+ changed = this.assignObserveString(runtime, "effort", payload.effort) || changed;
24066
+ changed = this.assignObserveString(runtime, "personality", payload.personality) || changed;
24067
+ if (isRecord(payload.sandbox_policy) && runtime.sandboxPolicy !== payload.sandbox_policy) {
24068
+ runtime.sandboxPolicy = payload.sandbox_policy;
24069
+ changed = true;
24070
+ }
24071
+ if (changed) {
24072
+ this.emitSessionUpdate(capturedAt);
24073
+ }
24074
+ }
24075
+ handleEventMessage(record2, capturedAt) {
24076
+ const payload = isRecord(record2.payload) ? record2.payload : {};
24077
+ const eventType = maybeString(payload.type);
24078
+ if (!eventType) {
24079
+ return;
24080
+ }
24081
+ switch (eventType) {
24082
+ case "task_started": {
24083
+ const startedAt = normalizeTimestamp(payload.started_at) ?? capturedAt;
24084
+ this.startTurn(startedAt, maybeString(payload.turn_id));
24085
+ break;
24086
+ }
24087
+ case "user_message":
24088
+ this.ensureTurn(capturedAt);
24089
+ break;
24090
+ case "agent_message": {
24091
+ const message = maybeString(payload.message);
24092
+ if (message) {
24093
+ this.appendCompletedText(capturedAt, message);
24094
+ this.assistantMessageTextThisTurn.add(message);
24095
+ }
24096
+ break;
24097
+ }
24098
+ case "patch_apply_end":
24099
+ this.handleToolOutput(capturedAt, maybeString(payload.call_id), [maybeString(payload.stdout), maybeString(payload.stderr)].filter(Boolean).join(`
24100
+ `), payload.success === false);
24101
+ break;
24102
+ case "token_count":
24103
+ this.captureTokenUsage(payload.info);
24104
+ break;
24105
+ case "task_complete":
24106
+ this.endTurn("completed", normalizeTimestamp(payload.completed_at) ?? capturedAt);
24107
+ break;
24108
+ case "context_compacted":
24109
+ case "web_search_end":
24110
+ break;
24111
+ default:
24112
+ break;
24113
+ }
24114
+ }
24115
+ handleResponseItem(record2, capturedAt) {
24116
+ const payload = isRecord(record2.payload) ? record2.payload : {};
24117
+ const itemType = maybeString(payload.type);
24118
+ if (!itemType) {
24119
+ return;
24120
+ }
24121
+ switch (itemType) {
24122
+ case "message":
24123
+ this.handleResponseMessage(payload, capturedAt);
24124
+ break;
24125
+ case "reasoning":
24126
+ this.handleReasoning(payload, capturedAt);
24127
+ break;
24128
+ case "function_call":
24129
+ case "custom_tool_call":
24130
+ this.handleToolCall(payload, capturedAt);
24131
+ break;
24132
+ case "function_call_output":
24133
+ case "custom_tool_call_output":
24134
+ this.handleToolOutput(capturedAt, maybeString(payload.call_id), renderToolResultContent(payload.output), false);
24135
+ break;
24136
+ case "web_search_call":
24137
+ this.handleWebSearchCall(payload, capturedAt);
24138
+ break;
24139
+ default:
24140
+ break;
24141
+ }
24142
+ }
24143
+ handleResponseMessage(payload, capturedAt) {
24144
+ const role = maybeString(payload.role);
24145
+ if (role !== "assistant") {
24146
+ return;
24147
+ }
24148
+ const text = renderContentPartsText(payload.content).trim();
24149
+ if (!text || this.assistantMessageTextThisTurn.has(text)) {
24150
+ return;
24151
+ }
24152
+ this.appendCompletedText(capturedAt, text);
24153
+ this.assistantMessageTextThisTurn.add(text);
24154
+ }
24155
+ handleReasoning(payload, capturedAt) {
24156
+ const text = extractReasoningText(payload);
24157
+ if (!text) {
24158
+ return;
24159
+ }
24160
+ const turn = this.ensureTurn(capturedAt);
24161
+ const block = this.startBlock(turn, capturedAt, {
24162
+ type: "reasoning",
24163
+ text,
24164
+ status: "completed"
24165
+ });
24166
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
24167
+ }
24168
+ handleToolCall(payload, capturedAt) {
24169
+ const turn = this.ensureTurn(capturedAt);
24170
+ const toolName = maybeString(payload.name) ?? "unknown";
24171
+ const toolCallId = maybeString(payload.call_id) ?? `${turn.id}:tool:${this.blockIndex}`;
24172
+ const input = this.parseToolInput(payload.arguments ?? payload.input);
24173
+ const action = this.buildAction(toolName, toolCallId, input);
24174
+ const block = this.startBlock(turn, capturedAt, {
24175
+ id: `${turn.id}:action:${toolCallId}`,
24176
+ type: "action",
24177
+ action,
24178
+ status: "streaming"
24179
+ });
24180
+ this.toolBlockMap.set(toolCallId, block.id);
24181
+ }
24182
+ handleWebSearchCall(payload, capturedAt) {
24183
+ const turn = this.ensureTurn(capturedAt);
24184
+ const toolCallId = `${turn.id}:web-search:${this.blockIndex}`;
24185
+ const action = {
24186
+ kind: "tool_call",
24187
+ toolName: "web_search",
24188
+ toolCallId,
24189
+ input: payload.action,
24190
+ result: payload.status,
24191
+ status: maybeString(payload.status) === "failed" ? "failed" : "completed",
24192
+ output: ""
24193
+ };
24194
+ const block = this.startBlock(turn, capturedAt, {
24195
+ id: `${turn.id}:action:${toolCallId}`,
24196
+ type: "action",
24197
+ action,
24198
+ status: "completed"
24199
+ });
24200
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
24201
+ }
24202
+ parseToolInput(value) {
24203
+ if (typeof value !== "string") {
24204
+ return value;
24205
+ }
24206
+ const trimmed = value.trim();
24207
+ if (!trimmed) {
24208
+ return "";
24209
+ }
24210
+ try {
24211
+ return JSON.parse(trimmed);
24212
+ } catch {
24213
+ return value;
24214
+ }
24215
+ }
24216
+ buildAction(toolName, toolCallId, input) {
24217
+ const inputRecord = isRecord(input) ? input : {};
24218
+ if (toolName === "exec_command") {
24219
+ return {
24220
+ kind: "command",
24221
+ command: maybeString(inputRecord.cmd) ?? "",
24222
+ status: "running",
24223
+ output: ""
24224
+ };
24225
+ }
24226
+ return {
24227
+ kind: "tool_call",
24228
+ toolName,
24229
+ toolCallId,
24230
+ input,
24231
+ status: "running",
24232
+ output: ""
24233
+ };
24234
+ }
24235
+ handleToolOutput(capturedAt, toolCallId, output, isError) {
24236
+ if (!toolCallId) {
24237
+ return;
24238
+ }
24239
+ const blockId = this.toolBlockMap.get(toolCallId);
24240
+ const turn = this.currentTurn;
24241
+ if (!blockId || !turn) {
24242
+ return;
24243
+ }
24244
+ if (output) {
24245
+ this.emitEvent(capturedAt, {
24246
+ event: "block:action:output",
24247
+ sessionId: this.session.id,
24248
+ turnId: turn.id,
24249
+ blockId,
24250
+ output
24251
+ });
24252
+ }
24253
+ const exitCode = this.extractExitCode(output);
24254
+ const status = isError || exitCode != null && exitCode !== 0 ? "failed" : "completed";
24255
+ this.emitEvent(capturedAt, {
24256
+ event: "block:action:status",
24257
+ sessionId: this.session.id,
24258
+ turnId: turn.id,
24259
+ blockId,
24260
+ status,
24261
+ ...exitCode != null ? { meta: { exitCode } } : {}
24262
+ });
24263
+ const block = this.blockById.get(blockId);
24264
+ if (block) {
24265
+ this.emitBlockEnd(capturedAt, turn, block, status === "failed" ? "failed" : "completed");
24266
+ }
24267
+ this.toolBlockMap.delete(toolCallId);
24268
+ }
24269
+ extractExitCode(output) {
24270
+ const match = output.match(/(?:exit code|exited with code):\s*(-?\d+)/iu);
24271
+ if (!match) {
24272
+ return null;
24273
+ }
24274
+ const exitCode = Number(match[1]);
24275
+ return Number.isFinite(exitCode) ? exitCode : null;
24276
+ }
24277
+ captureTokenUsage(info) {
24278
+ if (!isRecord(info)) {
24279
+ return;
24280
+ }
24281
+ const total = isRecord(info.total_token_usage) ? info.total_token_usage : {};
24282
+ this.inputTokens = maybeNumber(total.input_tokens) ?? this.inputTokens;
24283
+ this.outputTokens = maybeNumber(total.output_tokens) ?? this.outputTokens;
24284
+ this.reasoningOutputTokens = maybeNumber(total.reasoning_output_tokens) ?? this.reasoningOutputTokens;
24285
+ this.cachedInputTokens = maybeNumber(total.cached_input_tokens) ?? this.cachedInputTokens;
24286
+ this.tokenEventCount += 1;
24287
+ }
24288
+ persistUsageMetadata() {
24289
+ if (this.tokenEventCount === 0) {
24290
+ return false;
24291
+ }
24292
+ const usage = this.ensureObserveMetaRecord("observeUsage");
24293
+ let changed = false;
24294
+ const assignNumber = (key, value) => {
24295
+ if (value > 0 && usage[key] !== value) {
24296
+ usage[key] = value;
24297
+ changed = true;
24298
+ }
24299
+ };
24300
+ assignNumber("inputTokens", this.inputTokens);
24301
+ assignNumber("outputTokens", this.outputTokens);
24302
+ assignNumber("reasoningOutputTokens", this.reasoningOutputTokens);
24303
+ assignNumber("cacheReadInputTokens", this.cachedInputTokens);
24304
+ assignNumber("tokenEvents", this.tokenEventCount);
24305
+ return changed;
24306
+ }
24307
+ startTurn(capturedAt, turnId) {
24308
+ if (this.currentTurn) {
24309
+ this.endTurn("stopped", capturedAt);
24310
+ }
24311
+ this.turnCounter += 1;
24312
+ this.blockIndex = 0;
24313
+ this.blockById.clear();
24314
+ this.toolBlockMap.clear();
24315
+ this.assistantMessageTextThisTurn.clear();
24316
+ this.session.status = "active";
24317
+ this.emitSessionUpdate(capturedAt);
24318
+ const turn = {
24319
+ id: turnId || `history-turn-${this.turnCounter}`,
24320
+ sessionId: this.session.id,
24321
+ status: "started",
24322
+ startedAt: new Date(capturedAt).toISOString(),
24323
+ blocks: []
24324
+ };
24325
+ this.currentTurn = turn;
24326
+ this.emitEvent(capturedAt, {
24327
+ event: "turn:start",
24328
+ sessionId: this.session.id,
24329
+ turn
24330
+ });
24331
+ return turn;
24332
+ }
24333
+ ensureTurn(capturedAt) {
24334
+ return this.currentTurn ?? this.startTurn(capturedAt);
24335
+ }
24336
+ endTurn(status, capturedAt) {
24337
+ const turn = this.currentTurn;
24338
+ if (!turn) {
24339
+ return;
24340
+ }
24341
+ turn.status = status;
24342
+ turn.endedAt = new Date(capturedAt).toISOString();
24343
+ this.emitEvent(capturedAt, {
24344
+ event: "turn:end",
24345
+ sessionId: this.session.id,
24346
+ turnId: turn.id,
24347
+ status
24348
+ });
24349
+ this.currentTurn = null;
24350
+ this.blockById.clear();
24351
+ this.toolBlockMap.clear();
24352
+ this.assistantMessageTextThisTurn.clear();
24353
+ this.session.status = "idle";
24354
+ this.emitSessionUpdate(capturedAt);
24355
+ }
24356
+ appendCompletedText(capturedAt, text) {
24357
+ const turn = this.ensureTurn(capturedAt);
24358
+ const block = this.startBlock(turn, capturedAt, {
24359
+ type: "text",
24360
+ text,
24361
+ status: "completed"
24362
+ });
24363
+ this.emitBlockEnd(capturedAt, turn, block, "completed");
24364
+ }
24365
+ startBlock(turn, capturedAt, partial2) {
24366
+ const block = {
24367
+ ...partial2,
24368
+ id: partial2.id || `${turn.id}:block:${this.blockIndex}`,
24369
+ turnId: turn.id,
24370
+ index: this.blockIndex
24371
+ };
24372
+ this.blockIndex += 1;
24373
+ turn.blocks.push(block);
24374
+ this.blockById.set(block.id, block);
24375
+ this.emitEvent(capturedAt, {
24376
+ event: "block:start",
24377
+ sessionId: this.session.id,
24378
+ turnId: turn.id,
24379
+ block
24380
+ });
24381
+ return block;
24382
+ }
24383
+ emitBlockEnd(capturedAt, turn, block, status) {
24384
+ block.status = status;
24385
+ this.emitEvent(capturedAt, {
24386
+ event: "block:end",
24387
+ sessionId: this.session.id,
24388
+ turnId: turn.id,
24389
+ blockId: block.id,
24390
+ status
24391
+ });
24392
+ }
24393
+ ensureProviderMeta() {
24394
+ const providerMeta = isRecord(this.session.providerMeta) ? this.session.providerMeta : {};
24395
+ this.session.providerMeta = providerMeta;
24396
+ return providerMeta;
24397
+ }
24398
+ ensureObserveMetaRecord(key) {
24399
+ const providerMeta = this.ensureProviderMeta();
24400
+ const existing = providerMeta[key];
24401
+ if (isRecord(existing)) {
24402
+ return existing;
24403
+ }
24404
+ const next = {};
24405
+ providerMeta[key] = next;
24406
+ return next;
24407
+ }
24408
+ assignObserveString(target, key, value) {
24409
+ const next = maybeString(value);
24410
+ if (!next || target[key] === next) {
24411
+ return false;
24412
+ }
24413
+ target[key] = next;
24414
+ return true;
24415
+ }
24416
+ emitSessionUpdate(capturedAt) {
24417
+ this.emitEvent(capturedAt, {
24418
+ event: "session:update",
24419
+ session: { ...this.session }
24420
+ });
24421
+ }
24422
+ emitEvent(capturedAt, event) {
24423
+ this.events.push({
24424
+ capturedAt,
24425
+ event: structuredClone(event)
24426
+ });
24427
+ }
24428
+ }
23712
24429
  // packages/agent-sessions/src/adapters/claude-code.ts
23713
24430
  import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
23714
24431
  import { homedir as homedir8 } from "os";
@@ -24316,7 +25033,7 @@ import { homedir as homedir10 } from "os";
24316
25033
  // packages/agent-sessions/src/codex-launch-config.ts
24317
25034
  import { accessSync as accessSync2, constants as constants2, existsSync as existsSync9 } from "fs";
24318
25035
  import { homedir as homedir9 } from "os";
24319
- import { basename as basename6, delimiter as delimiter2, dirname as dirname4, join as join10, resolve as resolve3 } from "path";
25036
+ import { basename as basename7, delimiter as delimiter2, dirname as dirname5, join as join10, resolve as resolve3 } from "path";
24320
25037
  import { fileURLToPath } from "url";
24321
25038
  function isExecutable(filePath) {
24322
25039
  if (!filePath) {
@@ -24337,7 +25054,7 @@ function ancestorChain(start) {
24337
25054
  let current = resolve3(start);
24338
25055
  while (true) {
24339
25056
  chain.push(current);
24340
- const parent = dirname4(current);
25057
+ const parent = dirname5(current);
24341
25058
  if (parent === current) {
24342
25059
  return chain;
24343
25060
  }
@@ -24373,7 +25090,7 @@ function resolveBunExecutable2(env) {
24373
25090
  return candidate;
24374
25091
  }
24375
25092
  }
24376
- if (basename6(process.execPath).startsWith("bun") && isExecutable(process.execPath)) {
25093
+ if (basename7(process.execPath).startsWith("bun") && isExecutable(process.execPath)) {
24377
25094
  return process.execPath;
24378
25095
  }
24379
25096
  return resolveExecutableFromSearchPath(["bun"], env);
@@ -24393,7 +25110,7 @@ function resolveScoutExecutable(env) {
24393
25110
  return resolveExecutableFromSearchPath(["scout"], env);
24394
25111
  }
24395
25112
  function resolveRepoScoutScript(currentDirectory) {
24396
- const moduleDirectory = dirname4(fileURLToPath(import.meta.url));
25113
+ const moduleDirectory = dirname5(fileURLToPath(import.meta.url));
24397
25114
  const starts = uniquePaths([currentDirectory, moduleDirectory]);
24398
25115
  for (const start of starts) {
24399
25116
  for (const candidate of ancestorChain(start)) {
@@ -24681,7 +25398,7 @@ function stringifyValue2(value) {
24681
25398
  return String(value);
24682
25399
  }
24683
25400
  }
24684
- function extractReasoningText(item) {
25401
+ function extractReasoningText2(item) {
24685
25402
  const summary = Array.isArray(item.summary) ? item.summary : [];
24686
25403
  const content = Array.isArray(item.content) ? item.content : [];
24687
25404
  const summaryText = summary.map((entry) => {
@@ -25153,7 +25870,7 @@ class CodexAdapter extends BaseAdapter {
25153
25870
  this.ensureTextBlock(turnState, itemId, typeof item.text === "string" ? item.text : "");
25154
25871
  return;
25155
25872
  case "reasoning": {
25156
- const text = extractReasoningText(item);
25873
+ const text = extractReasoningText2(item);
25157
25874
  if (text) {
25158
25875
  this.ensureReasoningBlock(turnState, itemId, text);
25159
25876
  }
@@ -25232,7 +25949,7 @@ class CodexAdapter extends BaseAdapter {
25232
25949
  return;
25233
25950
  }
25234
25951
  case "reasoning": {
25235
- const finalText = extractReasoningText(item);
25952
+ const finalText = extractReasoningText2(item);
25236
25953
  if (!finalText && !turnState.blocksByItemId.has(itemId)) {
25237
25954
  return;
25238
25955
  }
@@ -28038,7 +28755,7 @@ import { execFileSync as execFileSync2 } from "child_process";
28038
28755
  import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
28039
28756
  import { access, mkdir as mkdir6, readdir as readdir2, readFile as readFile8, realpath, rm as rm4, stat as stat3, writeFile as writeFile6 } from "fs/promises";
28040
28757
  import { homedir as homedir12, hostname as hostname3, userInfo } from "os";
28041
- import { basename as basename7, dirname as dirname7, isAbsolute, join as join16, relative as relative3, resolve as resolve5 } from "path";
28758
+ import { basename as basename8, dirname as dirname8, isAbsolute, join as join16, relative as relative3, resolve as resolve5 } from "path";
28042
28759
  import { fileURLToPath as fileURLToPath3 } from "url";
28043
28760
 
28044
28761
  // node_modules/.bun/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
@@ -28990,7 +29707,7 @@ function parse5(toml, { maxDepth = 1000, integersAsBigInt } = {}) {
28990
29707
  // packages/runtime/src/harness-catalog.ts
28991
29708
  import { existsSync as existsSync10, statSync as statSync6 } from "fs";
28992
29709
  import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
28993
- import { dirname as dirname5, join as join14 } from "path";
29710
+ import { dirname as dirname6, join as join14 } from "path";
28994
29711
  var HARNESS_CATALOG_VERSION = 1;
28995
29712
  var DEFAULT_SUPPORT = {
28996
29713
  install: true,
@@ -29183,7 +29900,7 @@ var BUILT_IN_HARNESS_CATALOG = [
29183
29900
  }
29184
29901
  ];
29185
29902
  async function writeHarnessCatalogOverrides(overrides, overridePath = resolveOpenScoutSupportPaths().harnessCatalogPath) {
29186
- await mkdir5(dirname5(overridePath), { recursive: true });
29903
+ await mkdir5(dirname6(overridePath), { recursive: true });
29187
29904
  const payload = {
29188
29905
  version: HARNESS_CATALOG_VERSION,
29189
29906
  entries: overrides,
@@ -29201,7 +29918,7 @@ async function ensureHarnessCatalogOverrideFile(overridePath = resolveOpenScoutS
29201
29918
  // packages/runtime/src/user-project-hints.ts
29202
29919
  import { readdir, readFile as readFile7, stat as stat2 } from "fs/promises";
29203
29920
  import { homedir as homedir11 } from "os";
29204
- import { dirname as dirname6, join as join15, resolve as resolve4 } from "path";
29921
+ import { dirname as dirname7, join as join15, resolve as resolve4 } from "path";
29205
29922
  import { fileURLToPath as fileURLToPath2 } from "url";
29206
29923
  var PROJECT_HINT_MARKERS = [
29207
29924
  ".git",
@@ -29238,7 +29955,7 @@ async function resolveExistingDirectoryHint(pathLike) {
29238
29955
  return absolutePath;
29239
29956
  }
29240
29957
  if (info.isFile()) {
29241
- return dirname6(absolutePath);
29958
+ return dirname7(absolutePath);
29242
29959
  }
29243
29960
  } catch {
29244
29961
  return null;
@@ -29265,7 +29982,7 @@ async function findLikelyProjectRoot(absolutePath) {
29265
29982
  }
29266
29983
  }
29267
29984
  }
29268
- const parent = dirname6(current);
29985
+ const parent = dirname7(current);
29269
29986
  if (parent === current) {
29270
29987
  return lastCandidate ?? absolutePath;
29271
29988
  }
@@ -29406,7 +30123,7 @@ async function appendCursorWorkspacePaths(home, roots) {
29406
30123
  }
29407
30124
  const wsRaw = await readFile7(wsFile, "utf8");
29408
30125
  const wsDoc = JSON.parse(wsRaw);
29409
- const wsDir = dirname6(wsFile);
30126
+ const wsDir = dirname7(wsFile);
29410
30127
  for (const f of wsDoc.folders ?? []) {
29411
30128
  if (typeof f.path !== "string" || !f.path) {
29412
30129
  continue;
@@ -30115,7 +30832,7 @@ async function readJsonFile(filePath) {
30115
30832
  }
30116
30833
  }
30117
30834
  async function writeJsonFile(filePath, value) {
30118
- await mkdir6(dirname7(filePath), { recursive: true });
30835
+ await mkdir6(dirname8(filePath), { recursive: true });
30119
30836
  await writeFile6(filePath, JSON.stringify(value, null, 2) + `
30120
30837
  `, "utf8");
30121
30838
  }
@@ -30360,7 +31077,7 @@ function applyImportedEnvironmentSeeds(config2, codexImport, projectRoot) {
30360
31077
  ...(config2.environment?.actions?.length ?? 0) === 0 && importedEnvironment.actions ? { actions: importedEnvironment.actions } : {}
30361
31078
  };
30362
31079
  }
30363
- const defaultProjectName = titleCase2(basename7(projectRoot));
31080
+ const defaultProjectName = titleCase2(basename8(projectRoot));
30364
31081
  if (codexImport?.name?.trim() && (!config2.project.name?.trim() || config2.project.name === defaultProjectName)) {
30365
31082
  next.project = {
30366
31083
  ...config2.project,
@@ -30570,7 +31287,7 @@ async function seedWorkspaceRoots(options) {
30570
31287
  const roots = new Set;
30571
31288
  const currentProjectRoot = options.currentDirectory ? await findNearestProjectRoot(options.currentDirectory) : null;
30572
31289
  if (currentProjectRoot) {
30573
- roots.add(dirname7(currentProjectRoot));
31290
+ roots.add(dirname8(currentProjectRoot));
30574
31291
  }
30575
31292
  const legacyRoot = options.legacyRelayConfig?.projectRoot?.trim();
30576
31293
  if (legacyRoot) {
@@ -30578,7 +31295,7 @@ async function seedWorkspaceRoots(options) {
30578
31295
  }
30579
31296
  for (const record2 of Object.values(options.legacyAgents ?? {})) {
30580
31297
  if (record2.cwd?.trim()) {
30581
- roots.add(dirname7(normalizePath(record2.cwd)));
31298
+ roots.add(dirname8(normalizePath(record2.cwd)));
30582
31299
  }
30583
31300
  }
30584
31301
  if (roots.size === 0) {
@@ -30599,7 +31316,7 @@ async function readOpenScoutSettings(options = {}) {
30599
31316
  });
30600
31317
  }
30601
31318
  var SCOUT_SKILL_FILE_NAME = "SKILL.md";
30602
- var SETUP_MODULE_DIRECTORY = dirname7(fileURLToPath3(import.meta.url));
31319
+ var SETUP_MODULE_DIRECTORY = dirname8(fileURLToPath3(import.meta.url));
30603
31320
  var SCOUT_SKILL_REPO_ROOT = resolve5(SETUP_MODULE_DIRECTORY, "..", "..", "..");
30604
31321
  var SCOUT_SKILL_INSTALL_PATHS = {
30605
31322
  claude: join16(homedir12(), ".claude", "skills", "scout", SCOUT_SKILL_FILE_NAME),
@@ -30693,7 +31410,7 @@ async function findNearestProjectRoot(startDirectory) {
30693
31410
  if (await pathExists2(projectConfigPath(current)) || await pathExists2(join16(current, "package.json")) || await pathExists2(join16(current, "AGENTS.md")) || await pathExists2(join16(current, "CLAUDE.md"))) {
30694
31411
  lastCandidate = current;
30695
31412
  }
30696
- const parent = dirname7(current);
31413
+ const parent = dirname8(current);
30697
31414
  if (parent === current) {
30698
31415
  return lastCandidate;
30699
31416
  }
@@ -30701,7 +31418,7 @@ async function findNearestProjectRoot(startDirectory) {
30701
31418
  }
30702
31419
  }
30703
31420
  function defaultProjectConfig(projectRoot, settings, preferredHarness) {
30704
- const projectName = basename7(projectRoot);
31421
+ const projectName = basename8(projectRoot);
30705
31422
  const definitionId = normalizeAgentId(projectName);
30706
31423
  const relativeRoot = relative3(projectRoot, projectRoot) || ".";
30707
31424
  return {
@@ -30992,7 +31709,7 @@ function relayAgentOverrideFromResolvedConfig(config2) {
30992
31709
  }
30993
31710
  function projectDefaultDefinitionIds(config2) {
30994
31711
  return new Set([
30995
- basename7(config2.projectRoot),
31712
+ basename8(config2.projectRoot),
30996
31713
  config2.projectName
30997
31714
  ].map((value) => normalizeAgentId(value)).filter(Boolean));
30998
31715
  }
@@ -31033,8 +31750,8 @@ function selectorFromInput(value) {
31033
31750
  }
31034
31751
  async function resolveManifestBackedAgent(projectRoot, config2, settings, override) {
31035
31752
  const resolvedProjectRoot = resolveProjectRootFromConfig(projectRoot, config2);
31036
- const projectName = config2.project.name?.trim() || titleCase2(config2.project.id || basename7(resolvedProjectRoot));
31037
- const fallbackDefinitionId = normalizeAgentId(config2.project.id || basename7(resolvedProjectRoot));
31753
+ const projectName = config2.project.name?.trim() || titleCase2(config2.project.id || basename8(resolvedProjectRoot));
31754
+ const fallbackDefinitionId = normalizeAgentId(config2.project.id || basename8(resolvedProjectRoot));
31038
31755
  const definitionId = normalizeAgentId(config2.agent?.id || override?.definitionId || fallbackDefinitionId);
31039
31756
  const detectedHarness = await detectPreferredHarness(resolvedProjectRoot, settings.agents.defaultHarness);
31040
31757
  const runtimeDefaults = config2.agent?.runtime?.defaults;
@@ -31072,7 +31789,7 @@ async function resolveManifestBackedAgent(projectRoot, config2, settings, overri
31072
31789
  return mergeResolvedAgentConfig(base, override, settings);
31073
31790
  }
31074
31791
  async function resolveInferredAgent(projectRoot, settings, override) {
31075
- const projectName = basename7(projectRoot);
31792
+ const projectName = basename8(projectRoot);
31076
31793
  const definitionId = normalizeAgentId(override?.definitionId?.trim() || projectName);
31077
31794
  const detectedHarness = await detectPreferredHarness(projectRoot, settings.agents.defaultHarness);
31078
31795
  const defaultHarness = normalizeManagedHarness(override?.defaultHarness ?? override?.runtime?.harness, normalizeManagedHarness(detectedHarness, "claude"));
@@ -31108,7 +31825,7 @@ async function resolveInferredAgent(projectRoot, settings, override) {
31108
31825
  return mergeResolvedAgentConfig(base, override, settings);
31109
31826
  }
31110
31827
  async function buildProjectInventoryEntry(agent, sourceRoot) {
31111
- const manifestRoot = agent.projectConfigPath ? normalizePath(join16(dirname7(agent.projectConfigPath), "..")) : null;
31828
+ const manifestRoot = agent.projectConfigPath ? normalizePath(join16(dirname8(agent.projectConfigPath), "..")) : null;
31112
31829
  const manifest = manifestRoot ? await readProjectConfig(manifestRoot) : null;
31113
31830
  const markers = await detectHarnessMarkers(agent.projectRoot);
31114
31831
  const harnesses = new Map;
@@ -31373,7 +32090,7 @@ async function ensureRelayAgentConfigured(value, options = {}) {
31373
32090
  import { spawnSync } from "child_process";
31374
32091
  import { chmodSync, existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
31375
32092
  import { homedir as homedir13 } from "os";
31376
- import { dirname as dirname8, join as join17, resolve as resolve6 } from "path";
32093
+ import { dirname as dirname9, join as join17, resolve as resolve6 } from "path";
31377
32094
  import { fileURLToPath as fileURLToPath4 } from "url";
31378
32095
 
31379
32096
  // packages/runtime/src/broker-api.ts
@@ -31624,7 +32341,7 @@ function runtimePackageDir() {
31624
32341
  const fromGlobal = findGlobalRuntimeDir();
31625
32342
  if (fromGlobal)
31626
32343
  return fromGlobal;
31627
- const moduleDir = dirname8(fileURLToPath4(import.meta.url));
32344
+ const moduleDir = dirname9(fileURLToPath4(import.meta.url));
31628
32345
  return resolve6(moduleDir, "..");
31629
32346
  }
31630
32347
  function isInstalledRuntimePackageDir(candidate) {
@@ -31658,7 +32375,7 @@ function findGlobalRuntimeDir() {
31658
32375
  return null;
31659
32376
  }
31660
32377
  function findBundledRuntimeDir() {
31661
- const moduleDir = dirname8(fileURLToPath4(import.meta.url));
32378
+ const moduleDir = dirname9(fileURLToPath4(import.meta.url));
31662
32379
  const candidate = resolve6(moduleDir, "..");
31663
32380
  return isInstalledRuntimePackageDir(candidate) ? candidate : null;
31664
32381
  }
@@ -31669,7 +32386,7 @@ function findWorkspaceRuntimeDir(startDir) {
31669
32386
  if (existsSync12(join17(candidate, "package.json")) && existsSync12(join17(candidate, "src"))) {
31670
32387
  return candidate;
31671
32388
  }
31672
- const parent = dirname8(current);
32389
+ const parent = dirname9(current);
31673
32390
  if (parent === current)
31674
32391
  return null;
31675
32392
  current = parent;
@@ -31868,7 +32585,7 @@ function xmlEscape(value) {
31868
32585
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
31869
32586
  }
31870
32587
  function ensureParentDirectory(filePath) {
31871
- mkdirSync2(dirname8(filePath), { recursive: true });
32588
+ mkdirSync2(dirname9(filePath), { recursive: true });
31872
32589
  }
31873
32590
  function ensureServiceDirectories(config2) {
31874
32591
  ensureOpenScoutCleanSlateSync();
@@ -32242,7 +32959,7 @@ function compileCodexPermissionProfile(profileInput) {
32242
32959
  // packages/runtime/src/user-config.ts
32243
32960
  import { existsSync as existsSync13, readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
32244
32961
  import { homedir as homedir14 } from "os";
32245
- import { dirname as dirname9, join as join18 } from "path";
32962
+ import { dirname as dirname10, join as join18 } from "path";
32246
32963
  function userConfigPath() {
32247
32964
  return join18(process.env.OPENSCOUT_HOME ?? join18(homedir14(), ".openscout"), "user.json");
32248
32965
  }
@@ -32269,7 +32986,7 @@ function resolveOperatorHandle() {
32269
32986
  }
32270
32987
 
32271
32988
  // packages/runtime/src/local-agents.ts
32272
- var MODULE_DIRECTORY = dirname10(fileURLToPath5(import.meta.url));
32989
+ var MODULE_DIRECTORY = dirname11(fileURLToPath5(import.meta.url));
32273
32990
  var OPENSCOUT_REPO_ROOT = resolve7(MODULE_DIRECTORY, "..", "..", "..");
32274
32991
  var DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
32275
32992
  var DEFAULT_LOCAL_AGENT_HARNESS = "claude";
@@ -32285,13 +33002,13 @@ function resolveProjectsRoot(projectPath) {
32285
33002
  try {
32286
33003
  const supportPaths = resolveOpenScoutSupportPaths();
32287
33004
  if (!existsSync14(supportPaths.settingsPath)) {
32288
- return dirname10(projectPath);
33005
+ return dirname11(projectPath);
32289
33006
  }
32290
33007
  const raw = JSON.parse(readFileSync7(supportPaths.settingsPath, "utf8"));
32291
33008
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
32292
- return workspaceRoot ? resolve7(workspaceRoot) : dirname10(projectPath);
33009
+ return workspaceRoot ? resolve7(workspaceRoot) : dirname11(projectPath);
32293
33010
  } catch {
32294
- return dirname10(projectPath);
33011
+ return dirname11(projectPath);
32295
33012
  }
32296
33013
  }
32297
33014
  function resolveBrokerUrl() {
@@ -32729,6 +33446,7 @@ var DEFAULT_CLAUDE_SCOUT_ALLOWED_TOOLS = [
32729
33446
  "mcp__scout__messages_inbox",
32730
33447
  "mcp__scout__messages_channel",
32731
33448
  "mcp__scout__broker_feed",
33449
+ "mcp__scout__tail_events",
32732
33450
  "mcp__scout__agents_search",
32733
33451
  "mcp__scout__agents_resolve",
32734
33452
  "mcp__scout__messages_reply",
@@ -33091,7 +33809,7 @@ function normalizeLocalAgentCardLifecycle(value, now = Date.now()) {
33091
33809
  function normalizeLocalAgentRecord(agentId, record2) {
33092
33810
  const cwd = normalizeProjectPath2(record2.cwd || process.cwd());
33093
33811
  const projectRoot = normalizeProjectPath2(record2.projectRoot || cwd);
33094
- const project = record2.project?.trim() || basename9(projectRoot);
33812
+ const project = record2.project?.trim() || basename10(projectRoot);
33095
33813
  const definitionId = record2.definitionId?.trim() || agentId;
33096
33814
  const defaultHarness = activeLocalHarness(record2);
33097
33815
  const harnessProfiles = normalizeLocalHarnessProfiles(agentId, {
@@ -33161,7 +33879,7 @@ function localAgentRecordFromRelayAgentOverride(agentId, override) {
33161
33879
  return normalizeLocalAgentRecord(agentId, {
33162
33880
  definitionId: override.definitionId ?? agentId,
33163
33881
  registrationSource: override.source,
33164
- project: override.projectName ?? basename9(override.projectRoot || override.runtime?.cwd || agentId),
33882
+ project: override.projectName ?? basename10(override.projectRoot || override.runtime?.cwd || agentId),
33165
33883
  projectRoot: override.projectRoot ?? override.runtime?.cwd,
33166
33884
  tmuxSession: override.runtime?.sessionId ?? `relay-${agentId}`,
33167
33885
  cwd: override.runtime?.cwd ?? override.projectRoot,
@@ -33215,6 +33933,13 @@ async function ensureLocalSessionEndpointOnline(endpoint) {
33215
33933
  }
33216
33934
  return {};
33217
33935
  }
33936
+ function clearEndpointFailureMetadata(metadata) {
33937
+ const { lastError: _lastError, lastFailedAt: _lastFailedAt, ...baseMetadata } = metadata ?? {};
33938
+ return baseMetadata;
33939
+ }
33940
+ function endpointStateAfterSuccessfulSessionWarmup(state) {
33941
+ return state === "active" ? "active" : "idle";
33942
+ }
33218
33943
  async function shutdownLocalSessionEndpoint(endpoint) {
33219
33944
  if (endpoint.transport === "codex_app_server") {
33220
33945
  await shutdownCodexAppServerAgent(buildCodexEndpointSessionOptions(endpoint));
@@ -33547,7 +34272,7 @@ function invocationSessionFreshness(invocation) {
33547
34272
  case "existing":
33548
34273
  return "continuing session";
33549
34274
  case "any":
33550
- return "reuse-or-new session";
34275
+ return invocation.execution?.targetSessionId ? "continuing session" : "fresh session";
33551
34276
  default:
33552
34277
  return "session unspecified";
33553
34278
  }
@@ -34016,7 +34741,7 @@ async function ensureLocalAgentOnline(agentName, record2) {
34016
34741
  return normalizedRecord;
34017
34742
  }
34018
34743
  const projectPath = normalizedRecord.cwd;
34019
- const projectName = normalizedRecord.project || basename9(projectPath);
34744
+ const projectName = normalizedRecord.project || basename10(projectPath);
34020
34745
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
34021
34746
  const systemPrompt = renderLocalAgentSystemPromptTemplate(systemPromptTemplate, buildLocalAgentTemplateContext(agentName, projectName, projectPath), { transport: normalizedRecord.transport });
34022
34747
  const agentRuntimeDir = relayAgentRuntimeDirectory(agentName);
@@ -34199,7 +34924,7 @@ async function startLocalAgent(input) {
34199
34924
  }
34200
34925
  if (!matchingOverride) {
34201
34926
  const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
34202
- const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename9(projectRoot)) || "agent";
34927
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename10(projectRoot)) || "agent";
34203
34928
  const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
34204
34929
  const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(definitionId);
34205
34930
  const effectiveDisplayName = input.displayName || operatorAugmentDefaults?.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
@@ -34220,7 +34945,7 @@ async function startLocalAgent(input) {
34220
34945
  agentId: instance.id,
34221
34946
  definitionId,
34222
34947
  displayName: effectiveDisplayName,
34223
- projectName: basename9(projectRoot),
34948
+ projectName: basename10(projectRoot),
34224
34949
  projectRoot,
34225
34950
  projectConfigPath: coldProjectConfigPath,
34226
34951
  source: "manual",
@@ -34269,7 +34994,7 @@ async function startLocalAgent(input) {
34269
34994
  agentId: instance.id,
34270
34995
  definitionId: requestedDefinitionId,
34271
34996
  displayName: input.displayName || operatorAugmentDefaults?.displayName || titleCaseLocalAgentName(requestedDefinitionId),
34272
- projectName: matchingOverride.projectName ?? basename9(matchingProjectRoot),
34997
+ projectName: matchingOverride.projectName ?? basename10(matchingProjectRoot),
34273
34998
  projectRoot: matchingProjectRoot,
34274
34999
  projectConfigPath: null,
34275
35000
  source: "manual",
@@ -34625,7 +35350,7 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
34625
35350
  const requestedHarness = invocation.execution?.harness;
34626
35351
  const record2 = existing ?? (projectRoot ? {
34627
35352
  definitionId,
34628
- project: basename9(projectRoot),
35353
+ project: basename10(projectRoot),
34629
35354
  projectRoot,
34630
35355
  tmuxSession: String(endpoint.metadata?.tmuxSession ?? `relay-${agentRuntimeId}`),
34631
35356
  cwd: projectRoot,
@@ -34872,7 +35597,7 @@ function upsertScoutAgentCardFromInput(runtime, input) {
34872
35597
 
34873
35598
  // packages/runtime/src/pairing-session-agents.ts
34874
35599
  import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
34875
- import { basename as basename10, join as join20 } from "path";
35600
+ import { basename as basename11, join as join20 } from "path";
34876
35601
  import { homedir as homedir15 } from "os";
34877
35602
  var DEFAULT_PAIRING_PORT = 7888;
34878
35603
  var PAIRING_CONNECT_TIMEOUT_MS = 1500;
@@ -35059,7 +35784,7 @@ function shortPairingSessionId(sessionId) {
35059
35784
  }
35060
35785
  function pairingProjectLabel(session) {
35061
35786
  const cwd = session.cwd?.trim();
35062
- return cwd ? basename10(cwd) : null;
35787
+ return cwd ? basename11(cwd) : null;
35063
35788
  }
35064
35789
  function pairingDisplayName(session, shortId) {
35065
35790
  const explicit = session.name?.trim();
@@ -35440,7 +36165,7 @@ async function invokePairingSessionEndpoint(endpoint, invocation, options = {})
35440
36165
 
35441
36166
  // packages/runtime/src/sqlite-store.ts
35442
36167
  import { mkdirSync as mkdirSync5 } from "fs";
35443
- import { dirname as dirname12 } from "path";
36168
+ import { dirname as dirname13 } from "path";
35444
36169
  import { Database as Database2 } from "bun:sqlite";
35445
36170
 
35446
36171
  // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/entity.js
@@ -40042,6 +40767,9 @@ function stampControlPlaneSchemaVersion(database) {
40042
40767
  }
40043
40768
  if (false) {}
40044
40769
 
40770
+ // packages/runtime/src/conversations/api.ts
40771
+ import { randomUUID as randomUUID3 } from "crypto";
40772
+
40045
40773
  // packages/runtime/src/conversations/legacy-ids.ts
40046
40774
  function conversationIdForAgent(agentId) {
40047
40775
  return buildDirectConversationId("operator", agentId);
@@ -40107,11 +40835,22 @@ class Conversations {
40107
40835
  findById(id) {
40108
40836
  return this.store.getConversation(id);
40109
40837
  }
40110
- findByNaturalKey(_key) {
40838
+ findByNaturalKey(key) {
40839
+ const normalizedKey = key.trim();
40840
+ if (!normalizedKey) {
40841
+ return null;
40842
+ }
40843
+ const rows = this.readDb.query("SELECT id FROM conversations ORDER BY created_at ASC").all();
40844
+ for (const row of rows) {
40845
+ const conversation = this.findById(row.id);
40846
+ if (conversation && channelNaturalKeyFromMetadata(conversation.metadata) === normalizedKey) {
40847
+ return conversation;
40848
+ }
40849
+ }
40111
40850
  return null;
40112
40851
  }
40113
40852
  findByAgent(agentId) {
40114
- return this.findById(conversationIdForAgent(agentId));
40853
+ return this.findById(conversationIdForAgent(agentId)) ?? this.findByNaturalKey(directChannelNaturalKey(["operator", agentId]));
40115
40854
  }
40116
40855
  findByParent(parentId) {
40117
40856
  const rows = this.readDb.query("SELECT id FROM conversations WHERE parent_conversation_id = ?1 ORDER BY created_at ASC").all(parentId);
@@ -40155,7 +40894,7 @@ class Conversations {
40155
40894
  return existing;
40156
40895
  }
40157
40896
  const conversation = {
40158
- id: input.naturalKey,
40897
+ id: mintChannelId(randomUUID3),
40159
40898
  kind: input.kind,
40160
40899
  title: input.title,
40161
40900
  visibility: input.visibility,
@@ -40164,7 +40903,10 @@ class Conversations {
40164
40903
  participantIds: input.participantIds,
40165
40904
  parentConversationId: input.parentConversationId,
40166
40905
  topic: input.topic,
40167
- metadata: input.metadata
40906
+ metadata: {
40907
+ ...input.metadata ?? {},
40908
+ naturalKey: input.naturalKey
40909
+ }
40168
40910
  };
40169
40911
  this.upsert(conversation);
40170
40912
  return conversation;
@@ -40182,6 +40924,10 @@ class Conversations {
40182
40924
  }
40183
40925
  const parsedDirect = parseDirectConversationId(rawId);
40184
40926
  if (parsedDirect) {
40927
+ const byNaturalKey = this.findByNaturalKey(directChannelNaturalKey([parsedDirect.operatorId, parsedDirect.agentId]));
40928
+ if (byNaturalKey) {
40929
+ return byNaturalKey;
40930
+ }
40185
40931
  for (const candidate of directConversationIdCandidates(parsedDirect.agentId)) {
40186
40932
  const hit = this.findById(candidate);
40187
40933
  if (hit) {
@@ -40191,6 +40937,10 @@ class Conversations {
40191
40937
  }
40192
40938
  const legacyScoutAgentId = parseLegacyScoutSessionConversationId(rawId);
40193
40939
  if (legacyScoutAgentId) {
40940
+ const byNaturalKey = this.findByNaturalKey(directChannelNaturalKey(["operator", legacyScoutAgentId]));
40941
+ if (byNaturalKey) {
40942
+ return byNaturalKey;
40943
+ }
40194
40944
  for (const candidate of directConversationIdCandidates(legacyScoutAgentId)) {
40195
40945
  const hit = this.findById(candidate);
40196
40946
  if (hit) {
@@ -40415,7 +41165,7 @@ class SQLiteControlPlaneStore {
40415
41165
  flushPendingEventsTimer = null;
40416
41166
  conversationsApi = null;
40417
41167
  constructor(dbPath) {
40418
- mkdirSync5(dirname12(dbPath), { recursive: true });
41168
+ mkdirSync5(dirname13(dbPath), { recursive: true });
40419
41169
  this.db = new SQLiteDatabase(dbPath, { create: true });
40420
41170
  this.db.exec("PRAGMA busy_timeout = 5000;");
40421
41171
  this.db.exec("PRAGMA journal_mode = WAL;");
@@ -41658,7 +42408,7 @@ class SQLiteControlPlaneStore {
41658
42408
  if (message.replyToMessageId && this.isKnownAgentId(message.actorId)) {
41659
42409
  return "ask_replied";
41660
42410
  }
41661
- if (agentId && message.actorId !== agentId) {
42411
+ if (agentId && message.actorId !== agentId && this.isDirectConversation(message.conversationId)) {
41662
42412
  return "ask_opened";
41663
42413
  }
41664
42414
  if (this.isKnownAgentId(message.actorId)) {
@@ -41666,6 +42416,13 @@ class SQLiteControlPlaneStore {
41666
42416
  }
41667
42417
  return "message_posted";
41668
42418
  }
42419
+ isDirectConversation(conversationId, db = this.readDb) {
42420
+ if (!conversationId) {
42421
+ return false;
42422
+ }
42423
+ const row = queryGet(db, "SELECT kind FROM conversations WHERE id = ?1 LIMIT 1", conversationId);
42424
+ return row?.kind === "direct";
42425
+ }
41669
42426
  resolveActivityAgentIdForMessage(message) {
41670
42427
  if (message.class === "status" && typeof message.metadata?.targetAgentId === "string" && this.isKnownAgentId(message.metadata.targetAgentId)) {
41671
42428
  return message.metadata.targetAgentId;
@@ -43516,7 +44273,7 @@ import {
43516
44273
  writeFileSync as writeFileSync4
43517
44274
  } from "fs";
43518
44275
  import { homedir as homedir16, hostname as osHostname } from "os";
43519
- import { dirname as dirname13, join as join22 } from "path";
44276
+ import { dirname as dirname14, join as join22 } from "path";
43520
44277
  var LOCAL_CONFIG_VERSION = 1;
43521
44278
  var DEFAULT_LOCAL_CONFIG = {
43522
44279
  version: LOCAL_CONFIG_VERSION,
@@ -43771,7 +44528,7 @@ import { Database as Database3 } from "bun:sqlite";
43771
44528
  import { mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
43772
44529
  import { connect as connectHttp2 } from "http2";
43773
44530
  import { createPrivateKey, sign as signWithKey } from "crypto";
43774
- import { dirname as dirname14, join as join24 } from "path";
44531
+ import { dirname as dirname15, join as join24 } from "path";
43775
44532
  var MOBILE_PUSH_SCHEMA = `
43776
44533
  CREATE TABLE IF NOT EXISTS mobile_push_registrations (
43777
44534
  id TEXT PRIMARY KEY,
@@ -43813,7 +44570,7 @@ function writeDb() {
43813
44570
  dbHandle = null;
43814
44571
  }
43815
44572
  if (!dbHandle) {
43816
- mkdirSync7(dirname14(nextPath), { recursive: true });
44573
+ mkdirSync7(dirname15(nextPath), { recursive: true });
43817
44574
  dbHandle = new Database3(nextPath, { create: true });
43818
44575
  dbPath = nextPath;
43819
44576
  ensureMobilePushSchema(dbHandle);
@@ -44254,6 +45011,11 @@ var tailnetName = process.env.TAILSCALE_TAILNET ?? undefined;
44254
45011
  var brokerUrl = process.env.OPENSCOUT_BROKER_URL ?? buildDefaultBrokerUrl(host, port);
44255
45012
  var brokerSocketPath = process.env.OPENSCOUT_BROKER_SOCKET_PATH ?? resolveBrokerServiceConfig().brokerSocketPath;
44256
45013
  var nodeId = process.env.OPENSCOUT_NODE_ID ?? `${nodeName}-${meshId}`.toLowerCase().replace(/[^a-z0-9-]+/g, "-");
45014
+ var nodeLocalProductAgentIds = new Set([
45015
+ SCOUT_DISPATCHER_AGENT_ID,
45016
+ OPENSCOUT_COORDINATOR_AGENT_ID,
45017
+ "scoutbot"
45018
+ ]);
44257
45019
  var seedUrls = (process.env.OPENSCOUT_MESH_SEEDS ?? "").split(",").map((value) => value.trim()).filter(Boolean);
44258
45020
  var configuredCoreAgentIds = (process.env.OPENSCOUT_CORE_AGENTS ?? "").split(",").map((value) => value.trim().toLowerCase()).filter(Boolean);
44259
45021
  var discoveryIntervalMs = Number.parseInt(process.env.OPENSCOUT_MESH_DISCOVERY_INTERVAL_MS ?? "60000", 10);
@@ -44288,8 +45050,6 @@ var shuttingDown = false;
44288
45050
  var sseKeepAliveIntervalMs = Number.parseInt(process.env.OPENSCOUT_SSE_KEEPALIVE_MS ?? "15000", 10);
44289
45051
  var operatorActorId = "operator";
44290
45052
  var BROKER_SHARED_CHANNEL_ID = "channel.shared";
44291
- var BROKER_VOICE_CHANNEL_ID = "channel.voice";
44292
- var BROKER_SYSTEM_CHANNEL_ID = "channel.system";
44293
45053
  var WEB_START_POLL_TIMEOUT_MS = 15000;
44294
45054
  var WEB_START_POLL_INTERVAL_MS = 250;
44295
45055
  var webServerProcess = null;
@@ -44551,6 +45311,11 @@ async function discoverPeers(seeds = []) {
44551
45311
  continue;
44552
45312
  if (agent.homeNodeId === nodeId)
44553
45313
  continue;
45314
+ if (isNodeLocalProductAgentId(agent.id))
45315
+ continue;
45316
+ const existingAgent = runtime.agent(agent.id);
45317
+ if (existingAgent && isLocalAgentAuthority(existingAgent))
45318
+ continue;
44554
45319
  const agentHome = agent.homeNodeId || node.id;
44555
45320
  if (agentHome !== node.id)
44556
45321
  continue;
@@ -44572,6 +45337,12 @@ async function discoverPeers(seeds = []) {
44572
45337
  probes: result.probes
44573
45338
  };
44574
45339
  }
45340
+ function isNodeLocalProductAgentId(agentId) {
45341
+ return nodeLocalProductAgentIds.has(agentId.trim().toLowerCase());
45342
+ }
45343
+ function isLocalAgentAuthority(agent) {
45344
+ return agent.homeNodeId === nodeId || agent.authorityNodeId === nodeId;
45345
+ }
44575
45346
  function currentLocalNode() {
44576
45347
  return runtime.node(nodeId) ?? localNode;
44577
45348
  }
@@ -44671,7 +45442,7 @@ function resolveWebServerEntry() {
44671
45442
  if (repoEntry) {
44672
45443
  return repoEntry;
44673
45444
  }
44674
- const moduleDir = dirname15(fileURLToPath7(import.meta.url));
45445
+ const moduleDir = dirname16(fileURLToPath7(import.meta.url));
44675
45446
  const candidates = [
44676
45447
  resolve8(moduleDir, "..", "scout-control-plane-web.mjs"),
44677
45448
  resolve8(moduleDir, "..", "scout-web-server.mjs"),
@@ -44681,7 +45452,7 @@ function resolveWebServerEntry() {
44681
45452
  return candidates.find((candidate) => existsSync19(candidate)) ?? null;
44682
45453
  }
44683
45454
  function resolveWebServerRepoRoot() {
44684
- const moduleDir = dirname15(fileURLToPath7(import.meta.url));
45455
+ const moduleDir = dirname16(fileURLToPath7(import.meta.url));
44685
45456
  return resolveOpenScoutRepoRoot({
44686
45457
  startDirectories: [
44687
45458
  process.env.OPENSCOUT_SETUP_CWD,
@@ -45545,6 +46316,23 @@ function staleLocalEndpointReason(endpoint) {
45545
46316
  const replacement = typeof replacementAgentId === "string" && replacementAgentId.trim().length > 0 ? `; replacement agent is ${replacementAgentId.trim()}` : "";
45546
46317
  return `endpoint ${endpoint.id} is a stale local registration superseded by current setup${replacement}`;
45547
46318
  }
46319
+ function staleLocalAgentReason(snapshot, agent) {
46320
+ const endpoints = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agent.id);
46321
+ const staleEndpointReasons = endpoints.map((endpoint) => staleLocalEndpointReason(endpoint)).filter((reason) => Boolean(reason));
46322
+ if (endpoints.length > 0 && staleEndpointReasons.length === endpoints.length) {
46323
+ return staleLocalEndpointReason(latestEndpointForAgent(snapshot, agent.id)) ?? staleEndpointReasons[0] ?? null;
46324
+ }
46325
+ if (agent.metadata?.staleLocalRegistration !== true) {
46326
+ return null;
46327
+ }
46328
+ const endpointReason = staleLocalEndpointReason(latestEndpointForAgent(snapshot, agent.id));
46329
+ if (endpointReason) {
46330
+ return endpointReason;
46331
+ }
46332
+ const replacementAgentId = metadataStringValue2(agent.metadata, "replacedByAgentId");
46333
+ const replacement = replacementAgentId ? `; replacement agent is ${replacementAgentId}` : "";
46334
+ return `agent ${agent.id} is a stale local registration superseded by current setup${replacement}`;
46335
+ }
45548
46336
  function flightDispatchEndpointId(flight) {
45549
46337
  const dispatchAck = flight.metadata?.dispatchAck;
45550
46338
  if (!dispatchAck || typeof dispatchAck !== "object" || Array.isArray(dispatchAck)) {
@@ -45633,14 +46421,14 @@ function brokerConversationChannel(snapshot, conversationId) {
45633
46421
  if (!conversation) {
45634
46422
  return null;
45635
46423
  }
46424
+ if (typeof conversation.metadata?.channel === "string") {
46425
+ return conversation.metadata.channel;
46426
+ }
45636
46427
  return conversation.id.startsWith("channel.") ? conversation.id.replace(/^channel\./, "") : null;
45637
46428
  }
45638
46429
  function titleCaseName(value) {
45639
46430
  return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
45640
46431
  }
45641
- function sanitizeConversationSegment(value) {
45642
- return value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-") || "shared";
45643
- }
45644
46432
  function metadataStringValue2(metadata, key) {
45645
46433
  const value = metadata?.[key];
45646
46434
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
@@ -45674,7 +46462,7 @@ function brokerRouteKind(conversation) {
45674
46462
  if (conversation.kind === "direct") {
45675
46463
  return "dm";
45676
46464
  }
45677
- return conversation.id === BROKER_SHARED_CHANNEL_ID ? "broadcast" : "channel";
46465
+ return conversation.id === BROKER_SHARED_CHANNEL_ID || conversation.metadata?.channel === "shared" ? "broadcast" : "channel";
45678
46466
  }
45679
46467
  function normalizeBrokerProductTarget(value) {
45680
46468
  return value.trim().toLowerCase().replace(/^@+/, "");
@@ -45733,15 +46521,8 @@ function resolveConversationShareMode(snapshot, participantIds, fallback) {
45733
46521
  });
45734
46522
  return hasRemoteParticipant ? "shared" : fallback;
45735
46523
  }
45736
- function directConversationIdForActors(sourceId, targetId) {
45737
- if (sourceId === targetId) {
45738
- return `dm.${sourceId}.${targetId}`;
45739
- }
45740
- if (sourceId === operatorActorId || targetId === operatorActorId) {
45741
- const peerId = sourceId === operatorActorId ? targetId : sourceId;
45742
- return `dm.${operatorActorId}.${peerId}`;
45743
- }
45744
- return `dm.${[sourceId, targetId].sort().join(".")}`;
46524
+ function findConversationByIdentity(snapshot, naturalKey) {
46525
+ return Object.values(snapshot.conversations).find((conversation) => channelNaturalKeyFromMetadata(conversation.metadata) === naturalKey);
45745
46526
  }
45746
46527
  async function ensureBrokerActorForDelivery(actorId) {
45747
46528
  const snapshot = runtime.snapshot();
@@ -45762,10 +46543,11 @@ async function ensureBrokerDeliveryConversation(input) {
45762
46543
  const normalizedChannel = input.channel?.trim();
45763
46544
  const targetAgentId = input.targetAgentId?.trim();
45764
46545
  if (!normalizedChannel && targetAgentId) {
45765
- const conversationId = targetAgentId === SCOUT_DISPATCHER_AGENT_ID && input.requesterId === operatorActorId ? BROKER_SHARED_CHANNEL_ID : directConversationIdForActors(input.requesterId, targetAgentId);
45766
46546
  const participantIds = [...new Set([input.requesterId, targetAgentId])].sort();
45767
46547
  const shareMode = resolveConversationShareMode(snapshot, participantIds, "local");
45768
- const existing2 = snapshot.conversations[conversationId];
46548
+ const naturalKey = directChannelNaturalKey(participantIds);
46549
+ const existing2 = findConversationByIdentity(snapshot, naturalKey);
46550
+ const conversationId = existing2?.id ?? mintChannelId(randomUUID4);
45769
46551
  const alreadyMatches = existing2 && existing2.kind === "direct" && existing2.visibility === "private" && existing2.shareMode === shareMode && existing2.participantIds.join("\x00") === participantIds.join("\x00");
45770
46552
  if (alreadyMatches) {
45771
46553
  return existing2;
@@ -45782,6 +46564,7 @@ async function ensureBrokerDeliveryConversation(input) {
45782
46564
  participantIds,
45783
46565
  metadata: {
45784
46566
  surface: "broker",
46567
+ naturalKey,
45785
46568
  ...targetAgentId === SCOUT_DISPATCHER_AGENT_ID && input.requesterId === operatorActorId ? { role: "partner" } : {}
45786
46569
  }
45787
46570
  };
@@ -45797,48 +46580,72 @@ async function ensureBrokerDeliveryConversation(input) {
45797
46580
  ])].sort();
45798
46581
  let definition;
45799
46582
  if (channel === "voice") {
46583
+ const naturalKey = namedChannelNaturalKey("voice");
46584
+ const existing2 = findConversationByIdentity(snapshot, naturalKey);
45800
46585
  definition = {
45801
- id: BROKER_VOICE_CHANNEL_ID,
46586
+ id: existing2?.id ?? mintChannelId(randomUUID4),
45802
46587
  kind: "channel",
45803
46588
  title: "voice",
45804
46589
  visibility: "workspace",
45805
46590
  shareMode: resolveConversationShareMode(snapshot, scopedParticipants, "local"),
45806
46591
  authorityNodeId: nodeId,
45807
46592
  participantIds: scopedParticipants,
45808
- metadata: { surface: "broker", channel: "voice" }
46593
+ metadata: {
46594
+ surface: "broker",
46595
+ channel: "voice",
46596
+ naturalKey
46597
+ }
45809
46598
  };
45810
46599
  } else if (channel === "system") {
46600
+ const naturalKey = systemChannelNaturalKey("system");
46601
+ const existing2 = findConversationByIdentity(snapshot, naturalKey);
45811
46602
  definition = {
45812
- id: BROKER_SYSTEM_CHANNEL_ID,
46603
+ id: existing2?.id ?? mintChannelId(randomUUID4),
45813
46604
  kind: "system",
45814
46605
  title: "system",
45815
46606
  visibility: "system",
45816
46607
  shareMode: "local",
45817
46608
  authorityNodeId: nodeId,
45818
46609
  participantIds: [operatorActorId, input.requesterId].sort(),
45819
- metadata: { surface: "broker", channel: "system" }
46610
+ metadata: {
46611
+ surface: "broker",
46612
+ channel: "system",
46613
+ naturalKey
46614
+ }
45820
46615
  };
45821
46616
  } else if (channel === "shared") {
46617
+ const naturalKey = namedChannelNaturalKey("shared");
46618
+ const existing2 = findConversationByIdentity(snapshot, naturalKey);
45822
46619
  definition = {
45823
- id: BROKER_SHARED_CHANNEL_ID,
46620
+ id: existing2?.id ?? mintChannelId(randomUUID4),
45824
46621
  kind: "channel",
45825
46622
  title: "shared-channel",
45826
46623
  visibility: "workspace",
45827
46624
  shareMode: "shared",
45828
46625
  authorityNodeId: nodeId,
45829
46626
  participantIds: sharedParticipants,
45830
- metadata: { surface: "broker", channel: "shared" }
46627
+ metadata: {
46628
+ surface: "broker",
46629
+ channel: "shared",
46630
+ naturalKey
46631
+ }
45831
46632
  };
45832
46633
  } else {
46634
+ const naturalKey = namedChannelNaturalKey(channel);
46635
+ const existing2 = findConversationByIdentity(snapshot, naturalKey);
45833
46636
  definition = {
45834
- id: `channel.${sanitizeConversationSegment(channel)}`,
46637
+ id: existing2?.id ?? mintChannelId(randomUUID4),
45835
46638
  kind: "channel",
45836
46639
  title: channel,
45837
46640
  visibility: "workspace",
45838
46641
  shareMode: resolveConversationShareMode(snapshot, scopedParticipants, "local"),
45839
46642
  authorityNodeId: nodeId,
45840
46643
  participantIds: scopedParticipants,
45841
- metadata: { surface: "broker", channel }
46644
+ metadata: {
46645
+ surface: "broker",
46646
+ channel,
46647
+ naturalKey
46648
+ }
45842
46649
  };
45843
46650
  }
45844
46651
  const existing = snapshot.conversations[definition.id];
@@ -45870,7 +46677,22 @@ function buildBrokerReturnAddressForActor(snapshot, actorId, options = {}) {
45870
46677
  sessionId: options.sessionId ?? endpoint?.sessionId
45871
46678
  });
45872
46679
  }
45873
- function describeUnavailableDeliveryTarget(snapshot, agent) {
46680
+ function describeUnavailableDeliveryTarget(snapshot, agent, targetSessionId) {
46681
+ const staleReason = targetSessionId ? staleLocalAgentReason(snapshot, agent) : agent.metadata?.staleLocalRegistration === true ? staleLocalAgentReason(snapshot, agent) : null;
46682
+ if (staleReason) {
46683
+ const endpoint2 = latestEndpointForAgent(snapshot, agent.id);
46684
+ const projectRoot2 = brokerTargetProjectRoot(agent, endpoint2);
46685
+ return {
46686
+ agentId: agent.id,
46687
+ displayName: agent.displayName ?? agent.id,
46688
+ reason: "stale_registration",
46689
+ detail: staleReason,
46690
+ wakePolicy: agent.wakePolicy,
46691
+ endpointState: endpoint2?.state === "active" || endpoint2?.state === "idle" || endpoint2?.state === "waiting" ? "online" : endpoint2?.state === "offline" ? "offline" : "unknown",
46692
+ transport: endpoint2?.transport ?? null,
46693
+ projectRoot: projectRoot2
46694
+ };
46695
+ }
45874
46696
  const endpoint = homeEndpointForAgent(snapshot, agent.id);
45875
46697
  const projectRoot = brokerTargetProjectRoot(agent, endpoint);
45876
46698
  if (agent.metadata?.retiredFromFleet === true) {
@@ -46204,11 +47026,11 @@ function updateManagedSessionAgent(agent, input) {
46204
47026
  };
46205
47027
  }
46206
47028
  function managedLocalSessionDefaultDisplayName(input) {
46207
- const projectName = basename11(input.projectRoot ?? input.cwd) || input.cwd;
47029
+ const projectName = basename12(input.projectRoot ?? input.cwd) || input.cwd;
46208
47030
  return input.transport === "codex_app_server" ? `Codex (${projectName})` : `Claude (${projectName})`;
46209
47031
  }
46210
47032
  function suggestedManagedLocalSessionSelector(input) {
46211
- const projectName = normalizeAgentSelectorSegment(basename11(input.projectRoot ?? input.cwd) || "session") || "session";
47033
+ const projectName = normalizeAgentSelectorSegment(basename12(input.projectRoot ?? input.cwd) || "session") || "session";
46212
47034
  const prefix = input.transport === "codex_app_server" ? "codex" : "claude";
46213
47035
  return `@${prefix}-${projectName}`;
46214
47036
  }
@@ -46246,7 +47068,7 @@ function buildManagedLocalSessionAgent(input) {
46246
47068
  function buildManagedLocalSessionPairingEndpointBinding(input) {
46247
47069
  const projectRoot = resolve8(input.projectRoot ?? input.cwd);
46248
47070
  const cwd = resolve8(input.cwd);
46249
- const projectName = basename11(projectRoot) || projectRoot;
47071
+ const projectName = basename12(projectRoot) || projectRoot;
46250
47072
  const definitionId = input.definitionId?.trim() || selectorHandle(input.selector ?? input.agentId);
46251
47073
  const base = buildManagedPairingEndpointBinding({
46252
47074
  agentId: input.agentId,
@@ -47126,7 +47948,7 @@ async function reviveManagedLocalSessionEndpoint(endpoint) {
47126
47948
  }
47127
47949
  const sessionResult = await ensureLocalSessionEndpointOnline(endpoint);
47128
47950
  const externalSessionId = sessionResult.externalSessionId?.trim();
47129
- const { lastError: _lastError, lastFailedAt: _lastFailedAt, ...baseMetadata } = endpoint.metadata ?? {};
47951
+ const baseMetadata = clearEndpointFailureMetadata(endpoint.metadata);
47130
47952
  const revivedEndpoint = {
47131
47953
  ...endpoint,
47132
47954
  state: "idle",
@@ -47147,19 +47969,23 @@ async function resolveLocalEndpointForInvocation(invocation) {
47147
47969
  const requestedHarness = invocation.execution?.harness;
47148
47970
  const targetSessionId = invocationTargetSessionId(invocation);
47149
47971
  const sessionPreference = invocation.execution?.session ?? "new";
47972
+ const shouldUseExistingSession = Boolean(targetSessionId);
47150
47973
  const existing = activeLocalEndpointForAgent(invocation.targetAgentId, requestedHarness, targetSessionId);
47151
- if (existing && (sessionPreference !== "new" || existing.transport === "pairing_bridge")) {
47974
+ if (existing && (shouldUseExistingSession || existing.transport === "pairing_bridge")) {
47152
47975
  return existing;
47153
47976
  }
47154
- const staleEndpoints = runtime.endpointsForAgent(invocation.targetAgentId, {
47977
+ if (!shouldUseExistingSession && sessionPreference === "existing") {
47978
+ return;
47979
+ }
47980
+ const staleEndpoints = shouldUseExistingSession ? runtime.endpointsForAgent(invocation.targetAgentId, {
47155
47981
  nodeId,
47156
47982
  harness: requestedHarness
47157
- }).filter((endpoint) => endpoint.id !== existing?.id && (targetSessionId ? endpointMatchesTargetSession(endpoint, targetSessionId) : true));
47983
+ }).filter((endpoint) => endpoint.id !== existing?.id && endpointMatchesTargetSession(endpoint, targetSessionId)) : [];
47158
47984
  const staleLocalReason = staleEndpoints.map((endpoint) => staleLocalEndpointReason(endpoint)).find((reason) => Boolean(reason));
47159
47985
  if (staleLocalReason) {
47160
47986
  throw new Error(staleLocalReason);
47161
47987
  }
47162
- if (invocation.ensureAwake) {
47988
+ if (invocation.ensureAwake && shouldUseExistingSession) {
47163
47989
  for (const endpoint of staleEndpoints) {
47164
47990
  try {
47165
47991
  const revived = await reviveManagedLocalSessionEndpoint(endpoint);
@@ -47192,9 +48018,6 @@ async function resolveLocalEndpointForInvocation(invocation) {
47192
48018
  if (!invocation.ensureAwake) {
47193
48019
  return;
47194
48020
  }
47195
- if (sessionPreference === "existing") {
47196
- return;
47197
- }
47198
48021
  if (targetSessionId) {
47199
48022
  return;
47200
48023
  }
@@ -47236,7 +48059,8 @@ async function executeLocalInvocation(invocation, initialFlight) {
47236
48059
  return;
47237
48060
  }
47238
48061
  if (!agent || !endpoint) {
47239
- const staleEndpointReason = staleLocalEndpointReason(latestEndpointForAgent(runtime.snapshot(), invocation.targetAgentId));
48062
+ const targetSessionId = invocationTargetSessionId(invocation);
48063
+ const staleEndpointReason = targetSessionId ? staleLocalEndpointReason(latestEndpointForAgent(runtime.snapshot(), invocation.targetAgentId)) : null;
47240
48064
  if (staleEndpointReason) {
47241
48065
  const failedFlight = {
47242
48066
  ...initialFlight,
@@ -47257,7 +48081,15 @@ async function executeLocalInvocation(invocation, initialFlight) {
47257
48081
  const queuedFlight = {
47258
48082
  ...initialFlight,
47259
48083
  state: "queued",
47260
- summary: `Message stored for ${agent?.displayName ?? invocation.targetAgentId}. Will deliver when online.`
48084
+ summary: `Message stored for ${agent?.displayName ?? invocation.targetAgentId}. Will deliver when online.`,
48085
+ metadata: {
48086
+ ...initialFlight.metadata ?? {},
48087
+ dispatchOutcome: {
48088
+ status: "queued_until_online",
48089
+ reason: "no_runnable_endpoint",
48090
+ checkedAt: Date.now()
48091
+ }
48092
+ }
47261
48093
  };
47262
48094
  await persistFlight(queuedFlight);
47263
48095
  return;
@@ -47654,6 +48486,39 @@ function parseLimit(url2) {
47654
48486
  return 100;
47655
48487
  return Math.min(limit, 500);
47656
48488
  }
48489
+ function parseTailLimit(url2) {
48490
+ const limit = Number.parseInt(url2.searchParams.get("limit") ?? "500", 10);
48491
+ if (!Number.isFinite(limit) || limit <= 0)
48492
+ return 500;
48493
+ return Math.min(limit, 1000);
48494
+ }
48495
+ async function readTailRecentPayload(url2) {
48496
+ const limit = parseTailLimit(url2);
48497
+ const bufferedEvents = await readRecentLiveEvents(limit);
48498
+ const eventsById = new Map;
48499
+ if (url2.searchParams.get("transcripts") === "true" || url2.searchParams.get("transcripts") === "1") {
48500
+ const transcriptEvents = await readRecentTranscriptEvents(limit, {
48501
+ perTranscriptLineLimit: Math.min(200, Math.max(50, limit))
48502
+ });
48503
+ for (const event of transcriptEvents) {
48504
+ eventsById.set(event.id, event);
48505
+ }
48506
+ }
48507
+ for (const event of bufferedEvents) {
48508
+ eventsById.set(event.id, event);
48509
+ }
48510
+ const events2 = [...eventsById.values()].sort((left, right) => {
48511
+ if (left.ts === right.ts)
48512
+ return left.id.localeCompare(right.id);
48513
+ return left.ts - right.ts;
48514
+ }).slice(-limit);
48515
+ return {
48516
+ generatedAt: Date.now(),
48517
+ limit,
48518
+ cursor: events2.at(-1)?.id ?? null,
48519
+ events: events2
48520
+ };
48521
+ }
47657
48522
  function parseSince(url2) {
47658
48523
  const since = Number.parseInt(url2.searchParams.get("since") ?? "", 10);
47659
48524
  if (!Number.isFinite(since) || since <= 0) {
@@ -47683,7 +48548,7 @@ async function probeExistingBroker() {
47683
48548
  }
47684
48549
  }
47685
48550
  async function prepareBrokerSocketPath(socketPath) {
47686
- await mkdir8(dirname15(socketPath), { recursive: true, mode: 448 });
48551
+ await mkdir8(dirname16(socketPath), { recursive: true, mode: 448 });
47687
48552
  try {
47688
48553
  const existing = await lstat(socketPath);
47689
48554
  if (!existing.isSocket()) {
@@ -48025,6 +48890,15 @@ async function routeRequest(request, response) {
48025
48890
  json2(response, 200, await getHarnessTopologySnapshot(url2.searchParams.get("force") === "1"));
48026
48891
  return;
48027
48892
  }
48893
+ if (method === "GET" && url2.pathname === "/v1/tail/discover") {
48894
+ const force = url2.searchParams.get("force") === "1" || url2.searchParams.get("force") === "true";
48895
+ json2(response, 200, await getTailDiscovery(force));
48896
+ return;
48897
+ }
48898
+ if (method === "GET" && url2.pathname === "/v1/tail/recent") {
48899
+ json2(response, 200, await readTailRecentPayload(url2));
48900
+ return;
48901
+ }
48028
48902
  if (method === "POST" && url2.pathname === "/v1/topology/nudge") {
48029
48903
  json2(response, 200, await nudgeHarnessTopologyScan());
48030
48904
  return;
@@ -48703,10 +49577,10 @@ data: ${JSON.stringify({ nodeId, meshId })}
48703
49577
  const externalSessionId = sessionResult.externalSessionId?.trim();
48704
49578
  const nextEndpoint = {
48705
49579
  ...endpoint,
48706
- state: endpoint.state === "offline" ? "waiting" : endpoint.state,
49580
+ state: endpointStateAfterSuccessfulSessionWarmup(endpoint.state),
48707
49581
  ...externalSessionId ? { sessionId: externalSessionId } : {},
48708
49582
  metadata: {
48709
- ...endpoint.metadata ?? {},
49583
+ ...clearEndpointFailureMetadata(endpoint.metadata),
48710
49584
  ...externalSessionId ? {
48711
49585
  externalSessionId,
48712
49586
  threadId: endpoint.transport === "codex_app_server" ? externalSessionId : endpoint.metadata?.threadId
@@ -49066,16 +49940,22 @@ function supportedRouteHarness(value) {
49066
49940
  }
49067
49941
  return;
49068
49942
  }
49943
+ function supportedRouteModel(value) {
49944
+ const normalized = value?.trim();
49945
+ return normalized || undefined;
49946
+ }
49069
49947
  function executionWithRouteParams(payload) {
49070
49948
  const label = agentLabelForRouteParams(payload);
49071
49949
  const identity2 = label ? parseAgentIdentity(label.startsWith("@") ? label : `@${label}`) : null;
49072
- const harness = supportedRouteHarness(identity2?.harness);
49073
- if (!harness || payload.execution?.harness) {
49950
+ const harness = payload.execution?.harness ? undefined : supportedRouteHarness(identity2?.harness);
49951
+ const model = payload.execution?.model ? undefined : supportedRouteModel(identity2?.model);
49952
+ if (!harness && !model) {
49074
49953
  return payload.execution;
49075
49954
  }
49076
49955
  return {
49077
49956
  ...payload.execution ?? {},
49078
- harness
49957
+ ...harness ? { harness } : {},
49958
+ ...model ? { model } : {}
49079
49959
  };
49080
49960
  }
49081
49961
  function resolveBrokerDeliveryTarget(input) {
@@ -49088,43 +49968,82 @@ function projectPathRouteTarget(input) {
49088
49968
  return input.target?.kind === "project_path" ? input.target.projectPath.trim() || undefined : undefined;
49089
49969
  }
49090
49970
  function implicitProjectAgentName(projectPath) {
49091
- const base = normalizeAgentSelectorSegment(basename11(projectPath)) || "agent";
49971
+ const base = normalizeAgentSelectorSegment(basename12(projectPath)) || "agent";
49092
49972
  return `${base}-card-${createRuntimeId2("one").slice(-8)}`;
49093
49973
  }
49094
- async function resolveBrokerDeliveryTargetWithImplicitProjectCard(input, options) {
49974
+ function projectAgentPersistence(spec) {
49975
+ return spec?.persistence === "sticky" ? "sticky" : "one_time";
49976
+ }
49977
+ function compactProjectAgentName(value) {
49978
+ const normalized = value?.trim();
49979
+ return normalized || undefined;
49980
+ }
49981
+ function shouldMaterializeProjectAgent(input) {
49982
+ if (!input.projectPath) {
49983
+ return false;
49984
+ }
49985
+ if (input.projectAgent?.persistence === "one_time") {
49986
+ return true;
49987
+ }
49988
+ if (input.projectAgent?.persistence === "sticky") {
49989
+ return input.resolved.kind !== "resolved" || Boolean(input.projectAgent.agentName?.trim()) || Boolean(input.projectAgent.displayName?.trim()) || Boolean(input.execution?.harness) || Boolean(input.execution?.model?.trim()) || Boolean(input.execution?.permissionProfile);
49990
+ }
49991
+ return input.resolved.kind === "unknown" || input.resolved.kind === "ambiguous" && (input.execution?.session ?? "new") === "new";
49992
+ }
49993
+ async function resolveBrokerDeliveryTargetWithImplicitProjectAgent(input, options) {
49095
49994
  const resolved = resolveBrokerDeliveryTarget(input);
49096
49995
  const projectPath = projectPathRouteTarget(input);
49097
- const shouldCreateImplicitProjectCard = projectPath && (resolved.kind === "unknown" || resolved.kind === "ambiguous" && (input.execution?.session ?? "new") === "new");
49098
- if (!shouldCreateImplicitProjectCard) {
49996
+ const persistence = projectAgentPersistence(input.projectAgent);
49997
+ if (!shouldMaterializeProjectAgent({
49998
+ projectPath,
49999
+ resolved,
50000
+ persistence,
50001
+ projectAgent: input.projectAgent,
50002
+ execution: input.execution
50003
+ })) {
50004
+ return resolved;
50005
+ }
50006
+ if (!projectPath) {
49099
50007
  return resolved;
49100
50008
  }
49101
50009
  const createdAt = Date.now();
49102
50010
  const requesterId = options.requesterId?.trim();
50011
+ const agentName = persistence === "sticky" ? compactProjectAgentName(input.projectAgent?.agentName) : implicitProjectAgentName(projectPath);
50012
+ const displayName = compactProjectAgentName(input.projectAgent?.displayName);
49103
50013
  const status = await startLocalAgent({
49104
50014
  projectPath,
49105
- agentName: implicitProjectAgentName(projectPath),
50015
+ ...agentName ? { agentName } : {},
50016
+ ...displayName ? { displayName } : {},
49106
50017
  currentDirectory: options.currentDirectory ?? projectPath,
49107
50018
  harness: input.execution?.harness,
50019
+ model: input.execution?.model,
50020
+ permissionProfile: input.execution?.permissionProfile,
49108
50021
  ensureOnline: false,
49109
50022
  card: {
49110
- kind: "one_time",
50023
+ kind: persistence === "sticky" ? "persistent" : "one_time",
49111
50024
  createdAt,
49112
50025
  ...requesterId ? { createdById: requesterId } : {},
49113
- expiresAt: createdAt + DEFAULT_IMPLICIT_PROJECT_CARD_TTL_MS,
49114
- maxUses: 1
50026
+ ...persistence === "one_time" ? {
50027
+ expiresAt: createdAt + DEFAULT_IMPLICIT_PROJECT_CARD_TTL_MS,
50028
+ maxUses: 1
50029
+ } : {}
49115
50030
  }
49116
50031
  }).catch((error48) => {
49117
50032
  const detail = error48 instanceof Error ? error48.message : String(error48);
49118
- throw new Error(`could not create an agent card for project ${projectPath}: ${detail}`);
49119
- });
49120
- await pruneOneTimeLocalAgentCards({
49121
- ...requesterId ? { createdById: requesterId } : {},
49122
- projectRoot: status.projectRoot,
49123
- excludeAgentIds: [status.agentId]
49124
- }).catch((error48) => {
49125
- console.warn(`[openscout-runtime] implicit project card cleanup failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
50033
+ throw new Error(`could not create a ${persistence} agent for project ${projectPath}: ${detail}`);
49126
50034
  });
49127
- await syncRegisteredLocalAgentsIfChanged(options.reason);
50035
+ if (persistence === "one_time") {
50036
+ await pruneOneTimeLocalAgentCards({
50037
+ ...requesterId ? { createdById: requesterId } : {},
50038
+ projectRoot: status.projectRoot,
50039
+ excludeAgentIds: [status.agentId]
50040
+ }).catch((error48) => {
50041
+ console.warn(`[openscout-runtime] implicit project card cleanup failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
50042
+ });
50043
+ }
50044
+ clearGitBranchCache();
50045
+ console.log(`[openscout-runtime] local agent registry changed (${options.reason}); refreshing registered agents`);
50046
+ await syncRegisteredLocalAgents();
49128
50047
  const agent = runtime.snapshot().agents[status.agentId];
49129
50048
  if (agent && !isInactiveLocalAgent(agent)) {
49130
50049
  return { kind: "resolved", agent };
@@ -49132,13 +50051,14 @@ async function resolveBrokerDeliveryTargetWithImplicitProjectCard(input, options
49132
50051
  return resolveBrokerDeliveryTarget(input);
49133
50052
  }
49134
50053
  async function resolveInvocationTarget(payload) {
49135
- return resolveBrokerDeliveryTargetWithImplicitProjectCard({
50054
+ return resolveBrokerDeliveryTargetWithImplicitProjectAgent({
49136
50055
  target: payload.target,
49137
50056
  targetAgentId: payload.targetAgentId,
49138
50057
  targetSessionId: payload.targetSessionId,
49139
50058
  targetLabel: payload.targetLabel,
49140
50059
  routePolicy: payload.routePolicy,
49141
- execution: payload.execution
50060
+ execution: payload.execution,
50061
+ projectAgent: undefined
49142
50062
  }, {
49143
50063
  requesterId: payload.requesterId,
49144
50064
  currentDirectory: projectPathRouteTarget(payload),
@@ -49156,7 +50076,7 @@ function remediationForDispatch(dispatch) {
49156
50076
  }
49157
50077
  if (dispatch.kind === "unavailable") {
49158
50078
  return {
49159
- kind: dispatch.target?.reason === "manual_wake_required" ? "wake_target" : "retry_later",
50079
+ kind: dispatch.target?.reason === "manual_wake_required" ? "wake_target" : dispatch.target?.reason === "stale_registration" ? "stale_reference" : "retry_later",
49160
50080
  detail: dispatch.target?.detail ?? dispatch.detail,
49161
50081
  targetAgentId: dispatch.target?.agentId,
49162
50082
  targetLabel: dispatch.askedLabel,
@@ -49494,7 +50414,7 @@ async function acceptBrokerDelivery(payload) {
49494
50414
  const askedLabel = askedLabelForRouteTarget(payload);
49495
50415
  const execution = executionWithRouteParams(payload);
49496
50416
  const deliveryChannel = routeChannelForTarget(payload) ?? payload.channel?.trim();
49497
- const targetSessionId = payload.target?.kind === "session_id" ? payload.target.sessionId.trim() : payload.targetSessionId?.trim() || metadataStringValue2(payload.invocationMetadata, "targetSessionId") || metadataStringValue2(payload.messageMetadata, "targetSessionId") || undefined;
50417
+ const targetSessionId = payload.target?.kind === "session_id" ? payload.target.sessionId.trim() : payload.targetSessionId?.trim() || payload.execution?.targetSessionId?.trim() || metadataStringValue2(payload.invocationMetadata, "targetSessionId") || metadataStringValue2(payload.messageMetadata, "targetSessionId") || undefined;
49498
50418
  const replyToSessionId = payload.replyToSessionId?.trim() || metadataStringValue2(payload.invocationMetadata, "replyToSessionId") || metadataStringValue2(payload.messageMetadata, "replyToSessionId") || undefined;
49499
50419
  const labels = normalizeScoutLabels(payload.labels);
49500
50420
  const typedChannelTarget = payload.target?.kind === "channel" || payload.target?.kind === "broadcast";
@@ -49748,7 +50668,7 @@ async function acceptBrokerDelivery(payload) {
49748
50668
  message: message2
49749
50669
  };
49750
50670
  }
49751
- const resolved = await resolveBrokerDeliveryTargetWithImplicitProjectCard({
50671
+ const resolved = await resolveBrokerDeliveryTargetWithImplicitProjectAgent({
49752
50672
  ...payload,
49753
50673
  execution
49754
50674
  }, {
@@ -49776,7 +50696,7 @@ async function acceptBrokerDelivery(payload) {
49776
50696
  remediation: remediationForDispatch(record2)
49777
50697
  };
49778
50698
  }
49779
- const unavailable = describeUnavailableDeliveryTarget(runtime.snapshot(), resolved.agent);
50699
+ const unavailable = describeUnavailableDeliveryTarget(runtime.snapshot(), resolved.agent, targetSessionId);
49780
50700
  if (unavailable) {
49781
50701
  const { record: record2 } = await recordScoutDispatchDurably(buildUnavailableDispatchEnvelope(askedLabel || brokerTargetLabel(resolved.agent), unavailable), {
49782
50702
  requesterId
@@ -49881,9 +50801,11 @@ async function acceptBrokerDelivery(payload) {
49881
50801
  ...targetSessionId ? { targetSessionId } : {},
49882
50802
  ...payload.intent === "tell" && payload.invocationMetadata?.sourceIntent === undefined ? { sourceIntent: "direct_message" } : {}
49883
50803
  };
50804
+ const baseInvocationExecution = execution ?? {};
49884
50805
  const invocationExecution = {
49885
- ...execution ?? (payload.intent === "tell" ? { session: "any" } : {}),
49886
- ...targetSessionId ? { session: "existing", targetSessionId } : {}
50806
+ ...baseInvocationExecution,
50807
+ session: targetSessionId ? "existing" : "new",
50808
+ ...targetSessionId ? { targetSessionId } : {}
49887
50809
  };
49888
50810
  const invocation = {
49889
50811
  id: createRuntimeId2("inv"),