@openscout/scout 0.2.68 → 0.2.69

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.
@@ -15491,7 +15491,13 @@ import { execFile } from "child_process";
15491
15491
  import { promisify } from "util";
15492
15492
  var execFileAsync = promisify(execFile);
15493
15493
  var PARENT_HOP_LIMIT = 10;
15494
- async function listProcesses() {
15494
+ var PROCESS_LIST_CACHE_MS = readNonNegativeIntEnv("OPENSCOUT_TAIL_PROCESS_CACHE_MS", 1000);
15495
+ var processListCache = null;
15496
+ function readNonNegativeIntEnv(name, fallback) {
15497
+ const raw = Number.parseInt(process.env[name] ?? "", 10);
15498
+ return Number.isFinite(raw) && raw >= 0 ? raw : fallback;
15499
+ }
15500
+ async function readProcessListUncached() {
15495
15501
  const { stdout } = await execFileAsync("ps", ["-axww", "-o", "pid=,ppid=,etime=,command="], {
15496
15502
  maxBuffer: 32 * 1024 * 1024
15497
15503
  });
@@ -15513,6 +15519,24 @@ async function listProcesses() {
15513
15519
  }
15514
15520
  return out;
15515
15521
  }
15522
+ async function listProcesses() {
15523
+ const now = Date.now();
15524
+ if (processListCache && now < processListCache.expiresAt) {
15525
+ return processListCache.promise;
15526
+ }
15527
+ let promise2 = readProcessListUncached();
15528
+ promise2 = promise2.catch((error48) => {
15529
+ if (processListCache?.promise === promise2) {
15530
+ processListCache = null;
15531
+ }
15532
+ throw error48;
15533
+ });
15534
+ processListCache = {
15535
+ expiresAt: now + PROCESS_LIST_CACHE_MS,
15536
+ promise: promise2
15537
+ };
15538
+ return promise2;
15539
+ }
15516
15540
  function commandBasename(command) {
15517
15541
  const firstToken = command.split(/\s+/)[0] ?? "";
15518
15542
  const slashIdx = firstToken.lastIndexOf("/");
@@ -15608,6 +15632,19 @@ var DEFAULT_HOT_DISCOVERY_LIMIT = 64;
15608
15632
  var DEFAULT_SHALLOW_DISCOVERY_LIMIT = 160;
15609
15633
  var DEFAULT_DEEP_DISCOVERY_LIMIT = 160;
15610
15634
  var HEAD_READ_BYTES = 256 * 1024;
15635
+ var METADATA_CACHE_LIMIT = 512;
15636
+ var metadataCache = new Map;
15637
+ function rememberMetadata(file2, metadata) {
15638
+ metadataCache.delete(file2.path);
15639
+ metadataCache.set(file2.path, { ...file2, ...metadata });
15640
+ while (metadataCache.size > METADATA_CACHE_LIMIT) {
15641
+ const oldest = metadataCache.keys().next().value;
15642
+ if (!oldest)
15643
+ break;
15644
+ metadataCache.delete(oldest);
15645
+ }
15646
+ return metadata;
15647
+ }
15611
15648
  function projectsRoot() {
15612
15649
  return process.env.OPENSCOUT_TAIL_CLAUDE_PROJECTS_ROOT ?? join(homedir(), ".claude", "projects");
15613
15650
  }
@@ -15654,8 +15691,12 @@ function parseJsonRecord(line) {
15654
15691
  return null;
15655
15692
  }
15656
15693
  }
15657
- function readClaudeMetadata(filePath) {
15658
- const head = readFileHead(filePath);
15694
+ function readClaudeMetadata(file2) {
15695
+ const cached2 = metadataCache.get(file2.path);
15696
+ if (cached2 && cached2.mtimeMs === file2.mtimeMs && cached2.size === file2.size) {
15697
+ return { cwd: cached2.cwd, sessionId: cached2.sessionId };
15698
+ }
15699
+ const head = readFileHead(file2.path);
15659
15700
  let cwd = null;
15660
15701
  let sessionId = null;
15661
15702
  for (const line of head.split(/\r?\n/)) {
@@ -15665,10 +15706,10 @@ function readClaudeMetadata(filePath) {
15665
15706
  cwd ??= typeof record2.cwd === "string" && record2.cwd.trim() ? record2.cwd : null;
15666
15707
  sessionId ??= typeof record2.sessionId === "string" && record2.sessionId.trim() ? record2.sessionId : typeof record2.session_id === "string" && record2.session_id.trim() ? record2.session_id : null;
15667
15708
  if (cwd && sessionId) {
15668
- return { cwd, sessionId };
15709
+ return rememberMetadata(file2, { cwd, sessionId });
15669
15710
  }
15670
15711
  }
15671
- return { cwd, sessionId };
15712
+ return rememberMetadata(file2, { cwd, sessionId });
15672
15713
  }
15673
15714
  function decodeProjectDir(dirName) {
15674
15715
  if (!dirName.startsWith("-"))
@@ -15875,7 +15916,7 @@ var ClaudeSource = {
15875
15916
  if (!existsSync(root))
15876
15917
  return [];
15877
15918
  return walkRecentJsonlFiles(root, scope).map((file2) => {
15878
- const meta3 = readClaudeMetadata(file2.path);
15919
+ const meta3 = readClaudeMetadata(file2);
15879
15920
  const cwd = meta3.cwd ?? fallbackCwdForPath(file2.path);
15880
15921
  const sessionId = meta3.sessionId ?? basename(file2.path).replace(/\.jsonl$/, "");
15881
15922
  return {
@@ -15908,6 +15949,19 @@ var DEFAULT_HOT_DISCOVERY_LIMIT2 = 64;
15908
15949
  var DEFAULT_SHALLOW_DISCOVERY_LIMIT2 = 160;
15909
15950
  var DEFAULT_DEEP_DISCOVERY_LIMIT2 = 160;
15910
15951
  var HEAD_READ_BYTES2 = 512 * 1024;
15952
+ var METADATA_CACHE_LIMIT2 = 512;
15953
+ var metadataCache2 = new Map;
15954
+ function rememberMetadata2(file2, metadata) {
15955
+ metadataCache2.delete(file2.path);
15956
+ metadataCache2.set(file2.path, { ...file2, ...metadata });
15957
+ while (metadataCache2.size > METADATA_CACHE_LIMIT2) {
15958
+ const oldest = metadataCache2.keys().next().value;
15959
+ if (!oldest)
15960
+ break;
15961
+ metadataCache2.delete(oldest);
15962
+ }
15963
+ return metadata;
15964
+ }
15911
15965
  function readPositiveIntEnv2(names, fallback) {
15912
15966
  for (const name of names) {
15913
15967
  const raw = Number.parseInt(process.env[name] ?? "", 10);
@@ -15991,8 +16045,12 @@ function parseJsonRecord2(line) {
15991
16045
  function metadataRecord(value) {
15992
16046
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15993
16047
  }
15994
- function readCodexMetadata(filePath) {
15995
- const head = readFileHead2(filePath);
16048
+ function readCodexMetadata(file2) {
16049
+ const cached2 = metadataCache2.get(file2.path);
16050
+ if (cached2 && cached2.mtimeMs === file2.mtimeMs && cached2.size === file2.size) {
16051
+ return { cwd: cached2.cwd, sessionId: cached2.sessionId };
16052
+ }
16053
+ const head = readFileHead2(file2.path);
15996
16054
  let cwd = null;
15997
16055
  let sessionId = null;
15998
16056
  for (const line of head.split(/\r?\n/)) {
@@ -16006,12 +16064,13 @@ function readCodexMetadata(filePath) {
16006
16064
  cwd ??= typeof payload.cwd === "string" && payload.cwd.trim() ? payload.cwd : null;
16007
16065
  sessionId ??= typeof payload.id === "string" && payload.id.trim() ? payload.id : null;
16008
16066
  if (cwd || sessionId) {
16009
- if (cwd && sessionId)
16010
- return { cwd, sessionId };
16067
+ if (cwd && sessionId) {
16068
+ return rememberMetadata2(file2, { cwd, sessionId });
16069
+ }
16011
16070
  }
16012
16071
  }
16013
16072
  }
16014
- return { cwd, sessionId };
16073
+ return rememberMetadata2(file2, { cwd, sessionId });
16015
16074
  }
16016
16075
  function walkRecentJsonlFiles2(root, scope) {
16017
16076
  const cutoff = Date.now() - discoveryWindowMs2(scope);
@@ -16054,7 +16113,7 @@ function discoverCodexTranscripts(scope) {
16054
16113
  }
16055
16114
  }
16056
16115
  return [...byPath.values()].sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, discoveryLimit2(scope)).map((file2) => {
16057
- const meta3 = readCodexMetadata(file2.path);
16116
+ const meta3 = readCodexMetadata(file2);
16058
16117
  const sessionId = meta3.sessionId ?? sessionIdFromCodexPath(file2.path);
16059
16118
  return {
16060
16119
  source: SOURCE_NAME2,
@@ -16261,6 +16320,7 @@ var RAW_MAX_STRING_LEN = 1000;
16261
16320
  var RAW_MAX_ARRAY_ITEMS = 25;
16262
16321
  var RAW_MAX_OBJECT_KEYS = 50;
16263
16322
  var RECENT_TRANSCRIPT_READ_BYTES = 512 * 1024;
16323
+ var RECENT_TRANSCRIPT_MAX_FILES = readPositiveIntEnv3("OPENSCOUT_TAIL_RECENT_TRANSCRIPT_MAX_FILES", 24);
16264
16324
  var sources = [ClaudeSource, CodexSource];
16265
16325
  var watchers = new Map;
16266
16326
  var aggregateBuffer = [];
@@ -16397,26 +16457,27 @@ function pushEvent(rawEvent) {
16397
16457
  } catch {}
16398
16458
  }
16399
16459
  }
16400
- async function readNew(handle, fromOffset) {
16401
- const stats = await handle.stat();
16402
- if (stats.size <= fromOffset) {
16460
+ async function readNew(handle, fromOffset, fileSize) {
16461
+ if (fileSize <= fromOffset) {
16403
16462
  return { text: "", nextOffset: fromOffset };
16404
16463
  }
16405
- const length = stats.size - fromOffset;
16464
+ const length = fileSize - fromOffset;
16406
16465
  const buffer = Buffer.alloc(length);
16407
16466
  await handle.read(buffer, 0, length, fromOffset);
16408
- return { text: buffer.toString("utf8"), nextOffset: stats.size };
16467
+ return { text: buffer.toString("utf8"), nextOffset: fileSize };
16409
16468
  }
16410
16469
  async function pumpWatcher(watcher) {
16411
16470
  let handle = null;
16412
16471
  try {
16413
- handle = await open(watcher.transcriptPath, "r");
16414
- const stats = await handle.stat();
16472
+ const stats = await stat(watcher.transcriptPath);
16415
16473
  if (stats.size < watcher.offset) {
16416
16474
  watcher.offset = 0;
16417
16475
  watcher.carry = "";
16418
16476
  }
16419
- const { text, nextOffset } = await readNew(handle, watcher.offset);
16477
+ if (stats.size <= watcher.offset)
16478
+ return;
16479
+ handle = await open(watcher.transcriptPath, "r");
16480
+ const { text, nextOffset } = await readNew(handle, watcher.offset, stats.size);
16420
16481
  watcher.offset = nextOffset;
16421
16482
  if (!text)
16422
16483
  return;
@@ -18633,6 +18694,276 @@ function assertValidUnblockRequestEvent(event, record2) {
18633
18694
  throw new Error(errors3.join("; "));
18634
18695
  }
18635
18696
  }
18697
+ // packages/protocol/src/lifecycle.ts
18698
+ var TERMINAL_INVOCATION_STATES = new Set([
18699
+ "completed",
18700
+ "failed",
18701
+ "cancelled",
18702
+ "expired"
18703
+ ]);
18704
+ var MAX_TERMINAL_SUMMARY_LENGTH = 256;
18705
+ function projectInvocationLifecycle(input) {
18706
+ const deliveries = (input.deliveries ?? []).map((delivery) => projectOutcomeDelivery(delivery, input.deliveryAttempts?.[delivery.id] ?? []));
18707
+ const peerDelivery = deliveries.find((delivery) => delivery.state === "dispatched_to_peer");
18708
+ const expiresAt = input.expiresAt ?? expiresAtForInvocation(input.invocation);
18709
+ const state = projectInvocationState({
18710
+ invocation: input.invocation,
18711
+ flight: input.flight,
18712
+ deliveries,
18713
+ now: input.now,
18714
+ expiresAt
18715
+ });
18716
+ const terminal = projectTerminalResult({
18717
+ state,
18718
+ flight: input.flight,
18719
+ now: input.now
18720
+ });
18721
+ const lifecycle = {
18722
+ invocationId: input.invocation.id,
18723
+ state
18724
+ };
18725
+ addOptional(lifecycle, "flightId", input.flight?.id);
18726
+ addOptional(lifecycle, "targetAgentId", input.flight?.targetAgentId ?? input.invocation.targetAgentId);
18727
+ addOptional(lifecycle, "targetEndpointId", stringMetadata(input.flight?.metadata, "targetEndpointId"));
18728
+ addOptional(lifecycle, "peerNodeId", peerDelivery?.peerNodeId ?? input.invocation.targetNodeId);
18729
+ addOptional(lifecycle, "peerFlightId", peerDelivery?.peerFlightId);
18730
+ addOptional(lifecycle, "workId", inferWorkId(input.invocation));
18731
+ addOptional(lifecycle, "actionId", stringMetadata(input.invocation.metadata, "actionId"));
18732
+ addOptional(lifecycle, "idempotencyKey", stringMetadata(input.invocation.metadata, "idempotencyKey"));
18733
+ addOptional(lifecycle, "acknowledgedAt", dispatchAcknowledgedAt(input.flight?.metadata) ?? peerDelivery?.deliveredAt);
18734
+ addOptional(lifecycle, "startedAt", input.flight?.startedAt);
18735
+ addOptional(lifecycle, "completedAt", input.flight?.completedAt ?? terminal?.completedAt);
18736
+ addOptional(lifecycle, "expiresAt", expiresAt);
18737
+ addOptional(lifecycle, "lastProgressAt", lastProgressAt(input.flight, deliveries));
18738
+ addOptional(lifecycle, "waitingOn", input.waitingOn ?? waitingOnFromMetadata(input.flight?.metadata));
18739
+ addOptional(lifecycle, "terminal", terminal);
18740
+ addOptional(lifecycle, "deliveries", deliveries.length > 0 ? deliveries : undefined);
18741
+ addOptional(lifecycle, "metadata", lifecycleMetadata(input.invocation, input.flight));
18742
+ return lifecycle;
18743
+ }
18744
+ function projectInvocationState(input) {
18745
+ const flightState = input.flight?.state;
18746
+ if (flightState === "completed" || flightState === "failed" || flightState === "cancelled") {
18747
+ return flightState;
18748
+ }
18749
+ if (input.expiresAt !== undefined && input.now !== undefined && input.expiresAt <= input.now) {
18750
+ return "expired";
18751
+ }
18752
+ if (input.deliveries?.some((delivery) => delivery.state === "dispatched_to_peer")) {
18753
+ return "acknowledged";
18754
+ }
18755
+ if (flightState === "waiting") {
18756
+ return "waiting";
18757
+ }
18758
+ if (flightState === "running") {
18759
+ return "working";
18760
+ }
18761
+ if (flightState === "waking") {
18762
+ return "dispatching";
18763
+ }
18764
+ if (flightState === "queued") {
18765
+ return "queued";
18766
+ }
18767
+ return input.invocation.ensureAwake ? "dispatching" : "queued";
18768
+ }
18769
+ function projectOutcomeDelivery(delivery, attempts = []) {
18770
+ const sortedAttempts = [...attempts].sort((left, right) => left.createdAt - right.createdAt);
18771
+ const lastAttempt = sortedAttempts[sortedAttempts.length - 1];
18772
+ const latestFailedAttempt = [...sortedAttempts].reverse().find((attempt) => attempt.status === "failed");
18773
+ const state = projectDeliveryState(delivery);
18774
+ const deliveredAt = deliveredAtForDelivery(delivery, sortedAttempts);
18775
+ const subject = deliverySubject(delivery);
18776
+ const outcome = {
18777
+ deliveryId: delivery.id,
18778
+ subjectKind: subject.kind,
18779
+ subjectId: subject.id,
18780
+ transport: delivery.transport,
18781
+ state,
18782
+ attemptCount: sortedAttempts.length
18783
+ };
18784
+ addOptional(outcome, "peerNodeId", peerNodeId(delivery));
18785
+ addOptional(outcome, "peerFlightId", stringMetadata(delivery.metadata, "peerFlightId"));
18786
+ addOptional(outcome, "nextAttemptAt", numberMetadata(delivery.metadata, "nextAttemptAt"));
18787
+ addOptional(outcome, "lastError", deliveryError(delivery, latestFailedAttempt));
18788
+ addOptional(outcome, "lastAttemptAt", lastAttempt?.createdAt);
18789
+ addOptional(outcome, "deliveredAt", deliveredAt);
18790
+ addOptional(outcome, "metadata", delivery.metadata);
18791
+ return outcome;
18792
+ }
18793
+ function projectDeliveryState(delivery) {
18794
+ switch (delivery.status) {
18795
+ case "accepted":
18796
+ case "pending":
18797
+ return "pending";
18798
+ case "leased":
18799
+ return "leased";
18800
+ case "peer_acked":
18801
+ return "dispatched_to_peer";
18802
+ case "sent":
18803
+ case "acknowledged":
18804
+ case "running":
18805
+ case "completed":
18806
+ return "sent";
18807
+ case "deferred":
18808
+ return "retrying";
18809
+ case "failed":
18810
+ return "dead_lettered";
18811
+ case "cancelled":
18812
+ return isSuppressedDelivery(delivery) ? "suppressed" : "cancelled";
18813
+ default:
18814
+ return "pending";
18815
+ }
18816
+ }
18817
+ function isTerminalInvocationState(state) {
18818
+ return TERMINAL_INVOCATION_STATES.has(state);
18819
+ }
18820
+ function projectTerminalResult(input) {
18821
+ if (!isTerminalInvocationState(input.state)) {
18822
+ return;
18823
+ }
18824
+ const state = input.state;
18825
+ const completedAt = input.flight?.completedAt ?? input.now;
18826
+ if (completedAt === undefined) {
18827
+ return;
18828
+ }
18829
+ const terminal = {
18830
+ state,
18831
+ completedAt
18832
+ };
18833
+ addOptional(terminal, "summary", compactTerminalSummary(input.flight));
18834
+ addOptional(terminal, "errorClass", stringMetadata(input.flight?.metadata, "errorClass"));
18835
+ addOptional(terminal, "exitCode", numberMetadata(input.flight?.metadata, "exitCode"));
18836
+ addOptional(terminal, "sourceRecordId", input.flight?.id);
18837
+ addOptional(terminal, "metadata", terminalMetadata(input.flight));
18838
+ return terminal;
18839
+ }
18840
+ function compactTerminalSummary(flight) {
18841
+ const summary = flight?.state === "failed" ? flight.error ?? flight.summary : flight?.summary;
18842
+ if (!summary?.trim()) {
18843
+ return;
18844
+ }
18845
+ const compacted = summary.replace(/\s+/g, " ").trim();
18846
+ return compacted.length <= MAX_TERMINAL_SUMMARY_LENGTH ? compacted : `${compacted.slice(0, MAX_TERMINAL_SUMMARY_LENGTH - 3).trimEnd()}...`;
18847
+ }
18848
+ function terminalMetadata(flight) {
18849
+ if (!flight?.metadata) {
18850
+ return;
18851
+ }
18852
+ const metadata = {};
18853
+ addOptional(metadata, "failureStage", stringMetadata(flight.metadata, "failureStage"));
18854
+ addOptional(metadata, "failureReason", stringMetadata(flight.metadata, "failureReason"));
18855
+ addOptional(metadata, "cancelledBy", stringMetadata(flight.metadata, "cancelledBy"));
18856
+ addOptional(metadata, "expiredAt", numberMetadata(flight.metadata, "expiredAt"));
18857
+ return Object.keys(metadata).length > 0 ? metadata : undefined;
18858
+ }
18859
+ function deliverySubject(delivery) {
18860
+ const workId = stringMetadata(delivery.metadata, "workId") ?? stringMetadata(delivery.metadata, "collaborationRecordId");
18861
+ if (workId) {
18862
+ return { kind: "work_item", id: workId };
18863
+ }
18864
+ if (delivery.invocationId) {
18865
+ return { kind: "invocation", id: delivery.invocationId };
18866
+ }
18867
+ if (delivery.messageId) {
18868
+ return { kind: "message", id: delivery.messageId };
18869
+ }
18870
+ return { kind: "message", id: delivery.id };
18871
+ }
18872
+ function deliveredAtForDelivery(delivery, attempts) {
18873
+ return numberMetadata(delivery.metadata, "deliveredAt") ?? numberMetadata(delivery.metadata, "peerAckedAt") ?? lastSuccessfulAttemptAt(attempts);
18874
+ }
18875
+ function lastSuccessfulAttemptAt(attempts) {
18876
+ for (let index = attempts.length - 1;index >= 0; index -= 1) {
18877
+ const attempt = attempts[index];
18878
+ if (attempt?.status === "acknowledged" || attempt?.status === "sent") {
18879
+ return attempt.createdAt;
18880
+ }
18881
+ }
18882
+ return;
18883
+ }
18884
+ function deliveryError(delivery, latestFailedAttempt) {
18885
+ const reason = stringMetadata(delivery.metadata, "failureReason");
18886
+ const detail = stringMetadata(delivery.metadata, "failureDetail") ?? stringMetadata(delivery.metadata, "lastError") ?? latestFailedAttempt?.error;
18887
+ const code = stringMetadata(delivery.metadata, "errorCode");
18888
+ const status = numberMetadata(delivery.metadata, "httpStatus");
18889
+ if (!reason && !detail && !code && status === undefined) {
18890
+ return;
18891
+ }
18892
+ const error48 = {};
18893
+ addOptional(error48, "reason", reason);
18894
+ addOptional(error48, "detail", detail);
18895
+ addOptional(error48, "retryable", booleanMetadata(delivery.metadata, "retryable"));
18896
+ addOptional(error48, "code", code);
18897
+ addOptional(error48, "status", status);
18898
+ return error48;
18899
+ }
18900
+ function isSuppressedDelivery(delivery) {
18901
+ return booleanMetadata(delivery.metadata, "suppressed") || booleanMetadata(delivery.metadata, "policySuppressed") || booleanMetadata(delivery.metadata, "suppressedByPolicy") || stringMetadata(delivery.metadata, "suppressionReason") !== undefined || stringMetadata(delivery.metadata, "failureReason") === "suppressed";
18902
+ }
18903
+ function peerNodeId(delivery) {
18904
+ return stringMetadata(delivery.metadata, "peerNodeId") ?? delivery.targetNodeId;
18905
+ }
18906
+ function dispatchAcknowledgedAt(metadata) {
18907
+ const dispatchAck = metadataObject(metadata, "dispatchAck");
18908
+ return numberMetadata(dispatchAck, "acknowledgedAt");
18909
+ }
18910
+ function lastProgressAt(flight, deliveries) {
18911
+ const deliveryAt = deliveries.map((delivery) => delivery.lastAttemptAt ?? delivery.deliveredAt).filter((value) => value !== undefined).sort((left, right) => right - left)[0];
18912
+ return Math.max(flight?.completedAt ?? 0, flight?.startedAt ?? 0, deliveryAt ?? 0) || undefined;
18913
+ }
18914
+ function lifecycleMetadata(invocation, flight) {
18915
+ const metadata = {};
18916
+ addOptional(metadata, "invocationMetadata", invocation.metadata);
18917
+ addOptional(metadata, "flightMetadata", flight?.metadata);
18918
+ return Object.keys(metadata).length > 0 ? metadata : undefined;
18919
+ }
18920
+ function waitingOnFromMetadata(metadata) {
18921
+ const waitingOn = metadataObject(metadata, "waitingOn");
18922
+ if (!waitingOn) {
18923
+ return;
18924
+ }
18925
+ const kind = stringMetadata(waitingOn, "kind");
18926
+ const projected = {
18927
+ kind: isWaitingOnKind(kind) ? kind : "unknown"
18928
+ };
18929
+ addOptional(projected, "label", stringMetadata(waitingOn, "label"));
18930
+ addOptional(projected, "targetId", stringMetadata(waitingOn, "targetId"));
18931
+ addOptional(projected, "metadata", metadataObject(waitingOn, "metadata"));
18932
+ return projected;
18933
+ }
18934
+ function isWaitingOnKind(value) {
18935
+ return value === "human" || value === "peer" || value === "approval" || value === "artifact" || value === "condition" || value === "agent" || value === "unknown";
18936
+ }
18937
+ function inferWorkId(invocation) {
18938
+ return stringMetadata(invocation.metadata, "workId") ?? stringMetadata(invocation.context, "workId") ?? stringMetadata(metadataObject(invocation.context, "collaboration"), "recordId");
18939
+ }
18940
+ function expiresAtForInvocation(invocation) {
18941
+ const metadataExpiresAt = numberMetadata(invocation.metadata, "expiresAt");
18942
+ if (metadataExpiresAt !== undefined) {
18943
+ return metadataExpiresAt;
18944
+ }
18945
+ return invocation.timeoutMs && Number.isFinite(invocation.timeoutMs) ? invocation.createdAt + invocation.timeoutMs : undefined;
18946
+ }
18947
+ function metadataObject(metadata, key) {
18948
+ const value = metadata?.[key];
18949
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
18950
+ }
18951
+ function stringMetadata(metadata, key) {
18952
+ const value = metadata?.[key];
18953
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
18954
+ }
18955
+ function numberMetadata(metadata, key) {
18956
+ const value = metadata?.[key];
18957
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
18958
+ }
18959
+ function booleanMetadata(metadata, key) {
18960
+ return metadata?.[key] === true;
18961
+ }
18962
+ function addOptional(target, key, value) {
18963
+ if (value !== undefined) {
18964
+ target[key] = value;
18965
+ }
18966
+ }
18636
18967
  // packages/protocol/src/permission-policy.ts
18637
18968
  var SCOUT_PERMISSION_PROFILES = [
18638
18969
  "observe",
@@ -19628,6 +19959,14 @@ class FileBackedBrokerJournal {
19628
19959
  getDurableAction(actionId) {
19629
19960
  return this.state.durableActions.get(actionId) ?? null;
19630
19961
  }
19962
+ getDurableActionByIdempotencyKey(input) {
19963
+ for (const action of this.state.durableActions.values()) {
19964
+ if (action.authorityCellId === input.authorityCellId && action.kind === input.kind && action.idempotencyKey === input.idempotencyKey) {
19965
+ return action;
19966
+ }
19967
+ }
19968
+ return null;
19969
+ }
19631
19970
  async rewriteEntries(entries) {
19632
19971
  await mkdir(dirname2(this.filePath), { recursive: true });
19633
19972
  const payload = entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(`
@@ -19820,12 +20159,12 @@ function normalizedRouteTargetValue(target) {
19820
20159
  if (!target) {
19821
20160
  return;
19822
20161
  }
19823
- const direct = target.kind === "agent_id" ? target.agentId : target.kind === "agent_label" ? target.label : target.kind === "binding_ref" ? target.ref : target.kind === "project_path" ? target.projectPath : target.kind === "channel" ? target.channel : target.value;
20162
+ const direct = target.kind === "agent_id" ? target.agentId : target.kind === "agent_label" ? target.label : target.kind === "session_id" ? target.sessionId : target.kind === "binding_ref" ? target.ref : target.kind === "project_path" ? target.projectPath : target.kind === "channel" ? target.channel : target.value;
19824
20163
  const trimmed = direct?.trim();
19825
20164
  return trimmed && trimmed.length > 0 ? trimmed : undefined;
19826
20165
  }
19827
20166
  function askedLabelForRouteTarget(input) {
19828
- return normalizedRouteTargetValue(input.target) ?? input.targetLabel?.trim() ?? input.targetAgentId?.trim() ?? "";
20167
+ return normalizedRouteTargetValue(input.target) ?? input.targetSessionId?.trim() ?? input.targetLabel?.trim() ?? input.targetAgentId?.trim() ?? "";
19829
20168
  }
19830
20169
  function routeChannelForTarget(input) {
19831
20170
  const target = input.target;
@@ -19844,20 +20183,6 @@ function metadataStringValue(metadata, key) {
19844
20183
  const value = metadata?.[key];
19845
20184
  return typeof value === "string" && value.length > 0 ? value : undefined;
19846
20185
  }
19847
- function replacementForStaleAgent(snapshot, agent, helpers) {
19848
- if (!helpers.isStale(agent)) {
19849
- return;
19850
- }
19851
- const replacementAgentId = metadataStringValue(agent.metadata, "replacedByAgentId");
19852
- if (!replacementAgentId || replacementAgentId === agent.id) {
19853
- return;
19854
- }
19855
- const replacement = snapshot.agents[replacementAgentId];
19856
- if (!replacement || helpers.isStale(replacement)) {
19857
- return;
19858
- }
19859
- return replacement;
19860
- }
19861
20186
  function isReservedProductIdentity(definitionId) {
19862
20187
  return definitionId === SCOUT_DISPATCHER_AGENT_ID || definitionId === OPENSCOUT_COORDINATOR_AGENT_ID;
19863
20188
  }
@@ -19898,6 +20223,31 @@ function buildAgentLabelCandidate(snapshot, agent) {
19898
20223
  aliases
19899
20224
  };
19900
20225
  }
20226
+ function stripHarnessQualifier(identity2) {
20227
+ if (!identity2.harness) {
20228
+ return null;
20229
+ }
20230
+ return constructAgentIdentity({
20231
+ definitionId: identity2.definitionId,
20232
+ workspaceQualifier: identity2.workspaceQualifier,
20233
+ profile: identity2.profile,
20234
+ model: identity2.model,
20235
+ nodeQualifier: identity2.nodeQualifier
20236
+ });
20237
+ }
20238
+ function resolutionFromDiagnosis(diagnosis, label) {
20239
+ if (diagnosis.kind === "resolved") {
20240
+ return { kind: "resolved", agent: diagnosis.match.agent };
20241
+ }
20242
+ if (diagnosis.kind === "ambiguous") {
20243
+ return {
20244
+ kind: "ambiguous",
20245
+ label,
20246
+ candidates: diagnosis.candidates.map((candidate) => candidate.agent)
20247
+ };
20248
+ }
20249
+ return null;
20250
+ }
19901
20251
  function resolveAgentLabel(snapshot, label, options) {
19902
20252
  const trimmed = label.trim();
19903
20253
  if (!trimmed) {
@@ -19912,10 +20262,17 @@ function resolveAgentLabel(snapshot, label, options) {
19912
20262
  }
19913
20263
  const candidates = buildAgentLabelCandidates(snapshot, options.helpers);
19914
20264
  const diagnosis = diagnoseAgentIdentity(identity2, candidates);
20265
+ const harnessAgnosticIdentity = stripHarnessQualifier(identity2);
19915
20266
  if (diagnosis.kind === "resolved") {
19916
20267
  return { kind: "resolved", agent: diagnosis.match.agent };
19917
20268
  }
19918
20269
  if (diagnosis.kind === "unknown") {
20270
+ if (harnessAgnosticIdentity) {
20271
+ const agnosticResolution = resolutionFromDiagnosis(diagnoseAgentIdentity(harnessAgnosticIdentity, candidates), identity2.label);
20272
+ if (agnosticResolution) {
20273
+ return agnosticResolution;
20274
+ }
20275
+ }
19919
20276
  const fallbackCandidates = buildAgentLabelCandidates(snapshot, options.helpers, {
19920
20277
  includeStale: true
19921
20278
  });
@@ -19923,7 +20280,7 @@ function resolveAgentLabel(snapshot, label, options) {
19923
20280
  if (fallbackDiagnosis.kind === "resolved") {
19924
20281
  return {
19925
20282
  kind: "resolved",
19926
- agent: replacementForStaleAgent(snapshot, fallbackDiagnosis.match.agent, options.helpers) ?? fallbackDiagnosis.match.agent
20283
+ agent: fallbackDiagnosis.match.agent
19927
20284
  };
19928
20285
  }
19929
20286
  if (fallbackDiagnosis.kind === "ambiguous") {
@@ -19933,6 +20290,12 @@ function resolveAgentLabel(snapshot, label, options) {
19933
20290
  candidates: fallbackDiagnosis.candidates.map((candidate) => candidate.agent)
19934
20291
  };
19935
20292
  }
20293
+ if (harnessAgnosticIdentity) {
20294
+ const agnosticFallbackResolution = resolutionFromDiagnosis(diagnoseAgentIdentity(harnessAgnosticIdentity, fallbackCandidates), identity2.label);
20295
+ if (agnosticFallbackResolution) {
20296
+ return agnosticFallbackResolution;
20297
+ }
20298
+ }
19936
20299
  return { kind: "unknown", label: identity2.label };
19937
20300
  }
19938
20301
  const preferLocal = options.preferLocalNodeId?.trim();
@@ -20002,19 +20365,37 @@ function resolveProjectPathTarget(snapshot, projectPath, options) {
20002
20365
  candidates: candidates.filter((candidate) => candidate.rank === first.rank).map((candidate) => candidate.agent)
20003
20366
  };
20004
20367
  }
20368
+ function endpointMatchesSessionId(endpoint, sessionId) {
20369
+ return endpoint.sessionId?.trim() === sessionId || endpoint.id === sessionId;
20370
+ }
20371
+ function resolveSessionTarget(snapshot, sessionId, options) {
20372
+ const candidates = Object.values(snapshot.endpoints).filter((endpoint) => endpointMatchesSessionId(endpoint, sessionId)).map((endpoint) => snapshot.agents[endpoint.agentId]).filter((agent) => Boolean(agent)).map((agent) => agent);
20373
+ const unique2 = [...new Map(candidates.map((agent) => [agent.id, agent])).values()];
20374
+ if (unique2.length === 0) {
20375
+ return { kind: "unknown", label: `session:${sessionId}` };
20376
+ }
20377
+ if (unique2.length === 1) {
20378
+ return { kind: "resolved", agent: unique2[0] };
20379
+ }
20380
+ return {
20381
+ kind: "ambiguous",
20382
+ label: `session:${sessionId}`,
20383
+ candidates: unique2
20384
+ };
20385
+ }
20005
20386
  function resolveBrokerRouteTarget(snapshot, input, options) {
20006
20387
  const policy = input.routePolicy;
20007
20388
  const routeTarget = input.target;
20008
20389
  const preferLocalNodeId = policy?.preferLocalNodeId?.trim() || options.preferLocalNodeId;
20009
20390
  const directId = routeTarget?.kind === "agent_id" ? normalizedRouteTargetValue(routeTarget) : input.targetAgentId?.trim();
20391
+ const directSessionId = routeTarget?.kind === "session_id" ? normalizedRouteTargetValue(routeTarget) : input.targetSessionId?.trim();
20392
+ if (directSessionId) {
20393
+ return resolveSessionTarget(snapshot, directSessionId, {
20394
+ helpers: options.helpers
20395
+ });
20396
+ }
20010
20397
  if (directId) {
20011
20398
  const agent = snapshot.agents[directId];
20012
- if (agent) {
20013
- const replacement = replacementForStaleAgent(snapshot, agent, options.helpers);
20014
- if (replacement) {
20015
- return { kind: "resolved", agent: replacement };
20016
- }
20017
- }
20018
20399
  if (agent && (policy?.allowStaleDirectId ?? true)) {
20019
20400
  return { kind: "resolved", agent };
20020
20401
  }
@@ -21351,11 +21732,29 @@ function createPeerDeliveryWorker(deps, config2 = {}) {
21351
21732
  // packages/runtime/src/local-agents.ts
21352
21733
  import { randomUUID as randomUUID2 } from "crypto";
21353
21734
  import { execFileSync as execFileSync3, execSync } from "child_process";
21354
- import { existsSync as existsSync13, readFileSync as readFileSync6 } from "fs";
21735
+ import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
21355
21736
  import { mkdir as mkdir7, rm as rm5, stat as stat4, writeFile as writeFile7 } from "fs/promises";
21356
- import { basename as basename9, dirname as dirname9, join as join18, resolve as resolve7 } from "path";
21737
+ import { basename as basename9, dirname as dirname10, join as join19, resolve as resolve7 } from "path";
21357
21738
  import { fileURLToPath as fileURLToPath5 } from "url";
21358
21739
 
21740
+ // packages/runtime/src/dispatch-stalled.ts
21741
+ class DispatchStalledError extends Error {
21742
+ code = "DISPATCH_STALLED";
21743
+ sessionName;
21744
+ paneTail;
21745
+ retries;
21746
+ constructor(input) {
21747
+ super(`tmux dispatch for session ${input.sessionName} left the prompt in the composer after submit + ${input.retries} retr${input.retries === 1 ? "y" : "ies"}.`);
21748
+ this.name = "DispatchStalledError";
21749
+ this.sessionName = input.sessionName;
21750
+ this.paneTail = input.paneTail;
21751
+ this.retries = input.retries;
21752
+ }
21753
+ }
21754
+ function isDispatchStalledError(error48) {
21755
+ return error48 instanceof DispatchStalledError || Boolean(error48) && typeof error48 === "object" && error48.code === "DISPATCH_STALLED" && typeof error48.sessionName === "string";
21756
+ }
21757
+
21359
21758
  // packages/runtime/src/claude-stream-json.ts
21360
21759
  import { randomUUID } from "crypto";
21361
21760
  import { spawn as spawn2 } from "child_process";
@@ -26574,6 +26973,63 @@ function readCodexAppServerModelFromLaunchArgs(launchArgs) {
26574
26973
  }
26575
26974
  return null;
26576
26975
  }
26976
+ function readCodexAppServerReasoningEffortFromLaunchArgs(launchArgs) {
26977
+ const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
26978
+ for (let index = 0;index < normalized.length; index += 1) {
26979
+ const current = normalized[index] ?? "";
26980
+ if (current === "-c" || current === "--config") {
26981
+ const reasoningEffort = parseCodexReasoningEffortConfig(normalized[index + 1]);
26982
+ if (reasoningEffort) {
26983
+ return reasoningEffort;
26984
+ }
26985
+ index += 1;
26986
+ continue;
26987
+ }
26988
+ if (current.startsWith("--config=")) {
26989
+ const reasoningEffort = parseCodexReasoningEffortConfig(current.slice("--config=".length));
26990
+ if (reasoningEffort) {
26991
+ return reasoningEffort;
26992
+ }
26993
+ }
26994
+ }
26995
+ return null;
26996
+ }
26997
+
26998
+ class CodexAppServerExitError extends Error {
26999
+ code = "CODEX_APP_SERVER_EXIT";
27000
+ exitKind;
27001
+ agentName;
27002
+ exitCode;
27003
+ signal;
27004
+ reason;
27005
+ noteworthy;
27006
+ constructor(input) {
27007
+ const exitKind = input.exitKind ?? (input.signal === "SIGTERM" ? "external_sigterm" : "unexpected_exit");
27008
+ super(codexAppServerExitMessage({
27009
+ ...input,
27010
+ exitKind
27011
+ }));
27012
+ this.name = "CodexAppServerExitError";
27013
+ this.exitKind = exitKind;
27014
+ this.agentName = input.agentName;
27015
+ this.exitCode = input.exitCode;
27016
+ this.signal = input.signal;
27017
+ this.reason = input.reason ?? null;
27018
+ this.noteworthy = exitKind !== "unexpected_exit";
27019
+ }
27020
+ }
27021
+ function isCodexAppServerExitError(error48) {
27022
+ return error48 instanceof CodexAppServerExitError || Boolean(error48 && typeof error48 === "object" && error48.code === "CODEX_APP_SERVER_EXIT");
27023
+ }
27024
+ function codexAppServerExitMessage(input) {
27025
+ if (input.exitKind === "proactive_shutdown") {
27026
+ return `Codex app-server session for ${input.agentName} was stopped by OpenScout` + (input.reason ? `: ${input.reason}` : "") + ".";
27027
+ }
27028
+ if (input.exitKind === "external_sigterm") {
27029
+ return `Codex app-server for ${input.agentName} was interrupted by SIGTERM.`;
27030
+ }
27031
+ return `Codex app-server exited for ${input.agentName}` + (input.exitCode !== null ? ` with code ${input.exitCode}` : "") + (input.signal ? ` (${input.signal})` : "");
27032
+ }
26577
27033
  function resolveRequesterTimeoutMs2(timeoutMs) {
26578
27034
  if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
26579
27035
  return timeoutMs;
@@ -26748,6 +27204,7 @@ class CodexAppServerSession {
26748
27204
  activeTurn = null;
26749
27205
  threadId = null;
26750
27206
  threadPath = null;
27207
+ proactiveShutdown = null;
26751
27208
  lastConfigSignature;
26752
27209
  constructor(options) {
26753
27210
  this.options = options;
@@ -26878,13 +27335,23 @@ class CodexAppServerSession {
26878
27335
  });
26879
27336
  }
26880
27337
  async shutdown(options = {}) {
27338
+ this.proactiveShutdown = {
27339
+ reason: options.reason ?? (options.resetThread ? "OpenScout reset the app-server session" : "OpenScout stopped the app-server session")
27340
+ };
27341
+ const stoppedError = new CodexAppServerExitError({
27342
+ agentName: this.options.agentName,
27343
+ exitKind: "proactive_shutdown",
27344
+ exitCode: null,
27345
+ signal: null,
27346
+ reason: this.proactiveShutdown.reason
27347
+ });
26881
27348
  const activeTurn = this.activeTurn;
26882
27349
  this.activeTurn = null;
26883
27350
  if (activeTurn) {
26884
- activeTurn.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
27351
+ activeTurn.reject(stoppedError);
26885
27352
  }
26886
27353
  for (const pending of this.pendingRequests.values()) {
26887
- pending.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
27354
+ pending.reject(stoppedError);
26888
27355
  }
26889
27356
  this.pendingRequests.clear();
26890
27357
  const child = this.process;
@@ -26894,7 +27361,7 @@ class CodexAppServerSession {
26894
27361
  if (child && child.exitCode === null && !child.killed) {
26895
27362
  child.kill("SIGTERM");
26896
27363
  await new Promise((resolve4) => setTimeout(resolve4, 250));
26897
- if (child.exitCode === null && !child.killed) {
27364
+ if (child.exitCode === null && child.signalCode === null) {
26898
27365
  child.kill("SIGKILL");
26899
27366
  }
26900
27367
  }
@@ -26945,6 +27412,7 @@ class CodexAppServerSession {
26945
27412
  await mkdir4(this.options.runtimeDirectory, { recursive: true });
26946
27413
  await mkdir4(this.options.logsDirectory, { recursive: true });
26947
27414
  await writeFile4(join13(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
27415
+ this.proactiveShutdown = null;
26948
27416
  const codexExecutable = resolveCodexExecutable();
26949
27417
  const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
26950
27418
  const env = buildManagedAgentEnvironment({
@@ -26984,7 +27452,16 @@ class CodexAppServerSession {
26984
27452
  this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage3(error48)}`));
26985
27453
  });
26986
27454
  child.once("exit", (code, signal) => {
26987
- this.failSession(new Error(`Codex app-server exited for ${this.options.agentName}` + (code !== null ? ` with code ${code}` : "") + (signal ? ` (${signal})` : "")));
27455
+ const proactiveShutdown = this.proactiveShutdown;
27456
+ if (proactiveShutdown) {
27457
+ this.handleProactiveProcessExit(child, proactiveShutdown, code, signal);
27458
+ return;
27459
+ }
27460
+ this.failSession(new CodexAppServerExitError({
27461
+ agentName: this.options.agentName,
27462
+ exitCode: code,
27463
+ signal
27464
+ }));
26988
27465
  });
26989
27466
  await this.request("initialize", {
26990
27467
  clientInfo: {
@@ -27319,6 +27796,23 @@ class CodexAppServerSession {
27319
27796
  }
27320
27797
  this.pendingRequests.clear();
27321
27798
  appendFile4(this.stderrLogPath, `[openscout] ${error48.message}
27799
+ `).catch(() => {
27800
+ return;
27801
+ });
27802
+ this.persistState();
27803
+ }
27804
+ handleProactiveProcessExit(child, shutdown, code, signal) {
27805
+ if (this.process === child) {
27806
+ this.process = null;
27807
+ }
27808
+ this.proactiveShutdown = null;
27809
+ this.starting = null;
27810
+ const exitDetail = [
27811
+ code !== null ? `code ${code}` : null,
27812
+ signal
27813
+ ].filter(Boolean).join(", ");
27814
+ const detail = exitDetail ? ` (${exitDetail})` : "";
27815
+ appendFile4(this.stderrLogPath, `[openscout] Codex app-server stopped for ${this.options.agentName}: ${shutdown.reason}${detail}
27322
27816
  `).catch(() => {
27323
27817
  return;
27324
27818
  });
@@ -27390,7 +27884,10 @@ function getOrCreateSession2(options) {
27390
27884
  if (existing.matches(options)) {
27391
27885
  return existing;
27392
27886
  }
27393
- existing.shutdown({ resetThread: true });
27887
+ existing.shutdown({
27888
+ resetThread: true,
27889
+ reason: "OpenScout replaced the app-server session after its launch options changed"
27890
+ });
27394
27891
  sessions2.delete(key);
27395
27892
  }
27396
27893
  const session = new CodexAppServerSession(options);
@@ -31507,6 +32004,17 @@ async function fetchHealthSnapshot(config2) {
31507
32004
  nodes: payload.counts.nodes ?? 0,
31508
32005
  actors: payload.counts.actors ?? 0,
31509
32006
  agents: payload.counts.agents ?? 0,
32007
+ agentRecords: payload.counts.agentRecords,
32008
+ rawAgentRecords: payload.counts.rawAgentRecords,
32009
+ configuredAgents: payload.counts.configuredAgents,
32010
+ scoutManagedAgents: payload.counts.scoutManagedAgents,
32011
+ currentAgentRegistrations: payload.counts.currentAgentRegistrations,
32012
+ localAgentRegistrations: payload.counts.localAgentRegistrations,
32013
+ remoteAgentRegistrations: payload.counts.remoteAgentRegistrations,
32014
+ staleAgentRegistrations: payload.counts.staleAgentRegistrations,
32015
+ retiredAgentRegistrations: payload.counts.retiredAgentRegistrations,
32016
+ oneTimeAgentCards: payload.counts.oneTimeAgentCards,
32017
+ persistentAgentCards: payload.counts.persistentAgentCards,
31510
32018
  conversations: payload.counts.conversations ?? 0,
31511
32019
  messages: payload.counts.messages ?? 0,
31512
32020
  flights: payload.counts.flights ?? 0,
@@ -31731,11 +32239,42 @@ function compileCodexPermissionProfile(profileInput) {
31731
32239
  }
31732
32240
  }
31733
32241
 
32242
+ // packages/runtime/src/user-config.ts
32243
+ import { existsSync as existsSync13, readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
32244
+ import { homedir as homedir14 } from "os";
32245
+ import { dirname as dirname9, join as join18 } from "path";
32246
+ function userConfigPath() {
32247
+ return join18(process.env.OPENSCOUT_HOME ?? join18(homedir14(), ".openscout"), "user.json");
32248
+ }
32249
+ function loadUserConfig() {
32250
+ const configPath = userConfigPath();
32251
+ if (!existsSync13(configPath))
32252
+ return {};
32253
+ try {
32254
+ return JSON.parse(readFileSync6(configPath, "utf8"));
32255
+ } catch {
32256
+ return {};
32257
+ }
32258
+ }
32259
+ function resolveOperatorName() {
32260
+ const config2 = loadUserConfig();
32261
+ return config2.name?.trim() || process.env.OPENSCOUT_OPERATOR_NAME?.trim() || process.env.USER?.trim() || "operator";
32262
+ }
32263
+ function normalizeHandle(value) {
32264
+ return value?.trim().replace(/^@+/, "") ?? "";
32265
+ }
32266
+ function resolveOperatorHandle() {
32267
+ const config2 = loadUserConfig();
32268
+ return normalizeHandle(config2.handle) || normalizeHandle(process.env.OPENSCOUT_OPERATOR_HANDLE) || normalizeHandle(config2.name) || normalizeHandle(process.env.OPENSCOUT_OPERATOR_NAME) || normalizeHandle(process.env.USER) || "operator";
32269
+ }
32270
+
31734
32271
  // packages/runtime/src/local-agents.ts
31735
- var MODULE_DIRECTORY = dirname9(fileURLToPath5(import.meta.url));
32272
+ var MODULE_DIRECTORY = dirname10(fileURLToPath5(import.meta.url));
31736
32273
  var OPENSCOUT_REPO_ROOT = resolve7(MODULE_DIRECTORY, "..", "..", "..");
31737
32274
  var DEFAULT_LOCAL_AGENT_CAPABILITIES = ["chat", "invoke", "deliver"];
31738
32275
  var DEFAULT_LOCAL_AGENT_HARNESS = "claude";
32276
+ var DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_TTL_MS = 24 * 60 * 60 * 1000;
32277
+ var DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_RETAIN = 24;
31739
32278
  function resolveRelayHub() {
31740
32279
  return resolveOpenScoutSupportPaths().relayHubDirectory;
31741
32280
  }
@@ -31745,14 +32284,14 @@ function resolveProjectsRoot(projectPath) {
31745
32284
  }
31746
32285
  try {
31747
32286
  const supportPaths = resolveOpenScoutSupportPaths();
31748
- if (!existsSync13(supportPaths.settingsPath)) {
31749
- return dirname9(projectPath);
32287
+ if (!existsSync14(supportPaths.settingsPath)) {
32288
+ return dirname10(projectPath);
31750
32289
  }
31751
- const raw = JSON.parse(readFileSync6(supportPaths.settingsPath, "utf8"));
32290
+ const raw = JSON.parse(readFileSync7(supportPaths.settingsPath, "utf8"));
31752
32291
  const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
31753
- return workspaceRoot ? resolve7(workspaceRoot) : dirname9(projectPath);
32292
+ return workspaceRoot ? resolve7(workspaceRoot) : dirname10(projectPath);
31754
32293
  } catch {
31755
- return dirname9(projectPath);
32294
+ return dirname10(projectPath);
31756
32295
  }
31757
32296
  }
31758
32297
  function resolveBrokerUrl() {
@@ -31765,7 +32304,7 @@ function normalizeBrokerTimestamp(value) {
31765
32304
  return value > 10000000000 ? Math.floor(value / 1000) : value;
31766
32305
  }
31767
32306
  function scoutCliPath() {
31768
- return join18(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
32307
+ return join19(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
31769
32308
  }
31770
32309
  function legacyNodeBrokerRelayCommand() {
31771
32310
  return `node ${JSON.stringify(scoutCliPath())}`;
@@ -31792,13 +32331,13 @@ var SUPPORTED_SCOUT_HARNESSES = [
31792
32331
  ];
31793
32332
  function resolveScoutSkillPath() {
31794
32333
  const candidatePaths = [
31795
- join18(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
31796
- join18(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
31797
- join18(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
31798
- join18(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
32334
+ join19(OPENSCOUT_REPO_ROOT, ".agents", "skills", "scout", "SKILL.md"),
32335
+ join19(process.env.HOME ?? "", ".agents", "skills", "scout", "SKILL.md"),
32336
+ join19(OPENSCOUT_REPO_ROOT, ".agents", "skills", "relay-agent-comms", "SKILL.md"),
32337
+ join19(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
31799
32338
  ];
31800
32339
  for (const path of candidatePaths) {
31801
- if (existsSync13(path)) {
32340
+ if (existsSync14(path)) {
31802
32341
  return path;
31803
32342
  }
31804
32343
  }
@@ -31906,6 +32445,74 @@ function buildLocalAgentSystemPromptTemplate() {
31906
32445
  ].join(`
31907
32446
  `);
31908
32447
  }
32448
+ function normalizeOperatorHandleSegment(value) {
32449
+ return normalizeAgentSelectorSegment(value?.trim().replace(/^@+/, "") ?? "") || "operator";
32450
+ }
32451
+ function resolveOperatorAugmentAgentName() {
32452
+ return `${normalizeOperatorHandleSegment(resolveOperatorHandle())}-ai`;
32453
+ }
32454
+ function operatorAugmentAgentNameCandidates() {
32455
+ return new Set([
32456
+ resolveOperatorAugmentAgentName(),
32457
+ "operator-ai"
32458
+ ]);
32459
+ }
32460
+ function buildOperatorAugmentSystemPromptTemplate(input = {}) {
32461
+ const operatorHandle = normalizeOperatorHandleSegment(input.operatorHandle ?? resolveOperatorHandle());
32462
+ const augmentHandle = normalizeOperatorHandleSegment(input.augmentHandle ?? `${operatorHandle}-ai`);
32463
+ const humanLabel = `@${operatorHandle}`;
32464
+ const augmentLabel = `@${augmentHandle}`;
32465
+ return [
32466
+ "{{base_prompt}}",
32467
+ "",
32468
+ `You are the augmented counterpart to the human Scout operator ${humanLabel}.`,
32469
+ `${humanLabel} is the human. ${augmentLabel} is the AI-augmented looper with a human in the loop.`,
32470
+ `Do not impersonate ${humanLabel}. Speak and act as ${augmentLabel}, and involve ${humanLabel} only when human judgment or approval is the real next dependency.`,
32471
+ "",
32472
+ "Operating loop:",
32473
+ " - Keep long-running conversations in the same DM or invocation thread whenever possible.",
32474
+ " - Maintain continuity across turns: track the goal, decisions made, open questions, blockers, and promised follow-ups.",
32475
+ " - For efforts that span many turns, many files, or more than one working session, create or update a durable note/checkpoint when you have write access, then point to it briefly.",
32476
+ " - Prefer continuing from a concise recap over resetting context. If context is aging, summarize the useful state and keep moving.",
32477
+ ` - Be explicit about the next responsible owner: ${augmentLabel}, ${humanLabel}, or another named agent.`,
32478
+ "",
32479
+ `When to invoke ${humanLabel}:`,
32480
+ " - Approval is needed for destructive, irreversible, public, financial, security-sensitive, credential, privacy, or cross-project priority decisions.",
32481
+ ` - The task depends on taste, intent, product direction, personal context, or a choice only ${humanLabel} can make.`,
32482
+ " - You are blocked after a reasonable local attempt and the unblock request can be stated as a short concrete question.",
32483
+ " - A result is materially uncertain and acting without human input could waste meaningful time or create cleanup work.",
32484
+ " - You need permission to interrupt, wake, or redirect other people or agents outside the current work venue.",
32485
+ "",
32486
+ `How to invoke ${humanLabel}:`,
32487
+ " - Keep the ask concise: context, the decision needed, the default you recommend, and the consequence of no answer.",
32488
+ " - Use the same DM/thread when it exists. Do not broadcast human-loop requests.",
32489
+ ` - If a reply is required before work can proceed, say that ${humanLabel} owns the next move and stop claiming progress.`,
32490
+ " - If work can continue safely, state the assumption and continue without waiting.",
32491
+ "",
32492
+ `Do not invoke ${humanLabel} for:`,
32493
+ " - Routine status updates, obvious implementation details, command outputs, local orientation, or reversible low-risk cleanup.",
32494
+ " - Ambiguity that you can resolve by reading the repo, checking Scout state, or asking the correct specialist agent.",
32495
+ "",
32496
+ "{{project_context}}",
32497
+ "",
32498
+ "{{collaboration_prompt}}",
32499
+ "",
32500
+ "{{protocol_prompt}}"
32501
+ ].join(`
32502
+ `);
32503
+ }
32504
+ function operatorAugmentDefaultsForDefinitionId(definitionId) {
32505
+ const normalizedDefinitionId = normalizeAgentSelectorSegment(definitionId);
32506
+ if (!normalizedDefinitionId || !operatorAugmentAgentNameCandidates().has(normalizedDefinitionId)) {
32507
+ return null;
32508
+ }
32509
+ return {
32510
+ displayName: titleCaseLocalAgentName(normalizedDefinitionId),
32511
+ systemPrompt: buildOperatorAugmentSystemPromptTemplate({
32512
+ augmentHandle: normalizedDefinitionId
32513
+ })
32514
+ };
32515
+ }
31909
32516
  function renderLocalAgentSystemPromptTemplate(template, context, options = {}) {
31910
32517
  const basePrompt = buildLocalAgentBasePrompt(context);
31911
32518
  const projectContext = buildLocalAgentProjectContextPrompt(context);
@@ -32060,7 +32667,7 @@ function expandHomePath3(value) {
32060
32667
  return process.env.HOME ?? process.cwd();
32061
32668
  }
32062
32669
  if (value.startsWith("~/")) {
32063
- return join18(process.env.HOME ?? process.cwd(), value.slice(2));
32670
+ return join19(process.env.HOME ?? process.cwd(), value.slice(2));
32064
32671
  }
32065
32672
  return value;
32066
32673
  }
@@ -32125,8 +32732,8 @@ var DEFAULT_CLAUDE_SCOUT_ALLOWED_TOOLS = [
32125
32732
  "mcp__scout__agents_search",
32126
32733
  "mcp__scout__agents_resolve",
32127
32734
  "mcp__scout__messages_reply",
32735
+ "mcp__scout__ask",
32128
32736
  "mcp__scout__messages_send",
32129
- "mcp__scout__invocations_ask",
32130
32737
  "mcp__scout__invocations_get",
32131
32738
  "mcp__scout__invocations_wait",
32132
32739
  "mcp__scout__work_update",
@@ -32146,6 +32753,10 @@ function normalizeClaudeRuntimeLaunchArgs(value) {
32146
32753
  DEFAULT_CLAUDE_SCOUT_ALLOWED_TOOLS.join(",")
32147
32754
  ];
32148
32755
  }
32756
+ function normalizeRequestedModel(value) {
32757
+ const trimmed = value?.trim();
32758
+ return trimmed ? trimmed : undefined;
32759
+ }
32149
32760
  function readClaudeLaunchModel(launchArgs) {
32150
32761
  for (let index = 0;index < launchArgs.length; index += 1) {
32151
32762
  const current = launchArgs[index] ?? "";
@@ -32193,37 +32804,199 @@ function readLaunchModelForHarness(harness, launchArgs) {
32193
32804
  }
32194
32805
  return;
32195
32806
  }
32196
- function normalizeManagedHarness2(value, fallback) {
32197
- return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : value === "pi" ? "pi" : fallback;
32198
- }
32199
- function normalizeLocalHarnessProfiles(agentId, record2) {
32200
- const defaultHarness = normalizeManagedHarness2(typeof record2.defaultHarness === "string" ? record2.defaultHarness : record2.harness, "claude");
32201
- const nextProfiles = {};
32202
- for (const harness of ["claude", "codex", "cursor", "pi"]) {
32203
- const profile = record2.harnessProfiles?.[harness];
32204
- if (!profile) {
32205
- continue;
32807
+ function stripLaunchModelForHarness(harness, launchArgs) {
32808
+ if (harness === "codex") {
32809
+ const next = [];
32810
+ const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
32811
+ for (let index = 0;index < normalized.length; index += 1) {
32812
+ const current = normalized[index] ?? "";
32813
+ if (current === "-c" || current === "--config") {
32814
+ const value = normalized[index + 1];
32815
+ if (readCodexAppServerModelFromLaunchArgs(value ? [current, value] : [current])) {
32816
+ index += 1;
32817
+ continue;
32818
+ }
32819
+ next.push(current);
32820
+ if (value) {
32821
+ next.push(value);
32822
+ index += 1;
32823
+ }
32824
+ continue;
32825
+ }
32826
+ if (current.startsWith("--config=")) {
32827
+ if (readCodexAppServerModelFromLaunchArgs([current])) {
32828
+ continue;
32829
+ }
32830
+ }
32831
+ next.push(current);
32206
32832
  }
32207
- nextProfiles[harness] = {
32208
- cwd: normalizeProjectPath2(profile.cwd || record2.cwd || process.cwd()),
32209
- transport: normalizeLocalAgentTransport(profile.transport, harness),
32210
- sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
32211
- launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
32212
- ...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
32213
- };
32833
+ return next;
32214
32834
  }
32215
- const runtimeHarness = normalizeManagedHarness2(record2.harness, defaultHarness);
32216
- if (!nextProfiles[runtimeHarness]) {
32217
- nextProfiles[runtimeHarness] = {
32218
- cwd: normalizeProjectPath2(record2.cwd || process.cwd()),
32219
- transport: normalizeLocalAgentTransport(record2.transport, runtimeHarness),
32220
- sessionId: normalizeTmuxSessionName2(record2.tmuxSession, `${agentId}-${runtimeHarness}`),
32221
- launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record2.launchArgs),
32222
- ...record2.permissionProfile ? { permissionProfile: record2.permissionProfile } : {}
32223
- };
32835
+ if (harness === "claude" || harness === "pi") {
32836
+ const next = [];
32837
+ const normalized = normalizeLocalAgentLaunchArgs(launchArgs);
32838
+ for (let index = 0;index < normalized.length; index += 1) {
32839
+ const current = normalized[index] ?? "";
32840
+ if (current === "--model") {
32841
+ index += 1;
32842
+ continue;
32843
+ }
32844
+ if (current.startsWith("--model=")) {
32845
+ continue;
32846
+ }
32847
+ next.push(current);
32848
+ }
32849
+ return next;
32224
32850
  }
32225
- if (!nextProfiles[defaultHarness]) {
32226
- nextProfiles[defaultHarness] = {
32851
+ return normalizeLocalAgentLaunchArgs(launchArgs);
32852
+ }
32853
+ function buildLaunchArgsForRequestedModel(harness, model) {
32854
+ if (harness === "codex") {
32855
+ return normalizeCodexAppServerLaunchArgs(["--model", model]);
32856
+ }
32857
+ if (harness === "claude") {
32858
+ return ["--model", model];
32859
+ }
32860
+ if (harness === "pi") {
32861
+ return ["--model", model];
32862
+ }
32863
+ return [];
32864
+ }
32865
+ function normalizeRequestedReasoningEffort(reasoningEffort) {
32866
+ const trimmed = reasoningEffort?.trim();
32867
+ return trimmed ? trimmed : undefined;
32868
+ }
32869
+ function stripLaunchReasoningEffortForHarness(harness, launchArgs) {
32870
+ if (harness === "pi") {
32871
+ const next2 = [];
32872
+ const normalized2 = normalizeLocalAgentLaunchArgs(launchArgs);
32873
+ for (let index = 0;index < normalized2.length; index += 1) {
32874
+ const current = normalized2[index] ?? "";
32875
+ if (current === "--thinking") {
32876
+ index += 1;
32877
+ continue;
32878
+ }
32879
+ if (current.startsWith("--thinking=")) {
32880
+ continue;
32881
+ }
32882
+ next2.push(current);
32883
+ }
32884
+ return next2;
32885
+ }
32886
+ if (harness !== "codex") {
32887
+ return normalizeLaunchArgsForHarness(harness, launchArgs);
32888
+ }
32889
+ const next = [];
32890
+ const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
32891
+ for (let index = 0;index < normalized.length; index += 1) {
32892
+ const current = normalized[index] ?? "";
32893
+ if (current === "-c" || current === "--config") {
32894
+ const value = normalized[index + 1];
32895
+ if (readCodexAppServerReasoningEffortFromLaunchArgs(value ? [current, value] : [current])) {
32896
+ index += 1;
32897
+ continue;
32898
+ }
32899
+ next.push(current);
32900
+ if (value) {
32901
+ next.push(value);
32902
+ index += 1;
32903
+ }
32904
+ continue;
32905
+ }
32906
+ if (current.startsWith("--config=")) {
32907
+ if (readCodexAppServerReasoningEffortFromLaunchArgs([current])) {
32908
+ continue;
32909
+ }
32910
+ }
32911
+ next.push(current);
32912
+ }
32913
+ return next;
32914
+ }
32915
+ function buildLaunchArgsForRequestedReasoningEffort(harness, reasoningEffort) {
32916
+ if (harness === "codex") {
32917
+ return normalizeCodexAppServerLaunchArgs(["--reasoning-effort", reasoningEffort]);
32918
+ }
32919
+ if (harness === "pi") {
32920
+ return ["--thinking", reasoningEffort];
32921
+ }
32922
+ return [];
32923
+ }
32924
+ function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
32925
+ const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
32926
+ const requestedModel = normalizeRequestedModel(model);
32927
+ if (!requestedModel) {
32928
+ return normalized;
32929
+ }
32930
+ return [
32931
+ ...stripLaunchModelForHarness(harness, normalized),
32932
+ ...buildLaunchArgsForRequestedModel(harness, requestedModel)
32933
+ ];
32934
+ }
32935
+ function applyRequestedRuntimeOptionsToLaunchArgs(harness, launchArgs, options) {
32936
+ const withModel = applyRequestedModelToLaunchArgs(harness, launchArgs, options.model);
32937
+ const requestedReasoningEffort = normalizeRequestedReasoningEffort(options.reasoningEffort);
32938
+ if (!requestedReasoningEffort) {
32939
+ return withModel;
32940
+ }
32941
+ return [
32942
+ ...stripLaunchReasoningEffortForHarness(harness, withModel),
32943
+ ...buildLaunchArgsForRequestedReasoningEffort(harness, requestedReasoningEffort)
32944
+ ];
32945
+ }
32946
+ function defaultHarnessForOverride(override, fallback = "claude") {
32947
+ return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
32948
+ }
32949
+ function launchArgsForOverrideHarness(override, harness) {
32950
+ const profileLaunchArgs = override.harnessProfiles?.[harness]?.launchArgs;
32951
+ if (profileLaunchArgs) {
32952
+ return normalizeLaunchArgsForHarness(harness, profileLaunchArgs);
32953
+ }
32954
+ if (harness === defaultHarnessForOverride(override, harness)) {
32955
+ return normalizeLaunchArgsForHarness(harness, override.launchArgs);
32956
+ }
32957
+ return [];
32958
+ }
32959
+ function overrideHarnessProfile(override, harness) {
32960
+ const profile = override.harnessProfiles?.[harness];
32961
+ return {
32962
+ cwd: normalizeProjectPath2(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
32963
+ transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
32964
+ sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
32965
+ launchArgs: launchArgsForOverrideHarness(override, harness),
32966
+ ...profile?.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
32967
+ };
32968
+ }
32969
+ function normalizeManagedHarness2(value, fallback) {
32970
+ return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : value === "pi" ? "pi" : fallback;
32971
+ }
32972
+ function normalizeLocalHarnessProfiles(agentId, record2) {
32973
+ const defaultHarness = normalizeManagedHarness2(typeof record2.defaultHarness === "string" ? record2.defaultHarness : record2.harness, "claude");
32974
+ const nextProfiles = {};
32975
+ for (const harness of ["claude", "codex", "cursor", "pi"]) {
32976
+ const profile = record2.harnessProfiles?.[harness];
32977
+ if (!profile) {
32978
+ continue;
32979
+ }
32980
+ nextProfiles[harness] = {
32981
+ cwd: normalizeProjectPath2(profile.cwd || record2.cwd || process.cwd()),
32982
+ transport: normalizeLocalAgentTransport(profile.transport, harness),
32983
+ sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
32984
+ launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
32985
+ ...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
32986
+ };
32987
+ }
32988
+ const runtimeHarness = normalizeManagedHarness2(record2.harness, defaultHarness);
32989
+ if (!nextProfiles[runtimeHarness]) {
32990
+ nextProfiles[runtimeHarness] = {
32991
+ cwd: normalizeProjectPath2(record2.cwd || process.cwd()),
32992
+ transport: normalizeLocalAgentTransport(record2.transport, runtimeHarness),
32993
+ sessionId: normalizeTmuxSessionName2(record2.tmuxSession, `${agentId}-${runtimeHarness}`),
32994
+ launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record2.launchArgs),
32995
+ ...record2.permissionProfile ? { permissionProfile: record2.permissionProfile } : {}
32996
+ };
32997
+ }
32998
+ if (!nextProfiles[defaultHarness]) {
32999
+ nextProfiles[defaultHarness] = {
32227
33000
  cwd: normalizeProjectPath2(record2.cwd || process.cwd()),
32228
33001
  transport: normalizeLocalAgentTransport(record2.transport, defaultHarness),
32229
33002
  sessionId: normalizeTmuxSessionName2(record2.tmuxSession, `${agentId}-${defaultHarness}`),
@@ -32290,6 +33063,31 @@ function recordForHarness(record2, harnessOverride) {
32290
33063
  permissionProfile: profile.permissionProfile
32291
33064
  };
32292
33065
  }
33066
+ function normalizeCardTimestamp(value) {
33067
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
33068
+ }
33069
+ function normalizeLocalAgentCardLifecycle(value, now = Date.now()) {
33070
+ if (!value) {
33071
+ return;
33072
+ }
33073
+ const kind = value.kind === "one_time" ? "one_time" : value.kind === "persistent" ? "persistent" : undefined;
33074
+ if (!kind) {
33075
+ return;
33076
+ }
33077
+ const createdAt = normalizeCardTimestamp(value.createdAt) ?? now;
33078
+ const expiresAt = normalizeCardTimestamp(value.expiresAt);
33079
+ const maxUses = typeof value.maxUses === "number" && Number.isFinite(value.maxUses) && value.maxUses > 0 ? Math.floor(value.maxUses) : undefined;
33080
+ const createdById = value.createdById?.trim();
33081
+ const inboxConversationId = value.inboxConversationId?.trim();
33082
+ return {
33083
+ kind,
33084
+ createdAt,
33085
+ ...createdById ? { createdById } : {},
33086
+ ...expiresAt ? { expiresAt } : {},
33087
+ ...maxUses ? { maxUses } : {},
33088
+ ...inboxConversationId ? { inboxConversationId } : {}
33089
+ };
33090
+ }
32293
33091
  function normalizeLocalAgentRecord(agentId, record2) {
32294
33092
  const cwd = normalizeProjectPath2(record2.cwd || process.cwd());
32295
33093
  const projectRoot = normalizeProjectPath2(record2.projectRoot || cwd);
@@ -32319,9 +33117,28 @@ function normalizeLocalAgentRecord(agentId, record2) {
32319
33117
  capabilities: normalizeLocalAgentCapabilities(record2.capabilities),
32320
33118
  launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record2.launchArgs),
32321
33119
  permissionProfile: activeProfile?.permissionProfile ?? record2.permissionProfile,
32322
- registrationSource: record2.registrationSource
33120
+ registrationSource: record2.registrationSource,
33121
+ card: normalizeLocalAgentCardLifecycle(record2.card)
33122
+ };
33123
+ }
33124
+ function localAgentStatusFromRecord(agentId, record2, source) {
33125
+ const normalizedRecord = normalizeLocalAgentRecord(agentId, record2);
33126
+ return {
33127
+ agentId,
33128
+ definitionId: normalizedRecord.definitionId ?? agentId,
33129
+ projectName: normalizedRecord.project,
33130
+ projectRoot: normalizedRecord.projectRoot ?? normalizedRecord.cwd,
33131
+ sessionId: normalizedRecord.tmuxSession,
33132
+ startedAt: normalizedRecord.startedAt,
33133
+ harness: normalizeLocalAgentHarness(normalizedRecord.harness),
33134
+ transport: normalizedRecord.transport,
33135
+ isOnline: isLocalAgentRecordOnline(agentId, normalizedRecord),
33136
+ source
32323
33137
  };
32324
33138
  }
33139
+ function localAgentStatusSource(agentId, overrides) {
33140
+ return overrides[agentId]?.source === "manual" ? "manual" : "configured";
33141
+ }
32325
33142
  function localAgentRecordFromResolvedConfig(config2) {
32326
33143
  return normalizeLocalAgentRecord(config2.agentId, {
32327
33144
  definitionId: config2.definitionId,
@@ -32355,7 +33172,8 @@ function localAgentRecordFromRelayAgentOverride(agentId, override) {
32355
33172
  harnessProfiles: override.harnessProfiles,
32356
33173
  transport: normalizeLocalAgentTransport(override.runtime?.transport, normalizeLocalAgentHarness(override.runtime?.harness)),
32357
33174
  capabilities: override.capabilities,
32358
- launchArgs: override.launchArgs
33175
+ launchArgs: override.launchArgs,
33176
+ card: override.card
32359
33177
  });
32360
33178
  }
32361
33179
  function relayAgentOverrideFromLocalAgentRecord(agentId, record2, existing) {
@@ -32376,6 +33194,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record2, existing) {
32376
33194
  capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
32377
33195
  defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
32378
33196
  harnessProfiles: normalizedRecord.harnessProfiles,
33197
+ ...normalizedRecord.card ? { card: normalizedRecord.card } : {},
32379
33198
  runtime: {
32380
33199
  cwd: normalizeProjectPath2(normalizedRecord.cwd),
32381
33200
  harness: normalizeLocalAgentHarness(normalizedRecord.harness),
@@ -32387,8 +33206,8 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record2, existing) {
32387
33206
  }
32388
33207
  async function ensureLocalSessionEndpointOnline(endpoint) {
32389
33208
  if (endpoint.transport === "codex_app_server") {
32390
- await ensureCodexAppServerAgentOnline(buildCodexEndpointSessionOptions(endpoint));
32391
- return {};
33209
+ const result = await ensureCodexAppServerAgentOnline(buildCodexEndpointSessionOptions(endpoint));
33210
+ return { externalSessionId: result.threadId };
32392
33211
  }
32393
33212
  if (endpoint.transport === "claude_stream_json") {
32394
33213
  const result = await ensureClaudeStreamJsonAgentOnline(buildClaudeEndpointSessionOptions(endpoint));
@@ -32405,6 +33224,118 @@ async function shutdownLocalSessionEndpoint(endpoint) {
32405
33224
  await shutdownClaudeStreamJsonAgent(buildClaudeEndpointSessionOptions(endpoint));
32406
33225
  }
32407
33226
  }
33227
+ function oneTimeCardCreatedAt(lifecycle2) {
33228
+ return normalizeCardTimestamp(lifecycle2.createdAt) ?? 0;
33229
+ }
33230
+ function shouldPruneOneTimeCard(lifecycle2, now, maxAgeMs) {
33231
+ if (lifecycle2.kind !== "one_time") {
33232
+ return false;
33233
+ }
33234
+ if (lifecycle2.expiresAt !== undefined && lifecycle2.expiresAt <= now) {
33235
+ return true;
33236
+ }
33237
+ const createdAt = oneTimeCardCreatedAt(lifecycle2);
33238
+ return createdAt > 0 && createdAt + maxAgeMs <= now;
33239
+ }
33240
+ function oneTimeCardMatchesScope(agentId, override, input) {
33241
+ if (BUILT_IN_AGENT_DEFINITION_IDS.has(agentId)) {
33242
+ return false;
33243
+ }
33244
+ const lifecycle2 = normalizeLocalAgentCardLifecycle(override.card);
33245
+ if (lifecycle2?.kind !== "one_time") {
33246
+ return false;
33247
+ }
33248
+ if (input.createdById?.trim() && lifecycle2.createdById !== input.createdById.trim()) {
33249
+ return false;
33250
+ }
33251
+ if (input.projectRoot?.trim()) {
33252
+ const scopedRoot = normalizeProjectPath2(input.projectRoot);
33253
+ const cardRoot = normalizeProjectPath2(override.projectRoot || override.runtime?.cwd || ".");
33254
+ if (cardRoot !== scopedRoot) {
33255
+ return false;
33256
+ }
33257
+ }
33258
+ return true;
33259
+ }
33260
+ async function retireLocalAgentOverrides(overrides, agentIds) {
33261
+ const retired = [];
33262
+ for (const agentId of agentIds) {
33263
+ const override = overrides[agentId];
33264
+ if (!override) {
33265
+ continue;
33266
+ }
33267
+ const record2 = localAgentRecordFromRelayAgentOverride(agentId, override);
33268
+ const status = localAgentStatusFromRecord(agentId, record2, localAgentStatusSource(agentId, overrides));
33269
+ await stopLocalAgent(agentId).catch(() => null);
33270
+ retired.push({
33271
+ ...status,
33272
+ isOnline: false
33273
+ });
33274
+ delete overrides[agentId];
33275
+ }
33276
+ if (retired.length > 0) {
33277
+ await writeRelayAgentOverrides(overrides);
33278
+ }
33279
+ return retired;
33280
+ }
33281
+ async function pruneOneTimeLocalAgentCards(input = {}) {
33282
+ const now = input.now ?? Date.now();
33283
+ const maxAgeMs = Math.max(0, input.maxAgeMs ?? DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_TTL_MS);
33284
+ const maxCount = Math.max(0, input.maxCount ?? DEFAULT_ONE_TIME_LOCAL_AGENT_CARD_RETAIN);
33285
+ const excluded = new Set(input.excludeAgentIds ?? []);
33286
+ const overrides = await readRelayAgentOverrides();
33287
+ const candidates = Object.entries(overrides).filter(([agentId, override]) => !excluded.has(agentId) && oneTimeCardMatchesScope(agentId, override, input)).map(([agentId, override]) => ({
33288
+ agentId,
33289
+ override,
33290
+ lifecycle: normalizeLocalAgentCardLifecycle(override.card)
33291
+ })).sort((left, right) => oneTimeCardCreatedAt(right.lifecycle) - oneTimeCardCreatedAt(left.lifecycle) || left.agentId.localeCompare(right.agentId));
33292
+ const retireIds = new Set;
33293
+ for (const candidate of candidates) {
33294
+ if (shouldPruneOneTimeCard(candidate.lifecycle, now, maxAgeMs)) {
33295
+ retireIds.add(candidate.agentId);
33296
+ }
33297
+ }
33298
+ const retained = candidates.filter((candidate) => !retireIds.has(candidate.agentId));
33299
+ for (const candidate of retained.slice(maxCount)) {
33300
+ retireIds.add(candidate.agentId);
33301
+ }
33302
+ const retired = await retireLocalAgentOverrides({ ...overrides }, retireIds);
33303
+ return {
33304
+ inspected: candidates.length,
33305
+ remaining: Math.max(0, candidates.length - retired.length),
33306
+ retired
33307
+ };
33308
+ }
33309
+ async function retireConsumedOneTimeLocalAgentCards(input) {
33310
+ const conversationId = input.conversationId.trim();
33311
+ const actorId = input.actorId.trim();
33312
+ if (!conversationId || !actorId) {
33313
+ return [];
33314
+ }
33315
+ const participants = new Set(input.participantIds.map((entry) => entry.trim()).filter(Boolean));
33316
+ if (participants.size === 0) {
33317
+ return [];
33318
+ }
33319
+ const overrides = await readRelayAgentOverrides();
33320
+ const retireIds = new Set;
33321
+ for (const [agentId, override] of Object.entries(overrides)) {
33322
+ if (!participants.has(agentId)) {
33323
+ continue;
33324
+ }
33325
+ const lifecycle2 = normalizeLocalAgentCardLifecycle(override.card);
33326
+ if (lifecycle2?.kind !== "one_time") {
33327
+ continue;
33328
+ }
33329
+ if (lifecycle2.createdById && lifecycle2.createdById === actorId) {
33330
+ continue;
33331
+ }
33332
+ if (agentId === actorId) {
33333
+ continue;
33334
+ }
33335
+ retireIds.add(agentId);
33336
+ }
33337
+ return retireLocalAgentOverrides({ ...overrides }, retireIds);
33338
+ }
32408
33339
  function buildCodexAgentSessionOptions(agentName, record2, systemPrompt) {
32409
33340
  const permissionPosture = compileCodexPermissionProfile(record2.permissionProfile);
32410
33341
  return {
@@ -32435,6 +33366,22 @@ function endpointMetadataString(endpoint, key) {
32435
33366
  const value = endpoint.metadata?.[key];
32436
33367
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
32437
33368
  }
33369
+ function endpointMetadataStringArray(endpoint, key) {
33370
+ const value = endpoint.metadata?.[key];
33371
+ if (Array.isArray(value)) {
33372
+ return value.map((entry) => String(entry).trim()).filter(Boolean);
33373
+ }
33374
+ if (typeof value === "string" && value.trim()) {
33375
+ return [value.trim()];
33376
+ }
33377
+ return [];
33378
+ }
33379
+ function attachedCodexEndpointLaunchArgs(endpoint) {
33380
+ return applyRequestedRuntimeOptionsToLaunchArgs("codex", endpointMetadataStringArray(endpoint, "launchArgs"), {
33381
+ model: endpointMetadataString(endpoint, "model"),
33382
+ reasoningEffort: endpointMetadataString(endpoint, "reasoningEffort")
33383
+ });
33384
+ }
32438
33385
  function endpointInvocationPrompt(endpoint, agentName, invocation) {
32439
33386
  const source = endpointMetadataString(endpoint, "source");
32440
33387
  const externalSource = endpointMetadataString(endpoint, "externalSource");
@@ -32472,7 +33419,8 @@ function attachedLocalSessionSystemPrompt(endpoint) {
32472
33419
  }
32473
33420
  function buildCodexEndpointSessionOptions(endpoint) {
32474
33421
  const agentName = endpointAgentName(endpoint);
32475
- const threadId = endpointMetadataString(endpoint, "threadId") ?? endpointMetadataString(endpoint, "externalSessionId") ?? endpoint.sessionId ?? undefined;
33422
+ const ownsSessionThread = endpoint.metadata?.source === "scoutbot";
33423
+ const threadId = ownsSessionThread ? undefined : endpointMetadataString(endpoint, "threadId") ?? endpointMetadataString(endpoint, "externalSessionId") ?? endpoint.sessionId ?? undefined;
32476
33424
  return {
32477
33425
  agentName,
32478
33426
  sessionId: endpointRuntimeInstanceId(endpoint),
@@ -32480,9 +33428,9 @@ function buildCodexEndpointSessionOptions(endpoint) {
32480
33428
  systemPrompt: attachedLocalSessionSystemPrompt(endpoint),
32481
33429
  runtimeDirectory: relayAgentRuntimeDirectory(agentName),
32482
33430
  logsDirectory: relayAgentLogsDirectory(agentName),
32483
- launchArgs: [],
33431
+ launchArgs: attachedCodexEndpointLaunchArgs(endpoint),
32484
33432
  threadId,
32485
- requireExistingThread: Boolean(threadId)
33433
+ requireExistingThread: Boolean(threadId) && !ownsSessionThread
32486
33434
  };
32487
33435
  }
32488
33436
  function buildClaudeEndpointSessionOptions(endpoint) {
@@ -32659,7 +33607,7 @@ function buildScoutReplyContextPrompt(context) {
32659
33607
  ] : [
32660
33608
  "> Do not publish a separate acknowledgement or progress update through Scout for this request.",
32661
33609
  "> Your final assistant message will be delivered back through the Scout broker.",
32662
- "> Do not call `messages_reply`, `scout_reply`, `scout send`, `messages_send`, or `invocations_ask` to answer this request."
33610
+ "> Do not call `messages_reply`, `scout_reply`, `scout send`, `messages_send`, or `ask` to answer this request."
32663
33611
  ];
32664
33612
  const header = [
32665
33613
  "<!-- SCOUT BROKER REPLY MODE -->",
@@ -32770,35 +33718,228 @@ function stripLocalAgentReplyMetadata(body, flightId, asker) {
32770
33718
  async function sendLocalAgentPrompt(agentName, record2, prompt) {
32771
33719
  const harness = normalizeLocalAgentHarness(record2.harness);
32772
33720
  if (harness === "codex") {
32773
- const promptPipe = join18(relayAgentRuntimeDirectory(agentName), "prompt.pipe");
33721
+ const promptPipe = join19(relayAgentRuntimeDirectory(agentName), "prompt.pipe");
32774
33722
  await writeFile7(promptPipe, prompt.trim() + "\x00");
32775
33723
  return;
32776
33724
  }
32777
- sendTmuxPrompt(record2.tmuxSession, prompt, buildTmuxPromptSubmitKeys(harness));
33725
+ await sendTmuxPrompt(record2.tmuxSession, prompt, buildTmuxDispatchStrategy(harness, prompt));
33726
+ }
33727
+ var TMUX_PASTE_DRAIN_MS = 150;
33728
+ var TMUX_VERIFY_FIRST_SAMPLE_MS = 250;
33729
+ var TMUX_VERIFY_SECOND_SAMPLE_MS = 750;
33730
+ var TMUX_VERIFY_RETRY_SAMPLE_MS = 400;
33731
+ var TMUX_CAPTURE_TAIL_LINES = 20;
33732
+ var TMUX_READY_TIMEOUT_MS = 20000;
33733
+ var TMUX_READY_POLL_MS = 250;
33734
+ var TMUX_READY_TAIL_LINES = 80;
33735
+ function buildTmuxDispatchStrategy(harness, prompt) {
33736
+ const promptAbsentFromTail = (paneTail) => !tmuxPaneTailContainsPromptFragment(paneTail, prompt);
33737
+ if (harness === "pi") {
33738
+ return { submit: ["Enter"], verify: promptAbsentFromTail };
33739
+ }
33740
+ return { submit: ["Enter"], verify: promptAbsentFromTail };
32778
33741
  }
32779
- function sendTmuxPrompt(sessionName, prompt, submitKeys = buildTmuxPromptSubmitKeys()) {
33742
+ async function sendTmuxPrompt(sessionName, prompt, strategy) {
33743
+ const effectiveStrategy = strategy ?? buildTmuxDispatchStrategy("claude", prompt);
32780
33744
  const bufferName = `openscout-prompt-${randomUUID2()}`;
33745
+ let bufferOwned = true;
32781
33746
  try {
33747
+ if (effectiveStrategy.pre && effectiveStrategy.pre.length > 0) {
33748
+ execFileSync3("tmux", ["send-keys", "-t", sessionName, ...effectiveStrategy.pre], { stdio: "pipe" });
33749
+ }
32782
33750
  execFileSync3("tmux", ["load-buffer", "-b", bufferName, "-"], {
32783
33751
  stdio: "pipe",
32784
33752
  input: prompt
32785
33753
  });
32786
- execFileSync3("tmux", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName], {
33754
+ execFileSync3("tmux", buildTmuxPasteBufferArgs(bufferName, sessionName), {
32787
33755
  stdio: "pipe"
32788
33756
  });
32789
- execFileSync3("tmux", ["send-keys", "-t", sessionName, ...submitKeys], { stdio: "pipe" });
33757
+ bufferOwned = false;
33758
+ await tmuxDispatchSleep(TMUX_PASTE_DRAIN_MS);
33759
+ execFileSync3("tmux", ["send-keys", "-t", sessionName, ...effectiveStrategy.submit], { stdio: "pipe" });
33760
+ if (await dispatchVerifiedAfter(sessionName, effectiveStrategy, TMUX_VERIFY_FIRST_SAMPLE_MS)) {
33761
+ return { submitted: true, retries: 0 };
33762
+ }
33763
+ if (await dispatchVerifiedAfter(sessionName, effectiveStrategy, TMUX_VERIFY_SECOND_SAMPLE_MS)) {
33764
+ return { submitted: true, retries: 0 };
33765
+ }
33766
+ execFileSync3("tmux", ["send-keys", "-t", sessionName, "Enter"], { stdio: "pipe" });
33767
+ if (await dispatchVerifiedAfter(sessionName, effectiveStrategy, TMUX_VERIFY_RETRY_SAMPLE_MS)) {
33768
+ return { submitted: true, retries: 1 };
33769
+ }
33770
+ throw new DispatchStalledError({
33771
+ sessionName,
33772
+ paneTail: captureTmuxPaneTail(sessionName, TMUX_CAPTURE_TAIL_LINES).slice(0, 2000),
33773
+ retries: 1
33774
+ });
32790
33775
  } catch (error48) {
32791
- try {
32792
- execFileSync3("tmux", ["delete-buffer", "-b", bufferName], { stdio: "pipe" });
32793
- } catch {}
33776
+ if (bufferOwned) {
33777
+ try {
33778
+ execFileSync3("tmux", ["delete-buffer", "-b", bufferName], { stdio: "pipe" });
33779
+ } catch {}
33780
+ }
32794
33781
  throw error48;
32795
33782
  }
32796
33783
  }
32797
- function buildTmuxPromptSubmitKeys(harness = "claude") {
32798
- if (harness === "pi") {
32799
- return ["Enter"];
33784
+ function buildTmuxPasteBufferArgs(bufferName, sessionName) {
33785
+ return ["paste-buffer", "-dpr", "-b", bufferName, "-t", sessionName];
33786
+ }
33787
+ function tmuxDispatchSleep(ms) {
33788
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
33789
+ }
33790
+ async function dispatchVerifiedAfter(sessionName, strategy, delayMs) {
33791
+ await tmuxDispatchSleep(delayMs);
33792
+ const paneTail = captureTmuxPaneTail(sessionName, TMUX_CAPTURE_TAIL_LINES);
33793
+ if (!paneTail) {
33794
+ return true;
32800
33795
  }
32801
- return ["C-[", "Enter"];
33796
+ return strategy.verify(paneTail);
33797
+ }
33798
+ function captureTmuxPaneTail(sessionName, lines) {
33799
+ try {
33800
+ return execFileSync3("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", `-${lines}`, "-E", "-"], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" });
33801
+ } catch {
33802
+ return "";
33803
+ }
33804
+ }
33805
+ async function waitForTmuxHarnessReady(sessionName, harness) {
33806
+ if (harness !== "claude") {
33807
+ return;
33808
+ }
33809
+ const deadline = Date.now() + TMUX_READY_TIMEOUT_MS;
33810
+ let paneTail = "";
33811
+ while (Date.now() < deadline) {
33812
+ if (!isLocalAgentSessionAlive(sessionName)) {
33813
+ throw new Error(`tmux session ${sessionName} exited before Claude Code was ready.`);
33814
+ }
33815
+ paneTail = captureTmuxPaneTail(sessionName, TMUX_READY_TAIL_LINES);
33816
+ if (tmuxPaneTailShowsReadyComposer(paneTail)) {
33817
+ return;
33818
+ }
33819
+ await tmuxDispatchSleep(TMUX_READY_POLL_MS);
33820
+ }
33821
+ const tail = stripTerminalControlSequences(paneTail).trim().split(/\r?\n/).slice(-20).join(`
33822
+ `).trim();
33823
+ throw new Error(`tmux session ${sessionName} did not show a ready Claude Code composer within ${TMUX_READY_TIMEOUT_MS}ms.` + (tail ? `
33824
+ Recent pane tail:
33825
+ ${tail}` : ""));
33826
+ }
33827
+ function tmuxPaneTailShowsReadyComposer(paneTail) {
33828
+ const cleanedTail = stripTerminalControlSequences(paneTail);
33829
+ const lines = cleanedTail.split(/\r?\n/);
33830
+ const anchor = findActiveTmuxComposerAnchor(lines);
33831
+ if (!anchor) {
33832
+ return false;
33833
+ }
33834
+ const afterComposerLines = [];
33835
+ for (const line of lines.slice(anchor.index + 1)) {
33836
+ if (isTmuxComposerBoundary(line)) {
33837
+ break;
33838
+ }
33839
+ afterComposerLines.push(line);
33840
+ }
33841
+ return !tmuxPaneTailShowsHarnessActivity(afterComposerLines.join(`
33842
+ `));
33843
+ }
33844
+ function tmuxPaneTailContainsPromptFragment(paneTail, prompt) {
33845
+ const cleanedTail = stripTerminalControlSequences(paneTail);
33846
+ const composerText = extractActiveTmuxComposerText(cleanedTail);
33847
+ if (composerText !== null) {
33848
+ return textContainsPromptFragment(composerText, prompt);
33849
+ }
33850
+ if (tmuxPaneTailShowsHarnessActivity(cleanedTail)) {
33851
+ return false;
33852
+ }
33853
+ return textContainsPromptFragment(cleanedTail, prompt);
33854
+ }
33855
+ function textContainsPromptFragment(haystack, prompt) {
33856
+ const normalizedTail = haystack.replace(/\s+/g, " ").trim();
33857
+ if (!normalizedTail)
33858
+ return false;
33859
+ const normalizedPrompt = prompt.replace(/\s+/g, " ").trim();
33860
+ if (normalizedPrompt.length < 24) {
33861
+ return normalizedPrompt.length > 0 && normalizedTail.includes(normalizedPrompt);
33862
+ }
33863
+ const midpoint = Math.floor(normalizedPrompt.length / 2);
33864
+ const windows = [
33865
+ normalizedPrompt.slice(0, 40),
33866
+ normalizedPrompt.slice(Math.max(0, midpoint - 20), midpoint + 20),
33867
+ normalizedPrompt.slice(Math.max(0, normalizedPrompt.length - 40))
33868
+ ];
33869
+ return windows.some((w) => w.length >= 24 && normalizedTail.includes(w));
33870
+ }
33871
+ function stripTerminalControlSequences(value) {
33872
+ return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
33873
+ }
33874
+ function findActiveTmuxComposerAnchor(lines) {
33875
+ const inlineComposerIndex = findLastIndex(lines, (line) => /^\s*[\u276F\u203A]\s*/.test(line));
33876
+ const boxedComposerIndex = findLastIndex(lines, (line) => /^\s*[\u2502\u2503]\s*[>\u276F]\s*/.test(line));
33877
+ if (inlineComposerIndex < 0 && boxedComposerIndex < 0) {
33878
+ return null;
33879
+ }
33880
+ if (inlineComposerIndex > boxedComposerIndex) {
33881
+ return { index: inlineComposerIndex, kind: "inline" };
33882
+ }
33883
+ return { index: boxedComposerIndex, kind: "boxed" };
33884
+ }
33885
+ function extractActiveTmuxComposerText(paneTail) {
33886
+ const lines = paneTail.split(/\r?\n/);
33887
+ const anchor = findActiveTmuxComposerAnchor(lines);
33888
+ if (!anchor) {
33889
+ return null;
33890
+ }
33891
+ return anchor.kind === "inline" ? collectInlineComposerText(lines, anchor.index) : collectBoxedComposerText(lines, anchor.index);
33892
+ }
33893
+ function findLastIndex(items, predicate) {
33894
+ for (let index = items.length - 1;index >= 0; index -= 1) {
33895
+ if (predicate(items[index])) {
33896
+ return index;
33897
+ }
33898
+ }
33899
+ return -1;
33900
+ }
33901
+ function collectInlineComposerText(lines, startIndex) {
33902
+ const collected = [lines[startIndex].replace(/^\s*[\u276F\u203A]\s*/, "")];
33903
+ for (let index = startIndex + 1;index < lines.length; index += 1) {
33904
+ const line = lines[index];
33905
+ if (isTmuxComposerBoundary(line)) {
33906
+ break;
33907
+ }
33908
+ collected.push(line);
33909
+ }
33910
+ return collected.join(" ").trim();
33911
+ }
33912
+ function collectBoxedComposerText(lines, startIndex) {
33913
+ const collected = [];
33914
+ for (let index = startIndex;index < lines.length; index += 1) {
33915
+ const line = lines[index];
33916
+ if (index > startIndex && isTmuxComposerBoundary(line)) {
33917
+ break;
33918
+ }
33919
+ if (!/^\s*[\u2502\u2503]/.test(line)) {
33920
+ if (isTmuxComposerBoundary(line)) {
33921
+ break;
33922
+ }
33923
+ collected.push(line);
33924
+ continue;
33925
+ }
33926
+ const content = line.replace(/^\s*[\u2502\u2503]\s*/, "").replace(/\s*[\u2502\u2503]\s*$/, "").replace(index === startIndex ? /^[>\u276F]\s*/ : /^/, "");
33927
+ collected.push(content);
33928
+ }
33929
+ return collected.join(" ").trim();
33930
+ }
33931
+ function isTmuxComposerBoundary(line) {
33932
+ const trimmed = line.trim();
33933
+ if (!trimmed) {
33934
+ return false;
33935
+ }
33936
+ if (/^[\u2500\u2501\u2550\u256D\u256E\u2570\u256F\u250C\u2510\u2514\u2518\u2554\u2557\u255A\u255D\u255F\u2562\u2560\u2563\u256A\u256B\u256C\u2569\u2566\u2564\u2567\u254C\u254D\u254E\u254F\s]+$/.test(trimmed)) {
33937
+ return true;
33938
+ }
33939
+ return /^--\s*(?:INSERT|NORMAL)\s*--/.test(trimmed) || /^(?:Opus|Sonnet|Haiku|Claude|Codex|GPT)\b/.test(trimmed);
33940
+ }
33941
+ function tmuxPaneTailShowsHarnessActivity(paneTail) {
33942
+ return /(?:^|\n)\s*(?:[\u23FA\u25CF\u273D\u2722\u273B\u23BF]|Bash\(|Read\(|Edit\(|Write\(|Grep\(|Glob\(|TodoWrite\()/.test(paneTail);
32802
33943
  }
32803
33944
  function shellQuoteArguments(args) {
32804
33945
  return args.map((arg) => JSON.stringify(arg)).join(" ");
@@ -32810,10 +33951,10 @@ function buildLocalAgentLaunchCommand(agentName, record2, projectPath, promptFil
32810
33951
  const harness = normalizeLocalAgentHarness(record2.harness);
32811
33952
  const extraArgs = shellQuoteArguments(harness === "claude" ? normalizeClaudeRuntimeLaunchArgs(record2.launchArgs) : normalizeLaunchArgsForHarness(harness, record2.launchArgs));
32812
33953
  if (harness === "codex") {
32813
- return `exec bash ${JSON.stringify(workerScript ?? join18(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
33954
+ return `exec bash ${JSON.stringify(workerScript ?? join19(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
32814
33955
  }
32815
33956
  if (harness === "pi") {
32816
- const sessionDir = join18(relayAgentRuntimeDirectory(agentName), "pi-sessions");
33957
+ const sessionDir = join19(relayAgentRuntimeDirectory(agentName), "pi-sessions");
32817
33958
  return [
32818
33959
  "pi",
32819
33960
  `--append-system-prompt "$(cat ${JSON.stringify(promptFile)})"`,
@@ -32833,6 +33974,11 @@ function buildLocalAgentLaunchCommand(agentName, record2, projectPath, promptFil
32833
33974
  function buildTmuxLaunchShellCommand(launchScript) {
32834
33975
  return `exec bash ${JSON.stringify(launchScript)}`;
32835
33976
  }
33977
+ function killAgentSession(sessionName) {
33978
+ try {
33979
+ execSync(`tmux kill-session -t ${JSON.stringify(sessionName)}`, { stdio: "pipe" });
33980
+ } catch {}
33981
+ }
32836
33982
  function areHarnessBinariesAvailable(record2) {
32837
33983
  const harness = normalizeLocalAgentHarness(record2.harness);
32838
33984
  const binaries = new Set;
@@ -32878,7 +34024,7 @@ async function ensureLocalAgentOnline(agentName, record2) {
32878
34024
  await mkdir7(agentRuntimeDir, { recursive: true });
32879
34025
  await mkdir7(logsDir, { recursive: true });
32880
34026
  if (normalizedRecord.transport === "codex_app_server") {
32881
- await writeFile7(join18(agentRuntimeDir, "prompt.txt"), systemPrompt);
34027
+ await writeFile7(join19(agentRuntimeDir, "prompt.txt"), systemPrompt);
32882
34028
  await ensureCodexAppServerAgentOnline(buildCodexAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
32883
34029
  const registry3 = await readLocalAgentRegistry();
32884
34030
  registry3[agentName] = {
@@ -32890,7 +34036,7 @@ async function ensureLocalAgentOnline(agentName, record2) {
32890
34036
  return registry3[agentName];
32891
34037
  }
32892
34038
  if (normalizedRecord.transport === "claude_stream_json") {
32893
- await writeFile7(join18(agentRuntimeDir, "prompt.txt"), systemPrompt);
34039
+ await writeFile7(join19(agentRuntimeDir, "prompt.txt"), systemPrompt);
32894
34040
  await ensureClaudeStreamJsonAgentOnline(buildClaudeAgentSessionOptions(agentName, normalizedRecord, systemPrompt));
32895
34041
  const registry3 = await readLocalAgentRegistry();
32896
34042
  registry3[agentName] = {
@@ -32903,17 +34049,17 @@ async function ensureLocalAgentOnline(agentName, record2) {
32903
34049
  }
32904
34050
  const initialMessage = buildLocalAgentInitialMessage(projectName, agentName);
32905
34051
  const bootstrapPrompt = buildLocalAgentBootstrapPrompt(normalizeLocalAgentHarness(normalizedRecord.harness), systemPrompt, initialMessage);
32906
- const queueDirectory = join18(agentRuntimeDir, "queue");
32907
- const processingDirectory = join18(queueDirectory, "processing");
32908
- const processedDirectory = join18(queueDirectory, "processed");
32909
- const promptFile = join18(agentRuntimeDir, "prompt.txt");
32910
- const initialFile = join18(agentRuntimeDir, "initial.txt");
32911
- const launchScript = join18(agentRuntimeDir, "launch.sh");
32912
- const workerScript = join18(agentRuntimeDir, "codex-worker.sh");
32913
- const codexSessionIdFile = join18(agentRuntimeDir, "codex-session-id.txt");
32914
- const stateFile = join18(agentRuntimeDir, "state.json");
32915
- const stdoutLogFile = join18(logsDir, "stdout.log");
32916
- const stderrLogFile = join18(logsDir, "stderr.log");
34052
+ const queueDirectory = join19(agentRuntimeDir, "queue");
34053
+ const processingDirectory = join19(queueDirectory, "processing");
34054
+ const processedDirectory = join19(queueDirectory, "processed");
34055
+ const promptFile = join19(agentRuntimeDir, "prompt.txt");
34056
+ const initialFile = join19(agentRuntimeDir, "initial.txt");
34057
+ const launchScript = join19(agentRuntimeDir, "launch.sh");
34058
+ const workerScript = join19(agentRuntimeDir, "codex-worker.sh");
34059
+ const codexSessionIdFile = join19(agentRuntimeDir, "codex-session-id.txt");
34060
+ const stateFile = join19(agentRuntimeDir, "state.json");
34061
+ const stdoutLogFile = join19(logsDir, "stdout.log");
34062
+ const stderrLogFile = join19(logsDir, "stderr.log");
32917
34063
  await writeFile7(promptFile, systemPrompt);
32918
34064
  await writeFile7(initialFile, bootstrapPrompt);
32919
34065
  await writeFile7(stderrLogFile, "");
@@ -32978,11 +34124,17 @@ async function ensureLocalAgentOnline(agentName, record2) {
32978
34124
  await new Promise((resolve8) => setTimeout(resolve8, 100));
32979
34125
  }
32980
34126
  if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
32981
- const stderrTail = existsSync13(stderrLogFile) ? readFileSync6(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
34127
+ const stderrTail = existsSync14(stderrLogFile) ? readFileSync7(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
32982
34128
  `).trim() : "";
32983
34129
  throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
32984
34130
  ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
32985
34131
  }
34132
+ try {
34133
+ await waitForTmuxHarnessReady(normalizedRecord.tmuxSession, normalizeLocalAgentHarness(normalizedRecord.harness));
34134
+ } catch (error48) {
34135
+ killAgentSession(normalizedRecord.tmuxSession);
34136
+ throw error48;
34137
+ }
32986
34138
  const registry2 = await readLocalAgentRegistry();
32987
34139
  registry2[agentName] = {
32988
34140
  ...normalizedRecord,
@@ -32992,12 +34144,249 @@ ${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
32992
34144
  await writeLocalAgentRegistry(registry2);
32993
34145
  return registry2[agentName];
32994
34146
  }
34147
+ async function startLocalAgent(input) {
34148
+ const projectPath = normalizeProjectPath2(input.projectPath);
34149
+ const preferredHarness = input.harness ? normalizeLocalAgentHarness(input.harness) : undefined;
34150
+ const currentDirectory = input.currentDirectory ?? projectPath;
34151
+ const effectiveCwd = input.cwdOverride ? normalizeProjectPath2(input.cwdOverride) : undefined;
34152
+ const shouldEnsureOnline = input.ensureOnline !== false;
34153
+ const requestedPermissionProfile = parseScoutPermissionProfile(input.permissionProfile);
34154
+ const cardLifecycle = normalizeLocalAgentCardLifecycle(input.card);
34155
+ const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
34156
+ if (input.agentName?.trim() && !requestedDefinitionId) {
34157
+ throw new Error(`Invalid agent name "${input.agentName}".`);
34158
+ }
34159
+ const overrides = await readRelayAgentOverrides();
34160
+ const findMatchForRoot = (root) => {
34161
+ let fallback = null;
34162
+ for (const [id, override] of Object.entries(overrides)) {
34163
+ if (BUILT_IN_AGENT_DEFINITION_IDS.has(id))
34164
+ continue;
34165
+ if (!override.projectRoot)
34166
+ continue;
34167
+ if (normalizeProjectPath2(override.projectRoot) !== root)
34168
+ continue;
34169
+ if (requestedDefinitionId && override.definitionId === requestedDefinitionId) {
34170
+ return { agentId: id, override };
34171
+ }
34172
+ if (!fallback)
34173
+ fallback = { agentId: id, override };
34174
+ }
34175
+ return fallback;
34176
+ };
34177
+ let matched = findMatchForRoot(projectPath);
34178
+ let projectRoot = projectPath;
34179
+ if (!matched) {
34180
+ const resolved = await findNearestProjectRoot(projectPath);
34181
+ if (resolved && resolved !== projectPath) {
34182
+ projectRoot = normalizeProjectPath2(resolved);
34183
+ matched = findMatchForRoot(projectRoot);
34184
+ }
34185
+ }
34186
+ let matchingAgentId = matched?.agentId;
34187
+ let matchingOverride = matched?.override;
34188
+ let coldProjectConfigPath = null;
34189
+ let targetAgentId;
34190
+ let coldProjectConfig = null;
34191
+ if (!matchingOverride) {
34192
+ const ensuredProject = await ensureProjectConfigForDirectory(projectPath);
34193
+ projectRoot = ensuredProject.projectRoot ?? projectPath;
34194
+ coldProjectConfigPath = ensuredProject.projectConfigPath ?? null;
34195
+ coldProjectConfig = ensuredProject.config ?? null;
34196
+ matched = findMatchForRoot(projectRoot);
34197
+ matchingAgentId = matched?.agentId;
34198
+ matchingOverride = matched?.override;
34199
+ }
34200
+ if (!matchingOverride) {
34201
+ const configDefinitionId = coldProjectConfig?.agent?.id?.trim() ? normalizeAgentSelectorSegment(coldProjectConfig.agent.id.trim()) : "";
34202
+ const definitionId = requestedDefinitionId || configDefinitionId || normalizeAgentSelectorSegment(basename9(projectRoot)) || "agent";
34203
+ const configDisplayName = coldProjectConfig?.agent?.displayName?.trim() || "";
34204
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(definitionId);
34205
+ const effectiveDisplayName = input.displayName || operatorAugmentDefaults?.displayName || configDisplayName || titleCaseLocalAgentName(definitionId);
34206
+ const instance = buildRelayAgentInstance(definitionId, projectRoot);
34207
+ const configDefaultHarness = coldProjectConfig?.agent?.runtime?.defaultHarness;
34208
+ const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
34209
+ const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
34210
+ const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
34211
+ const launchArgs = applyRequestedRuntimeOptionsToLaunchArgs(effectiveHarness, [], {
34212
+ model: input.model,
34213
+ reasoningEffort: input.reasoningEffort
34214
+ });
34215
+ const existingForInstance = overrides[instance.id];
34216
+ if (existingForInstance && normalizeProjectPath2(existingForInstance.projectRoot) !== projectRoot) {
34217
+ throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
34218
+ }
34219
+ overrides[instance.id] = {
34220
+ agentId: instance.id,
34221
+ definitionId,
34222
+ displayName: effectiveDisplayName,
34223
+ projectName: basename9(projectRoot),
34224
+ projectRoot,
34225
+ projectConfigPath: coldProjectConfigPath,
34226
+ source: "manual",
34227
+ startedAt: nowSeconds2(),
34228
+ ...operatorAugmentDefaults ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
34229
+ launchArgs,
34230
+ defaultHarness: effectiveHarness,
34231
+ ...cardLifecycle ? { card: cardLifecycle } : {},
34232
+ harnessProfiles: {
34233
+ [effectiveHarness]: {
34234
+ cwd: effectiveCwd ?? projectRoot,
34235
+ transport,
34236
+ sessionId,
34237
+ launchArgs,
34238
+ ...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
34239
+ }
34240
+ },
34241
+ runtime: {
34242
+ cwd: effectiveCwd ?? projectRoot,
34243
+ harness: effectiveHarness,
34244
+ transport,
34245
+ sessionId,
34246
+ wakePolicy: "on_demand"
34247
+ }
34248
+ };
34249
+ await writeRelayAgentOverrides(overrides);
34250
+ targetAgentId = instance.id;
34251
+ } else if (requestedDefinitionId && requestedDefinitionId !== matchingOverride.definitionId) {
34252
+ const matchingProjectRoot = normalizeProjectPath2(matchingOverride.projectRoot);
34253
+ const instance = buildRelayAgentInstance(requestedDefinitionId, matchingProjectRoot);
34254
+ const existingForInstance = overrides[instance.id];
34255
+ if (existingForInstance && normalizeProjectPath2(existingForInstance.projectRoot) !== matchingProjectRoot) {
34256
+ throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
34257
+ }
34258
+ const resolvedHarness = preferredHarness ?? matchingOverride.runtime?.harness;
34259
+ const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
34260
+ const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
34261
+ const nextSessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${nextHarness}`);
34262
+ const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), {
34263
+ model: input.model,
34264
+ reasoningEffort: input.reasoningEffort
34265
+ });
34266
+ const nextTransport = normalizeLocalAgentTransport(preferredHarness ? undefined : matchingOverride.runtime?.transport, nextHarness);
34267
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(requestedDefinitionId);
34268
+ overrides[instance.id] = {
34269
+ agentId: instance.id,
34270
+ definitionId: requestedDefinitionId,
34271
+ displayName: input.displayName || operatorAugmentDefaults?.displayName || titleCaseLocalAgentName(requestedDefinitionId),
34272
+ projectName: matchingOverride.projectName ?? basename9(matchingProjectRoot),
34273
+ projectRoot: matchingProjectRoot,
34274
+ projectConfigPath: null,
34275
+ source: "manual",
34276
+ startedAt: matchingOverride.startedAt ?? nowSeconds2(),
34277
+ ...operatorAugmentDefaults ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
34278
+ launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
34279
+ capabilities: matchingOverride.capabilities,
34280
+ defaultHarness: nextDefaultHarness,
34281
+ ...cardLifecycle ? { card: cardLifecycle } : {},
34282
+ harnessProfiles: {
34283
+ ...matchingOverride.harnessProfiles ?? {},
34284
+ [nextHarness]: {
34285
+ ...overrideHarnessProfile(matchingOverride, nextHarness),
34286
+ transport: nextTransport,
34287
+ sessionId: nextSessionId,
34288
+ launchArgs: nextLaunchArgs,
34289
+ ...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
34290
+ }
34291
+ },
34292
+ runtime: {
34293
+ cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
34294
+ harness: nextHarness,
34295
+ transport: nextTransport,
34296
+ sessionId: nextSessionId,
34297
+ wakePolicy: "on_demand"
34298
+ }
34299
+ };
34300
+ await writeRelayAgentOverrides(overrides);
34301
+ targetAgentId = instance.id;
34302
+ } else {
34303
+ const operatorAugmentDefaults = operatorAugmentDefaultsForDefinitionId(matchingOverride.definitionId ?? requestedDefinitionId);
34304
+ const shouldApplyOperatorAugmentPrompt = Boolean(operatorAugmentDefaults && !matchingOverride.systemPrompt?.trim());
34305
+ if (input.model || input.reasoningEffort || requestedPermissionProfile || cardLifecycle || shouldApplyOperatorAugmentPrompt) {
34306
+ const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
34307
+ const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), {
34308
+ model: input.model,
34309
+ reasoningEffort: input.reasoningEffort
34310
+ });
34311
+ overrides[matchingAgentId] = {
34312
+ ...matchingOverride,
34313
+ ...cardLifecycle ? { card: cardLifecycle } : {},
34314
+ ...shouldApplyOperatorAugmentPrompt ? { systemPrompt: operatorAugmentDefaults.systemPrompt } : {},
34315
+ launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
34316
+ harnessProfiles: {
34317
+ ...matchingOverride.harnessProfiles ?? {},
34318
+ [existingHarness]: {
34319
+ ...overrideHarnessProfile(matchingOverride, existingHarness),
34320
+ launchArgs: nextLaunchArgs,
34321
+ ...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
34322
+ }
34323
+ }
34324
+ };
34325
+ await writeRelayAgentOverrides(overrides);
34326
+ }
34327
+ targetAgentId = matchingAgentId;
34328
+ }
34329
+ if (shouldEnsureOnline) {
34330
+ await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
34331
+ includeDiscovered: false,
34332
+ currentDirectory,
34333
+ ensureCurrentProjectConfig: false,
34334
+ harness: preferredHarness
34335
+ });
34336
+ }
34337
+ const finalOverrides = await readRelayAgentOverrides();
34338
+ const finalOverride = finalOverrides[targetAgentId];
34339
+ if (!finalOverride) {
34340
+ throw new Error(`Agent ${targetAgentId} did not register successfully.`);
34341
+ }
34342
+ const record2 = localAgentRecordFromRelayAgentOverride(targetAgentId, finalOverride);
34343
+ return localAgentStatusFromRecord(targetAgentId, record2, localAgentStatusSource(targetAgentId, finalOverrides));
34344
+ }
34345
+ async function stopLocalAgent(agentId) {
34346
+ const [registry2, overrides] = await Promise.all([
34347
+ readLocalAgentRegistry(),
34348
+ readRelayAgentOverrides()
34349
+ ]);
34350
+ const record2 = registry2[agentId];
34351
+ if (!record2) {
34352
+ return null;
34353
+ }
34354
+ const normalizedRecord = normalizeLocalAgentRecord(agentId, record2);
34355
+ const sessionsToStop = new Set([normalizedRecord.tmuxSession]);
34356
+ if (normalizedRecord.transport === "codex_app_server") {
34357
+ for (const sessionName of sessionsToStop) {
34358
+ await shutdownCodexAppServerAgent({
34359
+ ...buildCodexAgentSessionOptions(agentId, normalizedRecord),
34360
+ sessionId: sessionName
34361
+ }, {
34362
+ resetThread: true
34363
+ });
34364
+ }
34365
+ } else if (normalizedRecord.transport === "claude_stream_json") {
34366
+ for (const sessionName of sessionsToStop) {
34367
+ await shutdownClaudeStreamJsonAgent({
34368
+ ...buildClaudeAgentSessionOptions(agentId, normalizedRecord),
34369
+ sessionId: sessionName
34370
+ }, {
34371
+ resetSession: true
34372
+ });
34373
+ }
34374
+ } else {
34375
+ for (const sessionName of sessionsToStop) {
34376
+ killAgentSession(sessionName);
34377
+ }
34378
+ }
34379
+ return {
34380
+ ...localAgentStatusFromRecord(agentId, normalizedRecord, localAgentStatusSource(agentId, overrides)),
34381
+ isOnline: false
34382
+ };
34383
+ }
32995
34384
  function readPersistedClaudeSessionId(agentName) {
32996
34385
  try {
32997
34386
  const runtimeDir = relayAgentRuntimeDirectory(agentName);
32998
- const catalogPath = join18(runtimeDir, "session-catalog.json");
32999
- if (existsSync13(catalogPath)) {
33000
- const raw = readFileSync6(catalogPath, "utf8").trim();
34387
+ const catalogPath = join19(runtimeDir, "session-catalog.json");
34388
+ if (existsSync14(catalogPath)) {
34389
+ const raw = readFileSync7(catalogPath, "utf8").trim();
33001
34390
  if (raw) {
33002
34391
  const catalog = JSON.parse(raw);
33003
34392
  if (typeof catalog.activeSessionId === "string" && catalog.activeSessionId.trim()) {
@@ -33005,9 +34394,9 @@ function readPersistedClaudeSessionId(agentName) {
33005
34394
  }
33006
34395
  }
33007
34396
  }
33008
- const legacyPath = join18(runtimeDir, "claude-session-id.txt");
33009
- if (existsSync13(legacyPath)) {
33010
- const value = readFileSync6(legacyPath, "utf8").trim();
34397
+ const legacyPath = join19(runtimeDir, "claude-session-id.txt");
34398
+ if (existsSync14(legacyPath)) {
34399
+ const value = readFileSync7(legacyPath, "utf8").trim();
33011
34400
  return value || null;
33012
34401
  }
33013
34402
  return null;
@@ -33019,6 +34408,7 @@ function buildLocalAgentBinding(agentId, record2, alive, nodeId, source) {
33019
34408
  const normalizedRecord = normalizeLocalAgentRecord(agentId, record2);
33020
34409
  const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
33021
34410
  const codexPermissionPosture = normalizeLocalAgentHarness(normalizedRecord.harness) === "codex" ? compileCodexPermissionProfile(normalizedRecord.permissionProfile) : null;
34411
+ const cardLifecycle = normalizeLocalAgentCardLifecycle(normalizedRecord.card);
33022
34412
  const definitionId = normalizedRecord.definitionId ?? agentId;
33023
34413
  const displayName = titleCaseLocalAgentName(definitionId);
33024
34414
  const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
@@ -33046,6 +34436,7 @@ function buildLocalAgentBinding(agentId, record2, alive, nodeId, source) {
33046
34436
  workspaceQualifier: instance.workspaceQualifier,
33047
34437
  branch: instance.branch,
33048
34438
  ...configuredModel ? { model: configuredModel } : {},
34439
+ ...cardLifecycle ? { cardLifecycle } : {},
33049
34440
  ...codexPermissionPosture ? {
33050
34441
  permissionProfile: codexPermissionPosture.profile,
33051
34442
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -33081,6 +34472,7 @@ function buildLocalAgentBinding(agentId, record2, alive, nodeId, source) {
33081
34472
  workspaceQualifier: instance.workspaceQualifier,
33082
34473
  branch: instance.branch,
33083
34474
  ...configuredModel ? { model: configuredModel } : {},
34475
+ ...cardLifecycle ? { cardLifecycle } : {},
33084
34476
  ...codexPermissionPosture ? {
33085
34477
  permissionProfile: codexPermissionPosture.profile,
33086
34478
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -33121,6 +34513,7 @@ function buildLocalAgentBinding(agentId, record2, alive, nodeId, source) {
33121
34513
  workspaceQualifier: instance.workspaceQualifier,
33122
34514
  branch: instance.branch,
33123
34515
  ...configuredModel ? { model: configuredModel } : {},
34516
+ ...cardLifecycle ? { cardLifecycle } : {},
33124
34517
  ...codexPermissionPosture ? {
33125
34518
  permissionProfile: codexPermissionPosture.profile,
33126
34519
  approvalPolicy: codexPermissionPosture.approvalPolicy,
@@ -33212,7 +34605,8 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
33212
34605
  replyContext
33213
34606
  });
33214
34607
  return {
33215
- output: result.output
34608
+ output: result.output,
34609
+ externalSessionId: result.threadId
33216
34610
  };
33217
34611
  }
33218
34612
  if (!existing && endpoint.transport === "claude_stream_json") {
@@ -33223,7 +34617,8 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
33223
34617
  timeoutMs: invocation.timeoutMs
33224
34618
  });
33225
34619
  return {
33226
- output: result.output
34620
+ output: result.output,
34621
+ externalSessionId: result.sessionId
33227
34622
  };
33228
34623
  }
33229
34624
  const projectRoot = endpoint.projectRoot ?? endpoint.cwd;
@@ -33262,7 +34657,8 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
33262
34657
  replyContext
33263
34658
  });
33264
34659
  return {
33265
- output: result.output
34660
+ output: result.output,
34661
+ externalSessionId: result.threadId
33266
34662
  };
33267
34663
  }
33268
34664
  if (onlineRecord.transport === "claude_stream_json") {
@@ -33272,7 +34668,8 @@ async function invokeLocalAgentEndpoint(endpoint, invocation) {
33272
34668
  timeoutMs: invocation.timeoutMs
33273
34669
  });
33274
34670
  return {
33275
- output: result.output
34671
+ output: result.output,
34672
+ externalSessionId: result.sessionId
33276
34673
  };
33277
34674
  }
33278
34675
  const flightId = createLocalAgentFlightId();
@@ -33361,6 +34758,7 @@ function buildScoutAgentCard(binding, options = {}) {
33361
34758
  const supportedInterfaces = metadataRecordArray(binding.agent.metadata, "supportedInterfaces");
33362
34759
  const securitySchemes = metadataRecord3(binding.agent.metadata, "securitySchemes");
33363
34760
  const securityRequirements = metadataStringMatrix(binding.agent.metadata, "securityRequirements");
34761
+ const lifecycle2 = options.lifecycle ?? metadataRecord3(binding.agent.metadata, "cardLifecycle");
33364
34762
  return {
33365
34763
  id: binding.agent.id,
33366
34764
  agentId: binding.agent.id,
@@ -33390,6 +34788,7 @@ function buildScoutAgentCard(binding, options = {}) {
33390
34788
  ...options.createdById?.trim() ? { createdById: options.createdById.trim() } : {},
33391
34789
  brokerRegistered: options.brokerRegistered ?? false,
33392
34790
  ...options.inboxConversationId?.trim() ? { inboxConversationId: options.inboxConversationId.trim() } : {},
34791
+ ...lifecycle2 ? { lifecycle: lifecycle2 } : {},
33393
34792
  returnAddress: buildScoutReturnAddress({
33394
34793
  actorId: binding.agent.id,
33395
34794
  handle,
@@ -33407,7 +34806,8 @@ function buildScoutAgentCard(binding, options = {}) {
33407
34806
  actorId: binding.actor.id,
33408
34807
  endpointId: binding.endpoint.id,
33409
34808
  wakePolicy: binding.agent.wakePolicy,
33410
- ...model ? { model } : {}
34809
+ ...model ? { model } : {},
34810
+ ...lifecycle2 ? { cardLifecycle: lifecycle2 } : {}
33411
34811
  }
33412
34812
  };
33413
34813
  }
@@ -33471,9 +34871,9 @@ function upsertScoutAgentCardFromInput(runtime, input) {
33471
34871
  }
33472
34872
 
33473
34873
  // packages/runtime/src/pairing-session-agents.ts
33474
- import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
33475
- import { basename as basename10, join as join19 } from "path";
33476
- import { homedir as homedir14 } from "os";
34874
+ import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
34875
+ import { basename as basename10, join as join20 } from "path";
34876
+ import { homedir as homedir15 } from "os";
33477
34877
  var DEFAULT_PAIRING_PORT = 7888;
33478
34878
  var PAIRING_CONNECT_TIMEOUT_MS = 1500;
33479
34879
  var PAIRING_REQUEST_TIMEOUT_MS = 1e4;
@@ -33481,18 +34881,18 @@ var DEFAULT_PAIRING_POLL_INTERVAL_MS = 350;
33481
34881
  var DEFAULT_PAIRING_INVOCATION_TIMEOUT_MS = 45000;
33482
34882
  var MAX_SYNTHETIC_OUTPUT_CHARS = 1200;
33483
34883
  function pairingPaths() {
33484
- const rootDir = join19(homedir14(), ".scout", "pairing");
34884
+ const rootDir = join20(homedir15(), ".scout", "pairing");
33485
34885
  return {
33486
- configPath: join19(rootDir, "config.json"),
33487
- runtimeStatePath: join19(rootDir, "runtime.json")
34886
+ configPath: join20(rootDir, "config.json"),
34887
+ runtimeStatePath: join20(rootDir, "runtime.json")
33488
34888
  };
33489
34889
  }
33490
34890
  function readJsonFile2(filePath) {
33491
- if (!existsSync14(filePath)) {
34891
+ if (!existsSync15(filePath)) {
33492
34892
  return null;
33493
34893
  }
33494
34894
  try {
33495
- return JSON.parse(readFileSync7(filePath, "utf8"));
34895
+ return JSON.parse(readFileSync8(filePath, "utf8"));
33496
34896
  } catch {
33497
34897
  return null;
33498
34898
  }
@@ -33502,7 +34902,7 @@ function resolvePairingBridgePort(options = {}) {
33502
34902
  return options.port;
33503
34903
  }
33504
34904
  const { configPath, runtimeStatePath } = pairingPaths();
33505
- const configExists = existsSync14(configPath);
34905
+ const configExists = existsSync15(configPath);
33506
34906
  const runtimeSnapshot = readJsonFile2(runtimeStatePath);
33507
34907
  if (!configExists && !runtimeSnapshot) {
33508
34908
  return null;
@@ -34043,7 +35443,7 @@ import { mkdirSync as mkdirSync5 } from "fs";
34043
35443
  import { dirname as dirname12 } from "path";
34044
35444
  import { Database as Database2 } from "bun:sqlite";
34045
35445
 
34046
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/entity.js
35446
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/entity.js
34047
35447
  var entityKind = Symbol.for("drizzle:entityKind");
34048
35448
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
34049
35449
  function is(value, type) {
@@ -34068,7 +35468,7 @@ function is(value, type) {
34068
35468
  return false;
34069
35469
  }
34070
35470
 
34071
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column.js
35471
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/column.js
34072
35472
  class Column {
34073
35473
  constructor(table, config2) {
34074
35474
  this.table = table;
@@ -34118,7 +35518,7 @@ class Column {
34118
35518
  }
34119
35519
  }
34120
35520
 
34121
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column-builder.js
35521
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/column-builder.js
34122
35522
  class ColumnBuilder {
34123
35523
  static [entityKind] = "ColumnBuilder";
34124
35524
  config;
@@ -34174,20 +35574,20 @@ class ColumnBuilder {
34174
35574
  }
34175
35575
  }
34176
35576
 
34177
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.utils.js
35577
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/table.utils.js
34178
35578
  var TableName = Symbol.for("drizzle:Name");
34179
35579
 
34180
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing-utils.js
35580
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/tracing-utils.js
34181
35581
  function iife(fn, ...args) {
34182
35582
  return fn(...args);
34183
35583
  }
34184
35584
 
34185
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/unique-constraint.js
35585
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/unique-constraint.js
34186
35586
  function uniqueKeyName(table, columns) {
34187
35587
  return `${table[TableName]}_${columns.join("_")}_unique`;
34188
35588
  }
34189
35589
 
34190
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/common.js
35590
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/columns/common.js
34191
35591
  class PgColumn extends Column {
34192
35592
  constructor(table, config2) {
34193
35593
  if (!config2.uniqueName) {
@@ -34236,7 +35636,7 @@ class ExtraConfigColumn extends PgColumn {
34236
35636
  }
34237
35637
  }
34238
35638
 
34239
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/enum.js
35639
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/columns/enum.js
34240
35640
  class PgEnumObjectColumn extends PgColumn {
34241
35641
  static [entityKind] = "PgEnumObjectColumn";
34242
35642
  enum;
@@ -34266,7 +35666,7 @@ class PgEnumColumn extends PgColumn {
34266
35666
  }
34267
35667
  }
34268
35668
 
34269
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/subquery.js
35669
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/subquery.js
34270
35670
  class Subquery {
34271
35671
  static [entityKind] = "Subquery";
34272
35672
  constructor(sql, fields, alias, isWith = false, usedTables = []) {
@@ -34285,10 +35685,10 @@ class WithSubquery extends Subquery {
34285
35685
  static [entityKind] = "WithSubquery";
34286
35686
  }
34287
35687
 
34288
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/version.js
35688
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/version.js
34289
35689
  var version2 = "0.45.2";
34290
35690
 
34291
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing.js
35691
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/tracing.js
34292
35692
  var otel;
34293
35693
  var rawTracer;
34294
35694
  var tracer = {
@@ -34315,10 +35715,10 @@ var tracer = {
34315
35715
  }
34316
35716
  };
34317
35717
 
34318
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/view-common.js
35718
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/view-common.js
34319
35719
  var ViewBaseConfig = Symbol.for("drizzle:ViewBaseConfig");
34320
35720
 
34321
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.js
35721
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/table.js
34322
35722
  var Schema = Symbol.for("drizzle:Schema");
34323
35723
  var Columns = Symbol.for("drizzle:Columns");
34324
35724
  var ExtraConfigColumns = Symbol.for("drizzle:ExtraConfigColumns");
@@ -34362,7 +35762,7 @@ function getTableUniqueName(table) {
34362
35762
  return `${table[Schema] ?? "public"}.${table[TableName]}`;
34363
35763
  }
34364
35764
 
34365
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/sql.js
35765
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/sql.js
34366
35766
  function isSQLWrapper(value) {
34367
35767
  return value !== null && value !== undefined && typeof value.getSQL === "function";
34368
35768
  }
@@ -34642,7 +36042,7 @@ function sql(strings, ...params) {
34642
36042
  return new SQL([new StringChunk(str)]);
34643
36043
  }
34644
36044
  sql2.raw = raw;
34645
- function join20(chunks, separator) {
36045
+ function join21(chunks, separator) {
34646
36046
  const result = [];
34647
36047
  for (const [i, chunk] of chunks.entries()) {
34648
36048
  if (i > 0 && separator !== undefined) {
@@ -34652,7 +36052,7 @@ function sql(strings, ...params) {
34652
36052
  }
34653
36053
  return new SQL(result);
34654
36054
  }
34655
- sql2.join = join20;
36055
+ sql2.join = join21;
34656
36056
  function identifier(value) {
34657
36057
  return new Name(value);
34658
36058
  }
@@ -34742,7 +36142,7 @@ Subquery.prototype.getSQL = function() {
34742
36142
  return new SQL([this]);
34743
36143
  };
34744
36144
 
34745
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/alias.js
36145
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/alias.js
34746
36146
  class ColumnAliasProxyHandler {
34747
36147
  constructor(table) {
34748
36148
  this.table = table;
@@ -34821,7 +36221,7 @@ function mapColumnsInSQLToAlias(query, alias) {
34821
36221
  }));
34822
36222
  }
34823
36223
 
34824
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/errors.js
36224
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/errors.js
34825
36225
  class DrizzleError extends Error {
34826
36226
  static [entityKind] = "DrizzleError";
34827
36227
  constructor({ message, cause }) {
@@ -34851,7 +36251,7 @@ class TransactionRollbackError extends DrizzleError {
34851
36251
  }
34852
36252
  }
34853
36253
 
34854
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/logger.js
36254
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/logger.js
34855
36255
  class ConsoleLogWriter {
34856
36256
  static [entityKind] = "ConsoleLogWriter";
34857
36257
  write(message) {
@@ -34883,7 +36283,7 @@ class NoopLogger {
34883
36283
  logQuery() {}
34884
36284
  }
34885
36285
 
34886
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/query-promise.js
36286
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/query-promise.js
34887
36287
  class QueryPromise {
34888
36288
  static [entityKind] = "QueryPromise";
34889
36289
  [Symbol.toStringTag] = "QueryPromise";
@@ -34904,7 +36304,7 @@ class QueryPromise {
34904
36304
  }
34905
36305
  }
34906
36306
 
34907
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/utils.js
36307
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/utils.js
34908
36308
  function mapResultRow(columns, row, joinsNotNullableMap) {
34909
36309
  const nullifyMap = {};
34910
36310
  const result = columns.reduce((result2, { path, field }, columnIndex) => {
@@ -35058,7 +36458,7 @@ function isConfig(data) {
35058
36458
  }
35059
36459
  var textDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder;
35060
36460
 
35061
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/table.js
36461
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/table.js
35062
36462
  var InlineForeignKeys = Symbol.for("drizzle:PgInlineForeignKeys");
35063
36463
  var EnableRLS = Symbol.for("drizzle:EnableRLS");
35064
36464
 
@@ -35074,7 +36474,7 @@ class PgTable extends Table {
35074
36474
  [Table.Symbol.ExtraConfigColumns] = {};
35075
36475
  }
35076
36476
 
35077
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/primary-keys.js
36477
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/pg-core/primary-keys.js
35078
36478
  class PrimaryKeyBuilder {
35079
36479
  static [entityKind] = "PgPrimaryKeyBuilder";
35080
36480
  columns;
@@ -35102,7 +36502,7 @@ class PrimaryKey {
35102
36502
  }
35103
36503
  }
35104
36504
 
35105
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/expressions/conditions.js
36505
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/expressions/conditions.js
35106
36506
  function bindIfParam(value, column) {
35107
36507
  if (isDriverValueEncoder(column) && !isSQLWrapper(value) && !is(value, Param) && !is(value, Placeholder) && !is(value, Column) && !is(value, Table) && !is(value, View)) {
35108
36508
  return new Param(value, column);
@@ -35207,7 +36607,7 @@ function notIlike(column, value) {
35207
36607
  return sql`${column} not ilike ${value}`;
35208
36608
  }
35209
36609
 
35210
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/expressions/select.js
36610
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sql/expressions/select.js
35211
36611
  function asc(column) {
35212
36612
  return sql`${column} asc`;
35213
36613
  }
@@ -35215,7 +36615,7 @@ function desc(column) {
35215
36615
  return sql`${column} desc`;
35216
36616
  }
35217
36617
 
35218
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/relations.js
36618
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/relations.js
35219
36619
  class Relation {
35220
36620
  constructor(sourceTable, referencedTable, relationName) {
35221
36621
  this.sourceTable = sourceTable;
@@ -35435,10 +36835,10 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
35435
36835
  return result;
35436
36836
  }
35437
36837
 
35438
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/bun-sqlite/driver.js
36838
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/bun-sqlite/driver.js
35439
36839
  import { Database } from "bun:sqlite";
35440
36840
 
35441
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/selection-proxy.js
36841
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/selection-proxy.js
35442
36842
  class SelectionProxyHandler {
35443
36843
  static [entityKind] = "SelectionProxyHandler";
35444
36844
  config;
@@ -35490,7 +36890,7 @@ class SelectionProxyHandler {
35490
36890
  }
35491
36891
  }
35492
36892
 
35493
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
36893
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
35494
36894
  class ForeignKeyBuilder {
35495
36895
  static [entityKind] = "SQLiteForeignKeyBuilder";
35496
36896
  reference;
@@ -35544,12 +36944,12 @@ class ForeignKey {
35544
36944
  }
35545
36945
  }
35546
36946
 
35547
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
36947
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
35548
36948
  function uniqueKeyName2(table, columns) {
35549
36949
  return `${table[TableName]}_${columns.join("_")}_unique`;
35550
36950
  }
35551
36951
 
35552
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/common.js
36952
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/common.js
35553
36953
  class SQLiteColumnBuilder extends ColumnBuilder {
35554
36954
  static [entityKind] = "SQLiteColumnBuilder";
35555
36955
  foreignKeyConfigs = [];
@@ -35600,7 +37000,7 @@ class SQLiteColumn extends Column {
35600
37000
  static [entityKind] = "SQLiteColumn";
35601
37001
  }
35602
37002
 
35603
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/blob.js
37003
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/blob.js
35604
37004
  class SQLiteBigIntBuilder extends SQLiteColumnBuilder {
35605
37005
  static [entityKind] = "SQLiteBigIntBuilder";
35606
37006
  constructor(name) {
@@ -35688,7 +37088,7 @@ function blob(a, b) {
35688
37088
  return new SQLiteBlobBufferBuilder(name);
35689
37089
  }
35690
37090
 
35691
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/custom.js
37091
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/custom.js
35692
37092
  class SQLiteCustomColumnBuilder extends SQLiteColumnBuilder {
35693
37093
  static [entityKind] = "SQLiteCustomColumnBuilder";
35694
37094
  constructor(name, fieldConfig, customTypeParams) {
@@ -35729,7 +37129,7 @@ function customType(customTypeParams) {
35729
37129
  };
35730
37130
  }
35731
37131
 
35732
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/integer.js
37132
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/integer.js
35733
37133
  class SQLiteBaseIntegerBuilder extends SQLiteColumnBuilder {
35734
37134
  static [entityKind] = "SQLiteBaseIntegerBuilder";
35735
37135
  constructor(name, dataType, columnType) {
@@ -35831,7 +37231,7 @@ function integer2(a, b) {
35831
37231
  return new SQLiteIntegerBuilder(name);
35832
37232
  }
35833
37233
 
35834
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
37234
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
35835
37235
  class SQLiteNumericBuilder extends SQLiteColumnBuilder {
35836
37236
  static [entityKind] = "SQLiteNumericBuilder";
35837
37237
  constructor(name) {
@@ -35901,7 +37301,7 @@ function numeric(a, b) {
35901
37301
  return mode === "number" ? new SQLiteNumericNumberBuilder(name) : mode === "bigint" ? new SQLiteNumericBigIntBuilder(name) : new SQLiteNumericBuilder(name);
35902
37302
  }
35903
37303
 
35904
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/real.js
37304
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/real.js
35905
37305
  class SQLiteRealBuilder extends SQLiteColumnBuilder {
35906
37306
  static [entityKind] = "SQLiteRealBuilder";
35907
37307
  constructor(name) {
@@ -35922,7 +37322,7 @@ function real(name) {
35922
37322
  return new SQLiteRealBuilder(name ?? "");
35923
37323
  }
35924
37324
 
35925
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/text.js
37325
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/text.js
35926
37326
  class SQLiteTextBuilder extends SQLiteColumnBuilder {
35927
37327
  static [entityKind] = "SQLiteTextBuilder";
35928
37328
  constructor(name, config2) {
@@ -35977,7 +37377,7 @@ function text(a, b = {}) {
35977
37377
  return new SQLiteTextBuilder(name, config2);
35978
37378
  }
35979
37379
 
35980
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/all.js
37380
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/columns/all.js
35981
37381
  function getSQLiteColumnBuilders() {
35982
37382
  return {
35983
37383
  blob,
@@ -35989,7 +37389,7 @@ function getSQLiteColumnBuilders() {
35989
37389
  };
35990
37390
  }
35991
37391
 
35992
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/table.js
37392
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/table.js
35993
37393
  var InlineForeignKeys2 = Symbol.for("drizzle:SQLiteInlineForeignKeys");
35994
37394
 
35995
37395
  class SQLiteTable extends Table {
@@ -36023,7 +37423,7 @@ var sqliteTable = (name, columns, extraConfig) => {
36023
37423
  return sqliteTableBase(name, columns, extraConfig);
36024
37424
  };
36025
37425
 
36026
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/indexes.js
37426
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/indexes.js
36027
37427
  class IndexBuilderOn {
36028
37428
  constructor(name, unique4) {
36029
37429
  this.name = name;
@@ -36066,7 +37466,7 @@ function index(name) {
36066
37466
  return new IndexBuilderOn(name, false);
36067
37467
  }
36068
37468
 
36069
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/utils.js
37469
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/utils.js
36070
37470
  function extractUsedTable(table) {
36071
37471
  if (is(table, SQLiteTable)) {
36072
37472
  return [`${table[Table.Symbol.BaseName]}`];
@@ -36080,7 +37480,7 @@ function extractUsedTable(table) {
36080
37480
  return [];
36081
37481
  }
36082
37482
 
36083
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/delete.js
37483
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/delete.js
36084
37484
  class SQLiteDeleteBase extends QueryPromise {
36085
37485
  constructor(table, session, dialect, withList) {
36086
37486
  super();
@@ -36150,7 +37550,7 @@ class SQLiteDeleteBase extends QueryPromise {
36150
37550
  }
36151
37551
  }
36152
37552
 
36153
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/casing.js
37553
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/casing.js
36154
37554
  function toSnakeCase(input) {
36155
37555
  const words = input.replace(/['\u2019]/g, "").match(/[\da-z]+|[A-Z]+(?![a-z])|[A-Z][\da-z]+/g) ?? [];
36156
37556
  return words.map((word) => word.toLowerCase()).join("_");
@@ -36203,12 +37603,12 @@ class CasingCache {
36203
37603
  }
36204
37604
  }
36205
37605
 
36206
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/view-base.js
37606
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/view-base.js
36207
37607
  class SQLiteViewBase extends View {
36208
37608
  static [entityKind] = "SQLiteViewBase";
36209
37609
  }
36210
37610
 
36211
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/dialect.js
37611
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/dialect.js
36212
37612
  class SQLiteDialect {
36213
37613
  static [entityKind] = "SQLiteDialect";
36214
37614
  casing;
@@ -36791,7 +38191,7 @@ class SQLiteSyncDialect extends SQLiteDialect {
36791
38191
  }
36792
38192
  }
36793
38193
 
36794
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/query-builders/query-builder.js
38194
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/query-builders/query-builder.js
36795
38195
  class TypedQueryBuilder {
36796
38196
  static [entityKind] = "TypedQueryBuilder";
36797
38197
  getSelectedFields() {
@@ -36799,7 +38199,7 @@ class TypedQueryBuilder {
36799
38199
  }
36800
38200
  }
36801
38201
 
36802
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/select.js
38202
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/select.js
36803
38203
  class SQLiteSelectBuilder {
36804
38204
  static [entityKind] = "SQLiteSelectBuilder";
36805
38205
  fields;
@@ -36881,7 +38281,7 @@ class SQLiteSelectQueryBuilderBase extends TypedQueryBuilder {
36881
38281
  const tableName = getTableLikeName(table);
36882
38282
  for (const item of extractUsedTable(table))
36883
38283
  this.usedTables.add(item);
36884
- if (typeof tableName === "string" && this.config.joins?.some((join20) => join20.alias === tableName)) {
38284
+ if (typeof tableName === "string" && this.config.joins?.some((join21) => join21.alias === tableName)) {
36885
38285
  throw new Error(`Alias "${tableName}" is already used in this query`);
36886
38286
  }
36887
38287
  if (!this.isPartialSelect) {
@@ -37097,7 +38497,7 @@ var unionAll = createSetOperator("union", true);
37097
38497
  var intersect = createSetOperator("intersect", false);
37098
38498
  var except = createSetOperator("except", false);
37099
38499
 
37100
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/query-builder.js
38500
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/query-builder.js
37101
38501
  class QueryBuilder {
37102
38502
  static [entityKind] = "SQLiteQueryBuilder";
37103
38503
  dialect;
@@ -37156,7 +38556,7 @@ class QueryBuilder {
37156
38556
  }
37157
38557
  }
37158
38558
 
37159
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/insert.js
38559
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/insert.js
37160
38560
  class SQLiteInsertBuilder {
37161
38561
  constructor(table, session, dialect, withList) {
37162
38562
  this.table = table;
@@ -37265,7 +38665,7 @@ class SQLiteInsertBase extends QueryPromise {
37265
38665
  }
37266
38666
  }
37267
38667
 
37268
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/update.js
38668
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/update.js
37269
38669
  class SQLiteUpdateBuilder {
37270
38670
  constructor(table, session, dialect, withList) {
37271
38671
  this.table = table;
@@ -37295,7 +38695,7 @@ class SQLiteUpdateBase extends QueryPromise {
37295
38695
  createJoin(joinType) {
37296
38696
  return (table, on) => {
37297
38697
  const tableName = getTableLikeName(table);
37298
- if (typeof tableName === "string" && this.config.joins.some((join20) => join20.alias === tableName)) {
38698
+ if (typeof tableName === "string" && this.config.joins.some((join21) => join21.alias === tableName)) {
37299
38699
  throw new Error(`Alias "${tableName}" is already used in this query`);
37300
38700
  }
37301
38701
  if (typeof on === "function") {
@@ -37369,7 +38769,7 @@ class SQLiteUpdateBase extends QueryPromise {
37369
38769
  }
37370
38770
  }
37371
38771
 
37372
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/count.js
38772
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/count.js
37373
38773
  class SQLiteCountBuilder extends SQL {
37374
38774
  constructor(params) {
37375
38775
  super(SQLiteCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks);
@@ -37404,7 +38804,7 @@ class SQLiteCountBuilder extends SQL {
37404
38804
  }
37405
38805
  }
37406
38806
 
37407
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/query.js
38807
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/query.js
37408
38808
  class RelationalQueryBuilder {
37409
38809
  constructor(mode, fullSchema, schema, tableNamesMap, table, tableConfig, dialect, session) {
37410
38810
  this.mode = mode;
@@ -37498,7 +38898,7 @@ class SQLiteSyncRelationalQuery extends SQLiteRelationalQuery {
37498
38898
  }
37499
38899
  }
37500
38900
 
37501
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/query-builders/raw.js
38901
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/query-builders/raw.js
37502
38902
  class SQLiteRaw extends QueryPromise {
37503
38903
  constructor(execute, getSQL, action, dialect, mapBatchResult) {
37504
38904
  super();
@@ -37524,7 +38924,7 @@ class SQLiteRaw extends QueryPromise {
37524
38924
  }
37525
38925
  }
37526
38926
 
37527
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/db.js
38927
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/db.js
37528
38928
  class BaseSQLiteDatabase {
37529
38929
  constructor(resultKind, dialect, session, schema) {
37530
38930
  this.resultKind = resultKind;
@@ -37647,7 +39047,7 @@ class BaseSQLiteDatabase {
37647
39047
  }
37648
39048
  }
37649
39049
 
37650
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/cache/core/cache.js
39050
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/cache/core/cache.js
37651
39051
  class Cache {
37652
39052
  static [entityKind] = "Cache";
37653
39053
  }
@@ -37673,7 +39073,7 @@ async function hashQuery(sql2, params) {
37673
39073
  return hashHex;
37674
39074
  }
37675
39075
 
37676
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/session.js
39076
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/sqlite-core/session.js
37677
39077
  class ExecuteResultSync extends QueryPromise {
37678
39078
  constructor(resultCb) {
37679
39079
  super();
@@ -37846,7 +39246,7 @@ class SQLiteTransaction extends BaseSQLiteDatabase {
37846
39246
  }
37847
39247
  }
37848
39248
 
37849
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/bun-sqlite/session.js
39249
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/bun-sqlite/session.js
37850
39250
  class SQLiteBunSession extends SQLiteSession {
37851
39251
  constructor(client, dialect, schema, options = {}) {
37852
39252
  super(dialect);
@@ -37945,7 +39345,7 @@ class PreparedQuery extends SQLitePreparedQuery {
37945
39345
  }
37946
39346
  }
37947
39347
 
37948
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/bun-sqlite/driver.js
39348
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/bun-sqlite/driver.js
37949
39349
  class BunSQLiteDatabase extends BaseSQLiteDatabase {
37950
39350
  static [entityKind] = "BunSQLiteDatabase";
37951
39351
  }
@@ -37999,6 +39399,7 @@ function drizzle(...params) {
37999
39399
  })(drizzle || (drizzle = {}));
38000
39400
 
38001
39401
  // packages/runtime/src/drizzle-schema.ts
39402
+ var epochMsNow = sql`(CAST(strftime('%s','now') AS INTEGER) * 1000)`;
38002
39403
  var deliveriesTable = sqliteTable("deliveries", {
38003
39404
  id: text("id").primaryKey(),
38004
39405
  messageId: text("message_id"),
@@ -38014,7 +39415,7 @@ var deliveriesTable = sqliteTable("deliveries", {
38014
39415
  leaseOwner: text("lease_owner"),
38015
39416
  leaseExpiresAt: integer2("lease_expires_at"),
38016
39417
  metadataJson: text("metadata_json"),
38017
- createdAt: integer2("created_at").notNull().default(sql`(unixepoch())`)
39418
+ createdAt: integer2("created_at").notNull().default(epochMsNow)
38018
39419
  }, (table) => [
38019
39420
  index("idx_deliveries_status_transport").on(table.status, table.transport)
38020
39421
  ]);
@@ -38041,7 +39442,7 @@ var briefingsTable = sqliteTable("briefings", {
38041
39442
  snapshotJson: text("snapshot_json").notNull(),
38042
39443
  callJson: text("call_json").notNull(),
38043
39444
  markdown: text("markdown"),
38044
- createdAt: integer2("created_at").notNull().default(sql`(unixepoch())`)
39445
+ createdAt: integer2("created_at").notNull().default(epochMsNow)
38045
39446
  }, (table) => [
38046
39447
  index("idx_briefings_created_at").on(table.createdAt),
38047
39448
  index("idx_briefings_kind_created_at").on(table.kind, table.createdAt)
@@ -38058,11 +39459,11 @@ function openControlPlaneDrizzle(client) {
38058
39459
  }
38059
39460
 
38060
39461
  // packages/runtime/src/drizzle-migrate.ts
38061
- import { existsSync as existsSync15 } from "fs";
38062
- import { join as join20 } from "path";
39462
+ import { existsSync as existsSync16 } from "fs";
39463
+ import { join as join21 } from "path";
38063
39464
  import { fileURLToPath as fileURLToPath6 } from "url";
38064
39465
 
38065
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/migrator.js
39466
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/migrator.js
38066
39467
  import crypto2 from "crypto";
38067
39468
  import fs from "fs";
38068
39469
  function readMigrationFiles(config2) {
@@ -38094,7 +39495,7 @@ function readMigrationFiles(config2) {
38094
39495
  return migrationQueries;
38095
39496
  }
38096
39497
 
38097
- // node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/bun-sqlite/migrator.js
39498
+ // node_modules/.bun/drizzle-orm@0.45.2+e80f9ee8788b3855/node_modules/drizzle-orm/bun-sqlite/migrator.js
38098
39499
  function migrate(db, config2) {
38099
39500
  const migrations = readMigrationFiles(config2);
38100
39501
  db.dialect.migrate(migrations, db.session, config2);
@@ -38128,7 +39529,7 @@ CREATE TABLE IF NOT EXISTS actors (
38128
39529
  handle TEXT,
38129
39530
  labels_json TEXT,
38130
39531
  metadata_json TEXT,
38131
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
39532
+ created_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER) * 1000)
38132
39533
  );
38133
39534
 
38134
39535
  CREATE TABLE IF NOT EXISTS agents (
@@ -38161,7 +39562,7 @@ CREATE TABLE IF NOT EXISTS agent_endpoints (
38161
39562
  cwd TEXT,
38162
39563
  project_root TEXT,
38163
39564
  metadata_json TEXT,
38164
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
39565
+ updated_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER) * 1000)
38165
39566
  );
38166
39567
 
38167
39568
  CREATE TABLE IF NOT EXISTS conversations (
@@ -38175,7 +39576,7 @@ CREATE TABLE IF NOT EXISTS conversations (
38175
39576
  parent_conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL,
38176
39577
  message_id TEXT,
38177
39578
  metadata_json TEXT,
38178
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
39579
+ created_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER) * 1000)
38179
39580
  );
38180
39581
 
38181
39582
  CREATE TABLE IF NOT EXISTS conversation_members (
@@ -38292,7 +39693,7 @@ CREATE TABLE IF NOT EXISTS deliveries (
38292
39693
  lease_owner TEXT,
38293
39694
  lease_expires_at INTEGER,
38294
39695
  metadata_json TEXT,
38295
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
39696
+ created_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER) * 1000)
38296
39697
  );
38297
39698
 
38298
39699
  CREATE TABLE IF NOT EXISTS delivery_attempts (
@@ -38498,9 +39899,9 @@ CREATE TABLE IF NOT EXISTS mobile_push_registrations (
38498
39899
  updated_at INTEGER NOT NULL
38499
39900
  );
38500
39901
 
38501
- -- Briefing Room: persistent archive of Ranger-generated briefs.
39902
+ -- Briefing Room: persistent archive of Scoutbot-generated briefs.
38502
39903
  -- Each row carries three layers \u2014 snapshot (Layer 1), observations (Layer 2),
38503
- -- brief + call (Layer 3) \u2014 so an operator can audit not just what Ranger said
39904
+ -- brief + call (Layer 3) \u2014 so an operator can audit not just what Scoutbot said
38504
39905
  -- but what it read and how it asked. Rolling 100-cap is enforced in code.
38505
39906
  CREATE TABLE IF NOT EXISTS briefings (
38506
39907
  id TEXT PRIMARY KEY,
@@ -38518,7 +39919,7 @@ CREATE TABLE IF NOT EXISTS briefings (
38518
39919
  -- markdown pipeline landed; rows persisted after step 3 of SCO-037 carry
38519
39920
  -- the full markdown document the analyst emitted.
38520
39921
  markdown TEXT,
38521
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
39922
+ created_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER) * 1000)
38522
39923
  );
38523
39924
 
38524
39925
  CREATE INDEX IF NOT EXISTS idx_nodes_mesh_id
@@ -38629,8 +40030,8 @@ function resolveControlPlaneDrizzleMigrationsFolder() {
38629
40030
  }
38630
40031
  function applyControlPlaneDrizzleMigrations(database) {
38631
40032
  const migrationsFolder = resolveControlPlaneDrizzleMigrationsFolder();
38632
- const journalPath = join20(migrationsFolder, "meta", "_journal.json");
38633
- if (!existsSync15(journalPath)) {
40033
+ const journalPath = join21(migrationsFolder, "meta", "_journal.json");
40034
+ if (!existsSync16(journalPath)) {
38634
40035
  return false;
38635
40036
  }
38636
40037
  migrate(openControlPlaneDrizzle(database), { migrationsFolder });
@@ -38641,35 +40042,14 @@ function stampControlPlaneSchemaVersion(database) {
38641
40042
  }
38642
40043
  if (false) {}
38643
40044
 
38644
- // packages/runtime/src/user-config.ts
38645
- import { existsSync as existsSync16, readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
38646
- import { homedir as homedir15 } from "os";
38647
- import { dirname as dirname11, join as join21 } from "path";
38648
- function userConfigPath() {
38649
- return join21(process.env.OPENSCOUT_HOME ?? join21(homedir15(), ".openscout"), "user.json");
38650
- }
38651
- function loadUserConfig() {
38652
- const configPath = userConfigPath();
38653
- if (!existsSync16(configPath))
38654
- return {};
38655
- try {
38656
- return JSON.parse(readFileSync8(configPath, "utf8"));
38657
- } catch {
38658
- return {};
38659
- }
38660
- }
38661
- function resolveOperatorName() {
38662
- const config2 = loadUserConfig();
38663
- return config2.name?.trim() || process.env.OPENSCOUT_OPERATOR_NAME?.trim() || process.env.USER?.trim() || "operator";
38664
- }
38665
-
38666
40045
  // packages/runtime/src/conversations/legacy-ids.ts
38667
40046
  function conversationIdForAgent(agentId) {
38668
40047
  return buildDirectConversationId("operator", agentId);
38669
40048
  }
38670
40049
  function configuredOperatorActorIds() {
38671
40050
  const operatorName = resolveOperatorName().trim() || "operator";
38672
- return Array.from(new Set(["operator", operatorName]));
40051
+ const operatorHandle = resolveOperatorHandle().trim() || operatorName;
40052
+ return Array.from(new Set(["operator", operatorName, operatorHandle]));
38673
40053
  }
38674
40054
  function buildDirectConversationId(operatorId, agentId) {
38675
40055
  return `dm.${operatorId}.${agentId}`;
@@ -39984,6 +41364,11 @@ class SQLiteControlPlaneStore {
39984
41364
  const row = queryGet(this.readDb, "SELECT * FROM durable_actions WHERE id = ?1", actionId);
39985
41365
  return row ? durableActionFromRow(row) : null;
39986
41366
  }
41367
+ getDurableActionByIdempotencyKey(input) {
41368
+ const row = queryGet(this.readDb, `SELECT * FROM durable_actions
41369
+ WHERE authority_cell_id = ?1 AND kind = ?2 AND idempotency_key = ?3`, input.authorityCellId, input.kind, input.idempotencyKey);
41370
+ return row ? durableActionFromRow(row) : null;
41371
+ }
39987
41372
  listDueDurableActions(input) {
39988
41373
  const limit = Math.max(1, Math.min(500, Math.floor(input.limit ?? 50)));
39989
41374
  const claimableAt = input.claimableAt ?? input.dueAtLte;
@@ -41431,6 +42816,52 @@ class ThreadEventPlane {
41431
42816
  }
41432
42817
  }
41433
42818
 
42819
+ // packages/runtime/src/invocation-lifecycle-read-model.ts
42820
+ function readInvocationLifecycle(input) {
42821
+ const invocationId = input.invocationId.trim();
42822
+ if (!invocationId) {
42823
+ return null;
42824
+ }
42825
+ const invocation = input.snapshot.invocations[invocationId];
42826
+ if (!invocation) {
42827
+ return null;
42828
+ }
42829
+ const deliveries2 = input.journal.listDeliveries({ limit: input.deliveryLimit ?? 5000 }).filter((delivery) => deliveryMatchesInvocation(delivery, invocation));
42830
+ const deliveryAttempts = Object.fromEntries(deliveries2.map((delivery) => [
42831
+ delivery.id,
42832
+ input.journal.listDeliveryAttempts(delivery.id)
42833
+ ]));
42834
+ return projectInvocationLifecycle({
42835
+ invocation,
42836
+ flight: latestFlightForInvocation(input.snapshot, invocationId),
42837
+ deliveries: deliveries2,
42838
+ deliveryAttempts,
42839
+ now: input.now ?? Date.now()
42840
+ });
42841
+ }
42842
+ function latestFlightForInvocation(snapshot, invocationId) {
42843
+ let latest;
42844
+ for (const flight of Object.values(snapshot.flights)) {
42845
+ if (flight.invocationId !== invocationId) {
42846
+ continue;
42847
+ }
42848
+ if (!latest || flightSortTimestamp(flight) > flightSortTimestamp(latest)) {
42849
+ latest = flight;
42850
+ }
42851
+ }
42852
+ return latest;
42853
+ }
42854
+ function flightSortTimestamp(flight) {
42855
+ return flight.completedAt ?? flight.startedAt ?? 0;
42856
+ }
42857
+ function deliveryMatchesInvocation(delivery, invocation) {
42858
+ return delivery.invocationId === invocation.id || Boolean(invocation.messageId && delivery.messageId === invocation.messageId) || metadataString3(delivery.metadata, "invocationId") === invocation.id;
42859
+ }
42860
+ function metadataString3(metadata, key) {
42861
+ const value = metadata?.[key];
42862
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
42863
+ }
42864
+
41434
42865
  // packages/runtime/src/broker-core-service.ts
41435
42866
  function normalizeLimit(limit) {
41436
42867
  return typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? Math.min(limit, 500) : 100;
@@ -41453,7 +42884,13 @@ function listBrokerMessages(runtime, input = {}) {
41453
42884
  }
41454
42885
  return authored || addressed || participantConversation;
41455
42886
  };
41456
- return Object.values(snapshot.messages).filter((message) => !input.conversationId || message.conversationId === input.conversationId).filter(matchesParticipant).filter((message) => input.since === null || input.since === undefined ? true : message.createdAt >= input.since).sort((lhs, rhs) => rhs.createdAt - lhs.createdAt).slice(0, limit).reverse();
42887
+ return Object.values(snapshot.messages).filter((message) => !isBrokerRequesterWaitTimeoutStatusMessage(message)).filter((message) => !input.conversationId || message.conversationId === input.conversationId).filter(matchesParticipant).filter((message) => input.since === null || input.since === undefined ? true : message.createdAt >= input.since).sort((lhs, rhs) => rhs.createdAt - lhs.createdAt).slice(0, limit).reverse();
42888
+ }
42889
+ function isBrokerRequesterWaitTimeoutStatusMessage(message) {
42890
+ if (message.class !== "status" || metadataString4(message.metadata, "source") !== "broker") {
42891
+ return false;
42892
+ }
42893
+ return message.body.includes("Scout stopped waiting for a synchronous result") || message.body.includes("the requester stopped waiting after");
41457
42894
  }
41458
42895
  async function listBrokerActivity(projection, isReconciledStaleFlightActivityItem, input) {
41459
42896
  const items = await projection.listActivityItems({
@@ -41506,7 +42943,7 @@ function compactText(value, fallback) {
41506
42943
  }
41507
42944
  return text2.length > 240 ? `${text2.slice(0, 237)}...` : text2;
41508
42945
  }
41509
- function metadataString3(metadata, key) {
42946
+ function metadataString4(metadata, key) {
41510
42947
  const value = metadata?.[key];
41511
42948
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
41512
42949
  }
@@ -41514,6 +42951,82 @@ function metadataNumber(metadata, key) {
41514
42951
  const value = metadata?.[key];
41515
42952
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
41516
42953
  }
42954
+ function metadataBoolean(metadata, key) {
42955
+ return metadata?.[key] === true;
42956
+ }
42957
+ function metadataRecord4(metadata, key) {
42958
+ const value = metadata?.[key];
42959
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
42960
+ }
42961
+ function agentCardLifecycleKind(agent) {
42962
+ const lifecycle2 = metadataRecord4(agent.metadata, "cardLifecycle");
42963
+ const kind = metadataString4(lifecycle2, "kind");
42964
+ return kind === "persistent" || kind === "one_time" ? kind : null;
42965
+ }
42966
+ function summarizeBrokerAgentCounts(snapshot, localNodeId) {
42967
+ const counts = {
42968
+ agents: 0,
42969
+ agentRecords: 0,
42970
+ rawAgentRecords: 0,
42971
+ configuredAgents: 0,
42972
+ scoutManagedAgents: 0,
42973
+ currentAgentRegistrations: 0,
42974
+ localAgentRegistrations: 0,
42975
+ remoteAgentRegistrations: 0,
42976
+ staleAgentRegistrations: 0,
42977
+ retiredAgentRegistrations: 0,
42978
+ oneTimeAgentCards: 0,
42979
+ persistentAgentCards: 0
42980
+ };
42981
+ for (const agent of Object.values(snapshot.agents)) {
42982
+ counts.agentRecords += 1;
42983
+ counts.rawAgentRecords += 1;
42984
+ const retired = metadataBoolean(agent.metadata, "retiredFromFleet");
42985
+ const stale = metadataBoolean(agent.metadata, "staleLocalRegistration");
42986
+ const current = !retired && !stale;
42987
+ const local = agent.homeNodeId === localNodeId || agent.authorityNodeId === localNodeId;
42988
+ const lifecycleKind = agentCardLifecycleKind(agent);
42989
+ const oneTimeCard = lifecycleKind === "one_time";
42990
+ const persistentCard = lifecycleKind === "persistent";
42991
+ const source = metadataString4(agent.metadata, "source");
42992
+ const externalSource = metadataString4(agent.metadata, "externalSource");
42993
+ const registryBacked = source === undefined || source === "relay-agent-registry";
42994
+ const durableScoutManaged = source === "scout-managed" && externalSource !== "pairing-session";
42995
+ if (retired) {
42996
+ counts.retiredAgentRegistrations += 1;
42997
+ }
42998
+ if (stale) {
42999
+ counts.staleAgentRegistrations += 1;
43000
+ }
43001
+ if (!current) {
43002
+ continue;
43003
+ }
43004
+ counts.currentAgentRegistrations += 1;
43005
+ if (local) {
43006
+ counts.localAgentRegistrations += 1;
43007
+ } else {
43008
+ counts.remoteAgentRegistrations += 1;
43009
+ }
43010
+ if (oneTimeCard) {
43011
+ counts.oneTimeAgentCards += 1;
43012
+ }
43013
+ if (persistentCard) {
43014
+ counts.persistentAgentCards += 1;
43015
+ }
43016
+ if (!local || oneTimeCard) {
43017
+ continue;
43018
+ }
43019
+ if (registryBacked) {
43020
+ counts.configuredAgents += 1;
43021
+ continue;
43022
+ }
43023
+ if (durableScoutManaged) {
43024
+ counts.scoutManagedAgents += 1;
43025
+ }
43026
+ }
43027
+ counts.agents = counts.configuredAgents + counts.scoutManagedAgents;
43028
+ return counts;
43029
+ }
41517
43030
  function messageMentionsAgent(message, agentId) {
41518
43031
  return message.actorId === agentId || Boolean(message.mentions?.some((mention) => mention.actorId === agentId)) || Boolean(message.audience?.notify?.includes(agentId)) || Boolean(message.audience?.invoke?.includes(agentId)) || Boolean(message.audience?.visibleTo?.includes(agentId));
41519
43032
  }
@@ -41683,7 +43196,7 @@ function deliveryToFeedItem(delivery, snapshot, attempts) {
41683
43196
  severity: deliverySeverity(delivery.status),
41684
43197
  at: deliveryAt(delivery, snapshot, attempts),
41685
43198
  title: `Delivery ${delivery.status}`,
41686
- summary: compactText(metadataString3(delivery.metadata, "failureReason") ?? metadataString3(delivery.metadata, "lastError") ?? `${delivery.reason} via ${delivery.transport}`, delivery.status),
43199
+ summary: compactText(metadataString4(delivery.metadata, "failureReason") ?? metadataString4(delivery.metadata, "lastError") ?? `${delivery.reason} via ${delivery.transport}`, delivery.status),
41687
43200
  agentId: delivery.targetId,
41688
43201
  targetAgentId: delivery.targetId,
41689
43202
  conversationId: invocation?.conversationId,
@@ -41828,8 +43341,8 @@ function endpointStatus(endpoint) {
41828
43341
  sessionId: endpoint.sessionId,
41829
43342
  projectRoot: endpoint.projectRoot,
41830
43343
  cwd: endpoint.cwd,
41831
- lastError: metadataString3(endpoint.metadata, "lastError"),
41832
- lastFailureStage: metadataString3(endpoint.metadata, "lastFailureStage"),
43344
+ lastError: metadataString4(endpoint.metadata, "lastError"),
43345
+ lastFailureStage: metadataString4(endpoint.metadata, "lastFailureStage"),
41833
43346
  updatedAt: metadataNumber(endpoint.metadata, "lastFailedAt") ?? metadataNumber(endpoint.metadata, "lastSeenAt")
41834
43347
  };
41835
43348
  }
@@ -41933,6 +43446,7 @@ function createBrokerCoreService(deps) {
41933
43446
  baseUrl: deps.baseUrl,
41934
43447
  readHealth: async () => {
41935
43448
  const snapshot = deps.runtime.snapshot();
43449
+ const agentCounts = summarizeBrokerAgentCounts(snapshot, deps.nodeId);
41936
43450
  return {
41937
43451
  ok: true,
41938
43452
  nodeId: deps.nodeId,
@@ -41940,7 +43454,18 @@ function createBrokerCoreService(deps) {
41940
43454
  counts: {
41941
43455
  nodes: Object.keys(snapshot.nodes).length,
41942
43456
  actors: Object.keys(snapshot.actors).length,
41943
- agents: Object.keys(snapshot.agents).length,
43457
+ agents: agentCounts.agents,
43458
+ agentRecords: agentCounts.agentRecords,
43459
+ rawAgentRecords: agentCounts.rawAgentRecords,
43460
+ configuredAgents: agentCounts.configuredAgents,
43461
+ scoutManagedAgents: agentCounts.scoutManagedAgents,
43462
+ currentAgentRegistrations: agentCounts.currentAgentRegistrations,
43463
+ localAgentRegistrations: agentCounts.localAgentRegistrations,
43464
+ remoteAgentRegistrations: agentCounts.remoteAgentRegistrations,
43465
+ staleAgentRegistrations: agentCounts.staleAgentRegistrations,
43466
+ retiredAgentRegistrations: agentCounts.retiredAgentRegistrations,
43467
+ oneTimeAgentCards: agentCounts.oneTimeAgentCards,
43468
+ persistentAgentCards: agentCounts.persistentAgentCards,
41944
43469
  conversations: Object.keys(snapshot.conversations).length,
41945
43470
  messages: Object.keys(snapshot.messages).length,
41946
43471
  flights: Object.keys(snapshot.flights).length,
@@ -41958,6 +43483,11 @@ function createBrokerCoreService(deps) {
41958
43483
  readUnblockRequests: async (query) => listBrokerUnblockRequests(deps.journal, query),
41959
43484
  readUnblockRequestEvents: async (query) => listBrokerUnblockRequestEvents(deps.journal, query),
41960
43485
  readAgentBrokerFeed: async (query) => await readAgentBrokerFeed(deps, query),
43486
+ readInvocationLifecycle: async (query) => readInvocationLifecycle({
43487
+ snapshot: deps.runtime.snapshot(),
43488
+ journal: deps.journal,
43489
+ invocationId: query.invocationId
43490
+ }),
41961
43491
  readThreadEvents: async (query) => await deps.threadEvents.replay({
41962
43492
  conversationId: query.conversationId,
41963
43493
  afterSeq: query.afterSeq ?? 0,
@@ -42652,6 +44182,9 @@ async function broadcastApnsAlertToActiveMobileDevices(alert) {
42652
44182
  }
42653
44183
 
42654
44184
  // packages/runtime/src/broker-daemon.ts
44185
+ var PROCESS_NAME = "scout-broker";
44186
+ var WEB_PROCESS_NAME = "scout-web";
44187
+ process.title = PROCESS_NAME;
42655
44188
  function createRuntimeId2(prefix) {
42656
44189
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
42657
44190
  }
@@ -42728,6 +44261,7 @@ var parentPid = Number.parseInt(process.env.OPENSCOUT_PARENT_PID ?? "0", 10);
42728
44261
  var localAgentSyncIntervalMs = Number.parseInt(process.env.OPENSCOUT_LOCAL_AGENT_SYNC_INTERVAL_MS ?? "5000", 10);
42729
44262
  var registeredLocalAgentsRegistrySignature = null;
42730
44263
  var registeredLocalAgentsSyncInFlight = null;
44264
+ var DEFAULT_IMPLICIT_PROJECT_CARD_TTL_MS = 24 * 60 * 60 * 1000;
42731
44265
  ensureOpenScoutCleanSlateSync();
42732
44266
  var existingBroker = await probeExistingBroker();
42733
44267
  if (existingBroker) {
@@ -42824,6 +44358,7 @@ var DEFAULT_INBOX_STATUSES = new Set([
42824
44358
  "deferred",
42825
44359
  "leased"
42826
44360
  ]);
44361
+ var STALE_MESH_AUTHORITY_NODE_MS = 24 * 60 * 60 * 1000;
42827
44362
  async function listInboxItems(options) {
42828
44363
  const statuses = options.statuses ?? DEFAULT_INBOX_STATUSES;
42829
44364
  const limit = Math.min(Math.max(options.limit ?? 100, 1), 500);
@@ -43231,18 +44766,47 @@ function spawnWebServer(context = {}) {
43231
44766
  trustedHost: context.trustedHost
43232
44767
  });
43233
44768
  const child = spawn5(bun.path, entry.endsWith(".ts") ? ["run", "--hot", entry] : ["run", entry], {
44769
+ argv0: WEB_PROCESS_NAME,
43234
44770
  detached: true,
43235
44771
  env,
43236
44772
  stdio: ["ignore", logFd, logFd]
43237
44773
  });
43238
- child.once("exit", () => {
43239
- if (webServerProcess === child) {
43240
- webServerProcess = null;
44774
+ child.once("exit", (code, signal) => {
44775
+ if (webServerProcess !== child) {
44776
+ return;
44777
+ }
44778
+ webServerProcess = null;
44779
+ if (shuttingDown) {
44780
+ return;
44781
+ }
44782
+ const now = Date.now();
44783
+ while (webRespawnFailures.length > 0 && now - webRespawnFailures[0] > WEB_RESPAWN_FAILURE_WINDOW_MS) {
44784
+ webRespawnFailures.shift();
43241
44785
  }
44786
+ webRespawnFailures.push(now);
44787
+ if (webRespawnFailures.length > WEB_RESPAWN_MAX_FAILURES) {
44788
+ console.error(`[openscout-runtime] Scout web server has exited ${webRespawnFailures.length} times within ${WEB_RESPAWN_FAILURE_WINDOW_MS / 1000}s \u2014 pausing auto-respawn. Use 'scout server start' to retry.`);
44789
+ webRespawnFailures.length = 0;
44790
+ return;
44791
+ }
44792
+ const delay = Math.min(WEB_RESPAWN_BASE_DELAY_MS * webRespawnFailures.length, WEB_RESPAWN_MAX_DELAY_MS);
44793
+ console.warn(`[openscout-runtime] Scout web server exited unexpectedly (code=${code}, signal=${signal}) \u2014 respawning in ${delay}ms (failure ${webRespawnFailures.length}/${WEB_RESPAWN_MAX_FAILURES})`);
44794
+ setTimeout(() => {
44795
+ if (shuttingDown)
44796
+ return;
44797
+ startWebServerIfNeeded(context).catch((error48) => {
44798
+ console.error("[openscout-runtime] web server respawn failed:", error48);
44799
+ });
44800
+ }, delay).unref?.();
43242
44801
  });
43243
44802
  child.unref();
43244
44803
  return child;
43245
44804
  }
44805
+ var WEB_RESPAWN_BASE_DELAY_MS = 1000;
44806
+ var WEB_RESPAWN_MAX_DELAY_MS = 30000;
44807
+ var WEB_RESPAWN_MAX_FAILURES = 5;
44808
+ var WEB_RESPAWN_FAILURE_WINDOW_MS = 60000;
44809
+ var webRespawnFailures = [];
43246
44810
  async function webSupervisorStatus(error48 = null) {
43247
44811
  const running = await isWebServerHealthy();
43248
44812
  return {
@@ -43468,12 +45032,21 @@ async function recordInvocationDurably(invocation, options = {}) {
43468
45032
  });
43469
45033
  }
43470
45034
  async function recordFlightDurably(flight) {
45035
+ let recordedFlight = null;
43471
45036
  await runDurableWrite(async () => {
45037
+ const previous = runtime.snapshot().flights[flight.id];
45038
+ if (previous && shouldIgnoreFlightUpdate(previous, flight)) {
45039
+ console.warn(`[openscout-runtime] ignored stale flight update ${flight.id}: ${previous.state} -> ${flight.state}`);
45040
+ return;
45041
+ }
43472
45042
  const entries = await commitDurableEntries({ kind: "flight.record", flight }, async () => {
43473
45043
  await runtime.upsertFlight(flight);
43474
45044
  }, { enqueueProjection: false });
43475
45045
  await applyProjectedEntries(entries);
45046
+ recordedFlight = flight;
43476
45047
  });
45048
+ if (!recordedFlight)
45049
+ return;
43477
45050
  const invocation = knownInvocations.get(flight.invocationId) ?? runtime.snapshot().invocations[flight.invocationId];
43478
45051
  await reconcileMessageDeliveriesForFlight(flight, invocation);
43479
45052
  if (invocation && isTerminalFlightState(flight.state)) {
@@ -43489,7 +45062,18 @@ async function recordFlightDurably(flight) {
43489
45062
  console.warn(`[openscout-runtime] failed to forward flight ${flight.id} to conversation authority:`, error48 instanceof Error ? error48.message : String(error48));
43490
45063
  }
43491
45064
  }
45065
+ function shouldIgnoreFlightUpdate(previous, next) {
45066
+ return isTerminalFlightState(previous.state) && !isTerminalFlightState(next.state);
45067
+ }
43492
45068
  var terminalDeliveryStatuses = new Set(["completed", "failed", "cancelled"]);
45069
+ var staleReconcileableDeliveryStatuses = new Set([
45070
+ "accepted",
45071
+ "deferred",
45072
+ "leased",
45073
+ "pending",
45074
+ "running",
45075
+ "sent"
45076
+ ]);
43493
45077
  function deliveryStatusForFlight(flight) {
43494
45078
  if (flight.state === "running" || flight.state === "waiting") {
43495
45079
  return "running";
@@ -43528,6 +45112,46 @@ async function reconcileMessageDeliveriesForFlight(flight, invocation) {
43528
45112
  });
43529
45113
  }
43530
45114
  }
45115
+ function staleLocalDeliveryReason(snapshot, delivery) {
45116
+ if (delivery.targetKind !== "agent" || !staleReconcileableDeliveryStatuses.has(delivery.status)) {
45117
+ return null;
45118
+ }
45119
+ const endpoints = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === delivery.targetId);
45120
+ if (endpoints.length === 0) {
45121
+ return null;
45122
+ }
45123
+ if (endpoints.some((endpoint) => staleLocalEndpointReason(endpoint) === null)) {
45124
+ return null;
45125
+ }
45126
+ const staleEndpoints = endpoints.filter((endpoint) => staleLocalEndpointReason(endpoint) !== null).sort((left, right) => endpointStartedAt(right) - endpointStartedAt(left));
45127
+ const transportMatch = staleEndpoints.find((endpoint) => endpoint.transport === delivery.transport);
45128
+ return staleLocalEndpointReason(transportMatch ?? staleEndpoints[0] ?? null);
45129
+ }
45130
+ async function reconcileStaleLocalDeliveries() {
45131
+ const snapshot = runtime.snapshot();
45132
+ const now = Date.now();
45133
+ for (const delivery of journal.listDeliveries({ limit: 5000 })) {
45134
+ const reason = staleLocalDeliveryReason(snapshot, delivery);
45135
+ if (!reason) {
45136
+ continue;
45137
+ }
45138
+ await updateDeliveryStatusDurably({
45139
+ deliveryId: delivery.id,
45140
+ status: "failed",
45141
+ metadata: {
45142
+ failureReason: "agent_offline",
45143
+ failureDetail: `Stale local delivery reconciled: ${reason}`,
45144
+ staleLocalRegistration: true,
45145
+ reconciledStaleDelivery: true,
45146
+ reconciledReason: reason,
45147
+ reconciledAt: now
45148
+ },
45149
+ leaseOwner: null,
45150
+ leaseExpiresAt: null
45151
+ });
45152
+ console.warn(`[openscout-runtime] reconciled stale local delivery ${delivery.id}: ${reason}`);
45153
+ }
45154
+ }
43531
45155
  async function recordDeliveryDurably(delivery) {
43532
45156
  await runDurableWrite(async () => {
43533
45157
  await commitDurableEntries({ kind: "deliveries.record", deliveries: [delivery] }, async () => {});
@@ -43913,17 +45537,61 @@ function latestEndpointForAgent(snapshot, agentId) {
43913
45537
  const candidates = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agentId);
43914
45538
  return [...candidates].sort((left, right) => Math.max(endpointTerminalAt(right), endpointStartedAt(right)) - Math.max(endpointTerminalAt(left), endpointStartedAt(left)))[0] ?? null;
43915
45539
  }
45540
+ function staleLocalEndpointReason(endpoint) {
45541
+ if (!endpoint || endpoint.metadata?.staleLocalRegistration !== true) {
45542
+ return null;
45543
+ }
45544
+ const replacementAgentId = endpoint.metadata.replacedByAgentId;
45545
+ const replacement = typeof replacementAgentId === "string" && replacementAgentId.trim().length > 0 ? `; replacement agent is ${replacementAgentId.trim()}` : "";
45546
+ return `endpoint ${endpoint.id} is a stale local registration superseded by current setup${replacement}`;
45547
+ }
45548
+ function flightDispatchEndpointId(flight) {
45549
+ const dispatchAck = flight.metadata?.dispatchAck;
45550
+ if (!dispatchAck || typeof dispatchAck !== "object" || Array.isArray(dispatchAck)) {
45551
+ return null;
45552
+ }
45553
+ const endpointId = dispatchAck.endpointId;
45554
+ return typeof endpointId === "string" && endpointId.trim().length > 0 ? endpointId.trim() : null;
45555
+ }
45556
+ function endpointForFlight(snapshot, flight) {
45557
+ const dispatchedEndpointId = flightDispatchEndpointId(flight);
45558
+ if (dispatchedEndpointId) {
45559
+ const endpoint = snapshot.endpoints[dispatchedEndpointId];
45560
+ if (endpoint?.agentId === flight.targetAgentId) {
45561
+ return endpoint;
45562
+ }
45563
+ return null;
45564
+ }
45565
+ return latestEndpointForAgent(snapshot, flight.targetAgentId);
45566
+ }
45567
+ function flightDispatchEndpointUnavailableReason(snapshot, flight) {
45568
+ const dispatchedEndpointId = flightDispatchEndpointId(flight);
45569
+ if (!dispatchedEndpointId) {
45570
+ return null;
45571
+ }
45572
+ const endpoint = snapshot.endpoints[dispatchedEndpointId];
45573
+ if (!endpoint) {
45574
+ return `dispatched endpoint ${dispatchedEndpointId} is no longer registered`;
45575
+ }
45576
+ if (endpoint.agentId !== flight.targetAgentId) {
45577
+ return `dispatched endpoint ${dispatchedEndpointId} no longer belongs to target agent ${flight.targetAgentId}`;
45578
+ }
45579
+ return null;
45580
+ }
43916
45581
  function isReconciledStaleFlightActivityItem(item) {
43917
45582
  return item.kind === "flight_updated" && typeof item.summary === "string" && item.summary.startsWith("Stale running flight reconciled:");
43918
45583
  }
43919
- function isStaleLocalAgent(agent) {
43920
- return agent?.metadata?.staleLocalRegistration === true || agent?.metadata?.retiredFromFleet === true;
45584
+ function isRetiredLocalAgent(agent) {
45585
+ return agent?.metadata?.retiredFromFleet === true;
45586
+ }
45587
+ function isInactiveLocalAgent(agent) {
45588
+ return isRetiredLocalAgent(agent) || agent?.metadata?.staleLocalRegistration === true;
43921
45589
  }
43922
45590
  function isStaleLocalEndpoint(snapshot, endpoint) {
43923
45591
  if (!endpoint || endpoint.metadata?.staleLocalRegistration === true) {
43924
45592
  return true;
43925
45593
  }
43926
- return isStaleLocalAgent(snapshot.agents[endpoint.agentId]);
45594
+ return isInactiveLocalAgent(snapshot.agents[endpoint.agentId]);
43927
45595
  }
43928
45596
  function homeEndpointForAgent(snapshot, agentId) {
43929
45597
  const candidates = Object.values(snapshot.endpoints).filter((endpoint) => endpoint.agentId === agentId && !isStaleLocalEndpoint(snapshot, endpoint));
@@ -43977,6 +45645,20 @@ function metadataStringValue2(metadata, key) {
43977
45645
  const value = metadata?.[key];
43978
45646
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
43979
45647
  }
45648
+ function scoutbotReplyProvenanceMetadata(invocation) {
45649
+ if (invocation.targetAgentId !== "scoutbot") {
45650
+ return {};
45651
+ }
45652
+ return {
45653
+ source: metadataStringValue2(invocation.metadata, "source") ?? "scoutbot",
45654
+ requestedBy: metadataStringValue2(invocation.metadata, "requestedBy") ?? invocation.requesterId,
45655
+ sourceMessageId: metadataStringValue2(invocation.metadata, "sourceMessageId") ?? invocation.messageId ?? null,
45656
+ parentScoutbotTurnId: metadataStringValue2(invocation.metadata, "parentScoutbotTurnId"),
45657
+ generatedBy: metadataStringValue2(invocation.metadata, "generatedBy") ?? "scoutbot",
45658
+ scoutbotThreadId: metadataStringValue2(invocation.metadata, "scoutbotThreadId"),
45659
+ targetSessionId: metadataStringValue2(invocation.metadata, "targetSessionId")
45660
+ };
45661
+ }
43980
45662
  function brokerTargetProjectRoot(agent, endpoint) {
43981
45663
  return endpoint?.projectRoot ?? endpoint?.cwd ?? metadataStringValue2(agent.metadata, "projectRoot");
43982
45664
  }
@@ -44185,7 +45867,7 @@ function buildBrokerReturnAddressForActor(snapshot, actorId, options = {}) {
44185
45867
  replyToMessageId: options.replyToMessageId,
44186
45868
  nodeId: endpoint?.nodeId || agent?.authorityNodeId || agent?.homeNodeId,
44187
45869
  projectRoot: endpoint?.projectRoot ?? endpoint?.cwd ?? metadataStringValue2(agent?.metadata, "projectRoot") ?? undefined,
44188
- sessionId: endpoint?.sessionId
45870
+ sessionId: options.sessionId ?? endpoint?.sessionId
44189
45871
  });
44190
45872
  }
44191
45873
  function describeUnavailableDeliveryTarget(snapshot, agent) {
@@ -44203,20 +45885,8 @@ function describeUnavailableDeliveryTarget(snapshot, agent) {
44203
45885
  projectRoot
44204
45886
  };
44205
45887
  }
44206
- if (agent.metadata?.staleLocalRegistration === true) {
44207
- return {
44208
- agentId: agent.id,
44209
- displayName: agent.displayName ?? agent.id,
44210
- reason: "stale_registration",
44211
- detail: `${agent.displayName ?? agent.id} has a stale registration and needs operator follow-up before the broker can route to it again.`,
44212
- wakePolicy: agent.wakePolicy,
44213
- endpointState: endpoint?.state === "offline" ? "offline" : "unknown",
44214
- transport: endpoint?.transport ?? null,
44215
- projectRoot
44216
- };
44217
- }
44218
45888
  if (agent.authorityNodeId && agent.authorityNodeId !== nodeId) {
44219
- return null;
45889
+ return describeRemoteAuthorityIssue(agent, snapshot.nodes[agent.authorityNodeId]);
44220
45890
  }
44221
45891
  if (agent.wakePolicy !== "manual") {
44222
45892
  return null;
@@ -44298,7 +45968,7 @@ function summarizeHomeAgent(endpoint) {
44298
45968
  }
44299
45969
  async function brokerHomePayload() {
44300
45970
  const snapshot = runtime.snapshot();
44301
- const agents = Object.values(snapshot.agents).filter((agent) => !isStaleLocalAgent(agent)).map((agent) => {
45971
+ const agents = Object.values(snapshot.agents).filter((agent) => !isInactiveLocalAgent(agent)).map((agent) => {
44302
45972
  const endpoint = homeEndpointForAgent(snapshot, agent.id);
44303
45973
  const status = summarizeHomeAgent(endpoint);
44304
45974
  return {
@@ -44352,20 +46022,33 @@ function staleWorkingFlightReason(snapshot, flight) {
44352
46022
  if (!isWorkingFlightState(flight.state)) {
44353
46023
  return null;
44354
46024
  }
46025
+ if (activeInvocationTasks.has(flight.invocationId)) {
46026
+ return null;
46027
+ }
44355
46028
  const startedAt = flightTimestamp(flight);
44356
46029
  const newerTerminalFlight = Object.values(snapshot.flights).filter((candidate) => candidate.targetAgentId === flight.targetAgentId && candidate.id !== flight.id && !isWorkingFlightState(candidate.state) && flightTimestamp(candidate) > startedAt).sort((left, right) => flightTimestamp(right) - flightTimestamp(left))[0] ?? null;
44357
46030
  if (newerTerminalFlight) {
44358
46031
  return `superseded by newer ${newerTerminalFlight.state} flight ${newerTerminalFlight.id}`;
44359
46032
  }
44360
46033
  const agent = snapshot.agents[flight.targetAgentId];
44361
- if (isStaleLocalAgent(agent)) {
44362
- const replacementAgentId = metadataStringValue2(agent?.metadata, "replacedByAgentId");
44363
- return replacementAgentId ? `target agent ${flight.targetAgentId} was marked stale and replaced by ${replacementAgentId}` : `target agent ${flight.targetAgentId} was marked stale`;
46034
+ if (isRetiredLocalAgent(agent)) {
46035
+ return `target agent ${flight.targetAgentId} was retired from the fleet`;
46036
+ }
46037
+ if (agent?.metadata?.staleLocalRegistration === true) {
46038
+ return staleLocalEndpointReason(latestEndpointForAgent(snapshot, flight.targetAgentId)) ?? `target agent ${flight.targetAgentId} is a stale local registration superseded by current setup`;
44364
46039
  }
44365
- const endpoint = latestEndpointForAgent(snapshot, flight.targetAgentId);
46040
+ const dispatchEndpointReason = flightDispatchEndpointUnavailableReason(snapshot, flight);
46041
+ if (dispatchEndpointReason) {
46042
+ return dispatchEndpointReason;
46043
+ }
46044
+ const endpoint = endpointForFlight(snapshot, flight);
44366
46045
  if (!endpoint) {
44367
46046
  return null;
44368
46047
  }
46048
+ const staleEndpointReason = staleLocalEndpointReason(endpoint);
46049
+ if (staleEndpointReason) {
46050
+ return staleEndpointReason;
46051
+ }
44369
46052
  const terminalAt = endpointTerminalAt(endpoint);
44370
46053
  if (endpoint.state !== "active" && terminalAt > startedAt) {
44371
46054
  return `endpoint ${endpoint.id} moved to ${endpoint.state} at ${terminalAt}`;
@@ -44457,7 +46140,7 @@ function uniqueManagedAgentSelector(snapshot, requestedSelector, currentAgentId)
44457
46140
  for (let counter = 2;counter <= 101; counter += 1) {
44458
46141
  const resolution = resolveAgentLabel(snapshot, candidate, {
44459
46142
  preferLocalNodeId: nodeId,
44460
- helpers: { isStale: isStaleLocalAgent }
46143
+ helpers: { isStale: isInactiveLocalAgent }
44461
46144
  });
44462
46145
  if (resolution.kind === "unknown") {
44463
46146
  return candidate;
@@ -44610,7 +46293,7 @@ function resolveManagedSessionAttachTarget(snapshot, input) {
44610
46293
  const directAgentId = input.agentId?.trim();
44611
46294
  if (directAgentId) {
44612
46295
  const agent = snapshot.agents[directAgentId];
44613
- if (!agent || isStaleLocalAgent(agent)) {
46296
+ if (!agent || isInactiveLocalAgent(agent)) {
44614
46297
  throw new Error(`unknown Scout agent ${directAgentId}`);
44615
46298
  }
44616
46299
  if (agent.authorityNodeId !== nodeId) {
@@ -44624,7 +46307,7 @@ function resolveManagedSessionAttachTarget(snapshot, input) {
44624
46307
  }
44625
46308
  const resolution = resolveAgentLabel(snapshot, selector, {
44626
46309
  preferLocalNodeId: nodeId,
44627
- helpers: { isStale: isStaleLocalAgent }
46310
+ helpers: { isStale: isInactiveLocalAgent }
44628
46311
  });
44629
46312
  switch (resolution.kind) {
44630
46313
  case "resolved":
@@ -44663,6 +46346,41 @@ function staleLocalAgentReplacementId(definitionId, activeAgentIdsByDefinition)
44663
46346
  }
44664
46347
  return null;
44665
46348
  }
46349
+ function staleRegistrationMetadataMatches(metadata, replacementAgentId) {
46350
+ if (metadata?.staleLocalRegistration !== true) {
46351
+ return false;
46352
+ }
46353
+ if (!replacementAgentId) {
46354
+ return true;
46355
+ }
46356
+ const existingReplacement = typeof metadata.replacedByAgentId === "string" ? metadata.replacedByAgentId.trim() : "";
46357
+ return existingReplacement === replacementAgentId;
46358
+ }
46359
+ function staleLocalRegistrationMetadata(metadata, staleAt, replacementAgentId) {
46360
+ const existingReplacement = typeof metadata?.replacedByAgentId === "string" ? metadata.replacedByAgentId.trim() : "";
46361
+ const next = {
46362
+ ...metadata ?? {},
46363
+ staleLocalRegistration: true,
46364
+ staleAt
46365
+ };
46366
+ if (replacementAgentId) {
46367
+ next.replacedByAgentId = replacementAgentId;
46368
+ } else if (existingReplacement) {
46369
+ next.replacedByAgentId = existingReplacement;
46370
+ } else {
46371
+ delete next.replacedByAgentId;
46372
+ }
46373
+ return next;
46374
+ }
46375
+ function clearStaleLocalEndpointMetadata(metadata) {
46376
+ const {
46377
+ staleLocalRegistration,
46378
+ staleAt,
46379
+ replacedByAgentId,
46380
+ ...rest
46381
+ } = metadata ?? {};
46382
+ return rest;
46383
+ }
44666
46384
  async function archiveStaleRegisteredLocalAgents(bindings) {
44667
46385
  const activeAgentIds = new Set(bindings.map((binding) => binding.agent.id));
44668
46386
  const activeAgentIdsByDefinition = bindings.reduce((map2, binding) => {
@@ -44685,48 +46403,36 @@ async function archiveStaleRegisteredLocalAgents(bindings) {
44685
46403
  if (agent?.authorityNodeId && agent.authorityNodeId !== nodeId) {
44686
46404
  continue;
44687
46405
  }
46406
+ if (isLocalAgentEndpointAlive(endpoint)) {
46407
+ if (endpoint.metadata?.staleLocalRegistration === true) {
46408
+ await persistEndpoint({
46409
+ ...endpoint,
46410
+ metadata: clearStaleLocalEndpointMetadata(endpoint.metadata)
46411
+ });
46412
+ }
46413
+ continue;
46414
+ }
44688
46415
  const replacementAgentId = staleLocalAgentReplacementId(typeof agent?.definitionId === "string" ? agent.definitionId : null, activeAgentIdsByDefinition);
44689
- if (endpoint.state === "offline" && endpoint.metadata?.staleLocalRegistration === true && replacementAgentId && endpoint.metadata?.replacedByAgentId === replacementAgentId) {
46416
+ if (agent && !staleRegistrationMetadataMatches(agent.metadata, replacementAgentId)) {
46417
+ await upsertAgentDurably({
46418
+ ...agent,
46419
+ metadata: staleLocalRegistrationMetadata(agent.metadata, staleAt, replacementAgentId)
46420
+ });
46421
+ }
46422
+ if (endpoint.state === "offline" && staleRegistrationMetadataMatches(endpoint.metadata, replacementAgentId)) {
44690
46423
  continue;
44691
46424
  }
44692
46425
  await persistEndpoint({
44693
46426
  ...endpoint,
44694
46427
  state: "offline",
44695
46428
  metadata: {
44696
- ...endpoint.metadata ?? {},
44697
- staleLocalRegistration: true,
44698
- staleAt,
44699
- replacedByAgentId: replacementAgentId ?? endpoint.metadata?.replacedByAgentId,
46429
+ ...staleLocalRegistrationMetadata(endpoint.metadata, staleAt, replacementAgentId),
44700
46430
  lastError: "stale local agent registration superseded by current setup",
44701
46431
  lastFailedAt: staleAt
44702
46432
  }
44703
46433
  });
44704
46434
  console.log(`[openscout-runtime] archived stale local endpoint ${endpoint.id}`);
44705
46435
  }
44706
- for (const agent of Object.values(snapshot.agents)) {
44707
- if (activeAgentIds.has(agent.id) || !isGeneratedLocalAgentMetadata(agent.metadata)) {
44708
- continue;
44709
- }
44710
- if (agent.authorityNodeId && agent.authorityNodeId !== nodeId) {
44711
- continue;
44712
- }
44713
- const replacementAgentId = staleLocalAgentReplacementId(agent.definitionId, activeAgentIdsByDefinition);
44714
- const nextMetadata = {
44715
- ...agent.metadata ?? {},
44716
- staleLocalRegistration: true,
44717
- staleAt,
44718
- replacedByAgentId: replacementAgentId ?? agent.metadata?.replacedByAgentId
44719
- };
44720
- if (agent.metadata?.staleLocalRegistration === true && nextMetadata.replacedByAgentId === agent.metadata?.replacedByAgentId) {
44721
- continue;
44722
- }
44723
- const nextAgent = {
44724
- ...agent,
44725
- metadata: nextMetadata
44726
- };
44727
- await upsertAgentDurably(nextAgent);
44728
- console.log(`[openscout-runtime] archived stale local agent ${agent.id}`);
44729
- }
44730
46436
  }
44731
46437
  async function syncRegisteredLocalAgents() {
44732
46438
  const bindings = await loadRegisteredLocalAgentBindings(nodeId);
@@ -44741,6 +46447,7 @@ async function syncRegisteredLocalAgents() {
44741
46447
  }
44742
46448
  await archiveStaleRegisteredLocalAgents(bindings);
44743
46449
  await reconcileStaleWorkingFlights();
46450
+ await reconcileStaleLocalDeliveries();
44744
46451
  const snapshot = runtime.snapshot();
44745
46452
  for (const endpoint of Object.values(snapshot.endpoints)) {
44746
46453
  if (endpoint.transport === "tmux") {
@@ -45115,6 +46822,49 @@ function hasReachableMeshEntrypoint(node) {
45115
46822
  function isReachableMeshNode(node) {
45116
46823
  return Boolean(node?.brokerUrl || hasReachableMeshEntrypoint(node));
45117
46824
  }
46825
+ function meshNodeLastSeenAt(node) {
46826
+ return typeof node?.lastSeenAt === "number" && Number.isFinite(node.lastSeenAt) ? node.lastSeenAt : typeof node?.registeredAt === "number" && Number.isFinite(node.registeredAt) ? node.registeredAt : 0;
46827
+ }
46828
+ function isStaleMeshAuthorityNode(node) {
46829
+ if (!node) {
46830
+ return false;
46831
+ }
46832
+ const lastSeenAt = meshNodeLastSeenAt(node);
46833
+ return lastSeenAt > 0 && Date.now() - lastSeenAt > STALE_MESH_AUTHORITY_NODE_MS;
46834
+ }
46835
+ function formatMeshNodeLastSeen(node) {
46836
+ const lastSeenAt = meshNodeLastSeenAt(node);
46837
+ if (!lastSeenAt) {
46838
+ return "with no recent heartbeat";
46839
+ }
46840
+ return `last seen ${new Date(lastSeenAt).toISOString()}`;
46841
+ }
46842
+ function describeRemoteAuthorityIssue(agent, authorityNode) {
46843
+ const displayName = agent.displayName ?? agent.id;
46844
+ const authorityNodeId = agent.authorityNodeId;
46845
+ if (!authorityNodeId || authorityNodeId === nodeId) {
46846
+ return null;
46847
+ }
46848
+ const nodeLabel = authorityNode?.name ? `${authorityNode.name} (${authorityNodeId})` : authorityNodeId;
46849
+ const unavailable = !authorityNode || !isReachableMeshNode(authorityNode);
46850
+ const stale = isStaleMeshAuthorityNode(authorityNode);
46851
+ if (!unavailable && !stale) {
46852
+ return null;
46853
+ }
46854
+ const endpoint = homeEndpointForAgent(runtime.snapshot(), agent.id);
46855
+ const projectRoot = brokerTargetProjectRoot(agent, endpoint);
46856
+ const detail = unavailable ? `${displayName} belongs to peer node ${nodeLabel}, but that peer has no reachable broker URL or mesh entrypoint.` : `${displayName} belongs to peer node ${nodeLabel}, but that peer is stale (${formatMeshNodeLastSeen(authorityNode)}).`;
46857
+ return {
46858
+ agentId: agent.id,
46859
+ displayName,
46860
+ reason: "unknown",
46861
+ detail,
46862
+ wakePolicy: agent.wakePolicy,
46863
+ endpointState: endpoint?.state === "active" || endpoint?.state === "idle" || endpoint?.state === "waiting" ? "online" : endpoint?.state === "offline" ? "offline" : "unknown",
46864
+ transport: endpoint?.transport ?? null,
46865
+ projectRoot
46866
+ };
46867
+ }
45118
46868
  function authorityNodeForConversation(conversationId) {
45119
46869
  const conversation = runtime.conversation(conversationId);
45120
46870
  if (!conversation || conversation.authorityNodeId === nodeId) {
@@ -45189,6 +46939,30 @@ async function maybeForwardFlightToAuthority(flight) {
45189
46939
  }
45190
46940
  await brokerPostJson(authority.authorityNode.brokerUrl, "/v1/flights", flight);
45191
46941
  }
46942
+ async function retireConsumedOneTimeCardsForMessage(message) {
46943
+ if (message.class !== "agent" || message.actorId === systemActor.id) {
46944
+ return;
46945
+ }
46946
+ const conversation = runtime.conversation(message.conversationId);
46947
+ if (!conversation || conversation.kind !== "direct") {
46948
+ return;
46949
+ }
46950
+ const retired = await retireConsumedOneTimeLocalAgentCards({
46951
+ conversationId: conversation.id,
46952
+ actorId: message.actorId,
46953
+ participantIds: conversation.participantIds
46954
+ }).catch((error48) => {
46955
+ console.warn(`[openscout-runtime] one-time card cleanup failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
46956
+ return [];
46957
+ });
46958
+ if (retired.length === 0) {
46959
+ return;
46960
+ }
46961
+ console.log(`[openscout-runtime] retired ${retired.length} consumed one-time card${retired.length === 1 ? "" : "s"}`);
46962
+ await syncRegisteredLocalAgentsIfChanged("one-time card consumed").catch((error48) => {
46963
+ console.warn(`[openscout-runtime] one-time card broker sync failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
46964
+ });
46965
+ }
45192
46966
  async function postConversationMessage(message) {
45193
46967
  const authority = authorityNodeForConversation(message.conversationId);
45194
46968
  if (authority) {
@@ -45205,6 +46979,9 @@ async function postConversationMessage(message) {
45205
46979
  });
45206
46980
  await forwardPeerBrokerDeliveries(message, deliveries2);
45207
46981
  await applyProjectedEntries(entries);
46982
+ await reconcileStaleLocalDeliveries();
46983
+ await retireConsumedOneTimeCardsForMessage(message);
46984
+ await completeInvocationsForBrokerReply(message);
45208
46985
  return { ok: true, message, deliveries: deliveries2 };
45209
46986
  }
45210
46987
  async function postInvocationStatusMessage(invocation, flight) {
@@ -45250,10 +47027,69 @@ function existingBrokerReplyForInvocation(invocation, agentId, sinceMs) {
45250
47027
  const replies = Object.values(runtime.peek().messages).filter((message) => message.conversationId === invocation.conversationId && message.replyToMessageId === invocation.messageId && message.actorId === agentId && message.class === "agent" && message.createdAt >= since).sort((lhs, rhs) => rhs.createdAt - lhs.createdAt);
45251
47028
  return replies[0] ?? null;
45252
47029
  }
45253
- function activeLocalEndpointForAgent(agentId, harness) {
47030
+ function messageAnswersInvocation(message, invocation) {
47031
+ if (invocation.action === "wake") {
47032
+ return false;
47033
+ }
47034
+ if (!invocation.conversationId || !invocation.messageId) {
47035
+ return false;
47036
+ }
47037
+ return message.class === "agent" && message.actorId === invocation.targetAgentId && message.conversationId === invocation.conversationId && message.replyToMessageId === invocation.messageId && message.body.trim().length > 0;
47038
+ }
47039
+ function completedFlightFromBrokerReply(invocation, flight, reply) {
47040
+ const agent = runtime.agent(invocation.targetAgentId);
47041
+ const replySource = metadataStringValue2(reply.metadata, "source");
47042
+ return {
47043
+ ...flight,
47044
+ state: "completed",
47045
+ summary: `${agent?.displayName ?? invocation.targetAgentId} replied.`,
47046
+ output: reply.body,
47047
+ error: undefined,
47048
+ completedAt: reply.createdAt,
47049
+ metadata: {
47050
+ ...flight.metadata ?? {},
47051
+ completedByBrokerReply: true,
47052
+ replyMessageId: reply.id,
47053
+ ...replySource ? { replySource } : {}
47054
+ }
47055
+ };
47056
+ }
47057
+ async function completeInvocationForBrokerReply(invocation, reply) {
47058
+ const flight = runtime.flightForInvocation(invocation.id);
47059
+ if (!flight || !isWorkingFlightState(flight.state)) {
47060
+ return false;
47061
+ }
47062
+ const startedAt = flight.startedAt ?? invocation.createdAt;
47063
+ if (reply.createdAt < Math.max(0, startedAt - 5000)) {
47064
+ return false;
47065
+ }
47066
+ await persistFlight(completedFlightFromBrokerReply(invocation, flight, reply));
47067
+ return true;
47068
+ }
47069
+ async function completeInvocationsForBrokerReply(message) {
47070
+ if (message.class !== "agent" || !message.replyToMessageId || !message.body.trim()) {
47071
+ return;
47072
+ }
47073
+ const invocations2 = Object.values(runtime.snapshot().invocations).filter((invocation) => messageAnswersInvocation(message, invocation));
47074
+ for (const invocation of invocations2) {
47075
+ await completeInvocationForBrokerReply(invocation, message);
47076
+ }
47077
+ }
47078
+ function endpointMatchesTargetSession(endpoint, sessionId) {
47079
+ return endpoint.sessionId?.trim() === sessionId || endpoint.id === sessionId;
47080
+ }
47081
+ function invocationTargetSessionId(invocation) {
47082
+ return invocation.execution?.targetSessionId?.trim() || metadataStringValue2(invocation.metadata, "targetSessionId") || undefined;
47083
+ }
47084
+ function activeLocalEndpointForAgent(agentId, harness, targetSessionId) {
45254
47085
  const candidates = runtime.endpointsForAgent(agentId, {
45255
47086
  nodeId,
45256
47087
  harness
47088
+ }).filter((endpoint) => {
47089
+ if (endpoint.metadata?.staleLocalRegistration === true) {
47090
+ return false;
47091
+ }
47092
+ return targetSessionId ? endpointMatchesTargetSession(endpoint, targetSessionId) : true;
45257
47093
  });
45258
47094
  return candidates.find((endpoint) => endpoint.transport === "pairing_bridge" ? endpoint.state !== "offline" : isLocalAgentEndpointAlive(endpoint));
45259
47095
  }
@@ -45284,17 +47120,64 @@ function onlineConversationNotifyTargets(conversation, requesterId) {
45284
47120
  return Boolean(activeLocalEndpointForAgent(participantId));
45285
47121
  });
45286
47122
  }
47123
+ async function reviveManagedLocalSessionEndpoint(endpoint) {
47124
+ if (!isManagedLocalSessionMetadata(endpoint.metadata)) {
47125
+ return null;
47126
+ }
47127
+ const sessionResult = await ensureLocalSessionEndpointOnline(endpoint);
47128
+ const externalSessionId = sessionResult.externalSessionId?.trim();
47129
+ const { lastError: _lastError, lastFailedAt: _lastFailedAt, ...baseMetadata } = endpoint.metadata ?? {};
47130
+ const revivedEndpoint = {
47131
+ ...endpoint,
47132
+ state: "idle",
47133
+ ...externalSessionId ? { sessionId: externalSessionId } : {},
47134
+ metadata: {
47135
+ ...baseMetadata,
47136
+ lastResumedAt: Date.now(),
47137
+ ...externalSessionId ? {
47138
+ externalSessionId,
47139
+ ...endpoint.transport === "codex_app_server" ? { threadId: externalSessionId } : {}
47140
+ } : {}
47141
+ }
47142
+ };
47143
+ await persistEndpoint(revivedEndpoint);
47144
+ return revivedEndpoint;
47145
+ }
45287
47146
  async function resolveLocalEndpointForInvocation(invocation) {
45288
47147
  const requestedHarness = invocation.execution?.harness;
47148
+ const targetSessionId = invocationTargetSessionId(invocation);
45289
47149
  const sessionPreference = invocation.execution?.session ?? "new";
45290
- const existing = activeLocalEndpointForAgent(invocation.targetAgentId, requestedHarness);
47150
+ const existing = activeLocalEndpointForAgent(invocation.targetAgentId, requestedHarness, targetSessionId);
45291
47151
  if (existing && (sessionPreference !== "new" || existing.transport === "pairing_bridge")) {
45292
47152
  return existing;
45293
47153
  }
45294
47154
  const staleEndpoints = runtime.endpointsForAgent(invocation.targetAgentId, {
45295
47155
  nodeId,
45296
47156
  harness: requestedHarness
45297
- }).filter((endpoint) => endpoint.id !== existing?.id);
47157
+ }).filter((endpoint) => endpoint.id !== existing?.id && (targetSessionId ? endpointMatchesTargetSession(endpoint, targetSessionId) : true));
47158
+ const staleLocalReason = staleEndpoints.map((endpoint) => staleLocalEndpointReason(endpoint)).find((reason) => Boolean(reason));
47159
+ if (staleLocalReason) {
47160
+ throw new Error(staleLocalReason);
47161
+ }
47162
+ if (invocation.ensureAwake) {
47163
+ for (const endpoint of staleEndpoints) {
47164
+ try {
47165
+ const revived = await reviveManagedLocalSessionEndpoint(endpoint);
47166
+ if (revived)
47167
+ return revived;
47168
+ } catch (error48) {
47169
+ await persistEndpoint({
47170
+ ...endpoint,
47171
+ state: "offline",
47172
+ metadata: {
47173
+ ...endpoint.metadata ?? {},
47174
+ lastError: error48 instanceof Error ? error48.message : String(error48),
47175
+ lastFailedAt: Date.now()
47176
+ }
47177
+ });
47178
+ }
47179
+ }
47180
+ }
45298
47181
  for (const endpoint of staleEndpoints) {
45299
47182
  await persistEndpoint({
45300
47183
  ...endpoint,
@@ -45312,34 +47195,8 @@ async function resolveLocalEndpointForInvocation(invocation) {
45312
47195
  if (sessionPreference === "existing") {
45313
47196
  return;
45314
47197
  }
45315
- for (const endpoint of staleEndpoints) {
45316
- if (!isManagedLocalSessionMetadata(endpoint.metadata)) {
45317
- continue;
45318
- }
45319
- try {
45320
- const sessionResult = await ensureLocalSessionEndpointOnline(endpoint);
45321
- const revivedEndpoint = {
45322
- ...endpoint,
45323
- state: "idle",
45324
- metadata: {
45325
- ...endpoint.metadata ?? {},
45326
- lastResumedAt: Date.now(),
45327
- ...sessionResult.externalSessionId ? { externalSessionId: sessionResult.externalSessionId } : {}
45328
- }
45329
- };
45330
- await persistEndpoint(revivedEndpoint);
45331
- return revivedEndpoint;
45332
- } catch (error48) {
45333
- await persistEndpoint({
45334
- ...endpoint,
45335
- state: "offline",
45336
- metadata: {
45337
- ...endpoint.metadata ?? {},
45338
- lastError: error48 instanceof Error ? error48.message : String(error48),
45339
- lastFailedAt: Date.now()
45340
- }
45341
- });
45342
- }
47198
+ if (targetSessionId) {
47199
+ return;
45343
47200
  }
45344
47201
  const binding = await ensureLocalAgentBindingOnline(invocation.targetAgentId, nodeId, {
45345
47202
  includeDiscovered: true,
@@ -45358,7 +47215,7 @@ async function resolveLocalEndpointForInvocation(invocation) {
45358
47215
  async function executeLocalInvocation(invocation, initialFlight) {
45359
47216
  const agent = runtime.agent(invocation.targetAgentId);
45360
47217
  let endpoint;
45361
- const previousEndpoint = activeLocalEndpointForAgent(invocation.targetAgentId, invocation.execution?.harness);
47218
+ const previousEndpoint = activeLocalEndpointForAgent(invocation.targetAgentId, invocation.execution?.harness, invocationTargetSessionId(invocation));
45362
47219
  try {
45363
47220
  endpoint = await resolveLocalEndpointForInvocation(invocation);
45364
47221
  } catch (error48) {
@@ -45379,6 +47236,24 @@ async function executeLocalInvocation(invocation, initialFlight) {
45379
47236
  return;
45380
47237
  }
45381
47238
  if (!agent || !endpoint) {
47239
+ const staleEndpointReason = staleLocalEndpointReason(latestEndpointForAgent(runtime.snapshot(), invocation.targetAgentId));
47240
+ if (staleEndpointReason) {
47241
+ const failedFlight = {
47242
+ ...initialFlight,
47243
+ state: "failed",
47244
+ summary: `${agent?.displayName ?? invocation.targetAgentId} could not be prepared.`,
47245
+ error: `Endpoint resolution failed before execution: ${staleEndpointReason}`,
47246
+ completedAt: Date.now(),
47247
+ metadata: {
47248
+ ...initialFlight.metadata ?? {},
47249
+ failureStage: "endpoint_resolution",
47250
+ staleLocalRegistration: true
47251
+ }
47252
+ };
47253
+ await persistFlight(failedFlight);
47254
+ await postInvocationStatusMessage(invocation, failedFlight);
47255
+ return;
47256
+ }
45382
47257
  const queuedFlight = {
45383
47258
  ...initialFlight,
45384
47259
  state: "queued",
@@ -45436,6 +47311,20 @@ async function executeLocalInvocation(invocation, initialFlight) {
45436
47311
  await persistFlight(runningFlight);
45437
47312
  try {
45438
47313
  const result = endpoint.transport === "pairing_bridge" ? await invokePairingSessionEndpoint(runningEndpoint, invocation) : await invokeLocalAgentEndpoint(runningEndpoint, invocation);
47314
+ const rawResultExternalSessionId = "externalSessionId" in result ? result.externalSessionId : undefined;
47315
+ const resultExternalSessionId = typeof rawResultExternalSessionId === "string" && rawResultExternalSessionId.trim() ? rawResultExternalSessionId.trim() : undefined;
47316
+ const completedEndpoint = {
47317
+ ...runningEndpoint,
47318
+ ...resultExternalSessionId ? { sessionId: resultExternalSessionId } : {},
47319
+ metadata: {
47320
+ ...runningEndpoint.metadata ?? {},
47321
+ lastCompletedAt: Date.now(),
47322
+ ...resultExternalSessionId ? {
47323
+ externalSessionId: resultExternalSessionId,
47324
+ ...runningEndpoint.transport === "codex_app_server" ? { threadId: resultExternalSessionId } : {}
47325
+ } : {}
47326
+ }
47327
+ };
45439
47328
  if (invocation.action === "wake") {
45440
47329
  const completedFlight2 = {
45441
47330
  ...runningFlight,
@@ -45446,12 +47335,16 @@ async function executeLocalInvocation(invocation, initialFlight) {
45446
47335
  };
45447
47336
  await persistFlight(completedFlight2);
45448
47337
  await persistEndpoint({
45449
- ...runningEndpoint,
45450
- state: "idle",
45451
- metadata: {
45452
- ...runningEndpoint.metadata ?? {},
45453
- lastCompletedAt: Date.now()
45454
- }
47338
+ ...completedEndpoint,
47339
+ state: "idle"
47340
+ });
47341
+ return;
47342
+ }
47343
+ const currentFlight = runtime.flightForInvocation(invocation.id);
47344
+ if (currentFlight && isTerminalFlightState(currentFlight.state)) {
47345
+ await persistEndpoint({
47346
+ ...completedEndpoint,
47347
+ state: "idle"
45455
47348
  });
45456
47349
  return;
45457
47350
  }
@@ -45491,12 +47384,8 @@ async function executeLocalInvocation(invocation, initialFlight) {
45491
47384
  };
45492
47385
  await persistFlight(completedFlight);
45493
47386
  await persistEndpoint({
45494
- ...runningEndpoint,
45495
- state: "idle",
45496
- metadata: {
45497
- ...runningEndpoint.metadata ?? {},
45498
- lastCompletedAt: Date.now()
45499
- }
47387
+ ...completedEndpoint,
47388
+ state: "idle"
45500
47389
  });
45501
47390
  if (invocation.conversationId && !postedReply) {
45502
47391
  const conversation = runtime.conversation(invocation.conversationId);
@@ -45519,6 +47408,7 @@ async function executeLocalInvocation(invocation, initialFlight) {
45519
47408
  invocationId: invocation.id,
45520
47409
  flightId: completedFlight.id,
45521
47410
  source: "broker",
47411
+ ...scoutbotReplyProvenanceMetadata(invocation),
45522
47412
  returnAddress: buildScoutReturnAddress({
45523
47413
  actorId: agent.id,
45524
47414
  handle: agent.handle?.trim() || agent.definitionId,
@@ -45527,19 +47417,19 @@ async function executeLocalInvocation(invocation, initialFlight) {
45527
47417
  defaultSelector: agent.defaultSelector,
45528
47418
  conversationId: invocation.conversationId,
45529
47419
  replyToMessageId: invocation.messageId,
45530
- nodeId: runningEndpoint.nodeId,
45531
- projectRoot: runningEndpoint.projectRoot ?? runningEndpoint.cwd,
45532
- sessionId: runningEndpoint.sessionId
47420
+ nodeId: completedEndpoint.nodeId,
47421
+ projectRoot: completedEndpoint.projectRoot ?? completedEndpoint.cwd,
47422
+ sessionId: completedEndpoint.sessionId
45533
47423
  }),
45534
47424
  requestedReturnAddress: invocation.metadata?.["returnAddress"],
45535
- responderHarness: runningEndpoint.harness,
45536
- responderTransport: runningEndpoint.transport,
45537
- responderSessionId: runningEndpoint.sessionId ?? "",
45538
- responderCwd: runningEndpoint.cwd ?? "",
45539
- responderProjectRoot: runningEndpoint.projectRoot ?? "",
45540
- responderAgentName: String(runningEndpoint.metadata?.agentName ?? agent.id),
45541
- responderStartedAt: String(runningEndpoint.metadata?.startedAt ?? ""),
45542
- responderNodeId: runningEndpoint.nodeId
47425
+ responderHarness: completedEndpoint.harness,
47426
+ responderTransport: completedEndpoint.transport,
47427
+ responderSessionId: completedEndpoint.sessionId ?? "",
47428
+ responderCwd: completedEndpoint.cwd ?? "",
47429
+ responderProjectRoot: completedEndpoint.projectRoot ?? "",
47430
+ responderAgentName: String(completedEndpoint.metadata?.agentName ?? agent.id),
47431
+ responderStartedAt: String(completedEndpoint.metadata?.startedAt ?? ""),
47432
+ responderNodeId: completedEndpoint.nodeId
45543
47433
  }
45544
47434
  });
45545
47435
  }
@@ -45547,10 +47437,18 @@ async function executeLocalInvocation(invocation, initialFlight) {
45547
47437
  } catch (error48) {
45548
47438
  const message = error48 instanceof Error ? error48.message : String(error48);
45549
47439
  if (isRequesterWaitTimeoutError(error48)) {
47440
+ const currentFlight = runtime.flightForInvocation(invocation.id);
47441
+ if (currentFlight && isTerminalFlightState(currentFlight.state)) {
47442
+ return;
47443
+ }
47444
+ const postedReply = existingBrokerReplyForInvocation(invocation, agent.id, runningFlight.startedAt ?? Date.now());
47445
+ if (postedReply && await completeInvocationForBrokerReply(invocation, postedReply)) {
47446
+ return;
47447
+ }
45550
47448
  const waitingFlight = {
45551
47449
  ...runningFlight,
45552
47450
  state: "waiting",
45553
- summary: `${agent.displayName} is still working; the requester stopped waiting after ${error48.timeoutMs}ms.`,
47451
+ summary: `${agent.displayName} is still working; Scout stopped waiting for a synchronous result after ${error48.timeoutMs}ms.`,
45554
47452
  error: undefined,
45555
47453
  completedAt: undefined,
45556
47454
  metadata: {
@@ -45561,7 +47459,71 @@ async function executeLocalInvocation(invocation, initialFlight) {
45561
47459
  }
45562
47460
  };
45563
47461
  await persistFlight(waitingFlight);
45564
- await postInvocationStatusMessage(invocation, waitingFlight);
47462
+ console.warn(`[openscout-runtime] ${waitingFlight.summary}`);
47463
+ return;
47464
+ }
47465
+ if (isDispatchStalledError(error48)) {
47466
+ const stalledFlight = {
47467
+ ...runningFlight,
47468
+ state: "failed",
47469
+ summary: `${agent.displayName} dispatch stalled \u2014 prompt left in composer after submit + retry.`,
47470
+ error: message,
47471
+ completedAt: Date.now(),
47472
+ metadata: {
47473
+ ...runningFlight.metadata ?? {},
47474
+ failureStage: "dispatch_stalled",
47475
+ dispatchStalledSession: error48.sessionName,
47476
+ dispatchStalledRetries: error48.retries,
47477
+ dispatchStalledPaneTail: error48.paneTail.slice(0, 1000)
47478
+ }
47479
+ };
47480
+ await persistFlight(stalledFlight);
47481
+ await persistEndpoint({
47482
+ ...runningEndpoint,
47483
+ state: "offline",
47484
+ metadata: {
47485
+ ...runningEndpoint.metadata ?? {},
47486
+ lastError: message,
47487
+ lastFailedAt: Date.now(),
47488
+ lastFailureStage: "dispatch_stalled"
47489
+ }
47490
+ });
47491
+ await postInvocationStatusMessage(invocation, stalledFlight);
47492
+ return;
47493
+ }
47494
+ if (isCodexAppServerExitError(error48) && error48.noteworthy) {
47495
+ const interruptedAt = Date.now();
47496
+ const failureStage = error48.exitKind === "proactive_shutdown" ? "codex_app_server_proactive_shutdown" : "codex_app_server_sigterm";
47497
+ const summary = error48.exitKind === "proactive_shutdown" ? `${agent.displayName} was stopped by OpenScout before it could reply.` : `${agent.displayName} was interrupted by a local Codex app-server SIGTERM.`;
47498
+ const interruptedFlight = {
47499
+ ...runningFlight,
47500
+ state: "failed",
47501
+ summary,
47502
+ error: undefined,
47503
+ completedAt: interruptedAt,
47504
+ metadata: {
47505
+ ...runningFlight.metadata ?? {},
47506
+ failureStage,
47507
+ failureSeverity: "noteworthy",
47508
+ noteworthy: true,
47509
+ exitKind: error48.exitKind,
47510
+ exitSignal: error48.signal,
47511
+ exitCode: error48.exitCode,
47512
+ ...error48.reason ? { shutdownReason: error48.reason } : {}
47513
+ }
47514
+ };
47515
+ await persistFlight(interruptedFlight);
47516
+ await persistEndpoint({
47517
+ ...runningEndpoint,
47518
+ state: "offline",
47519
+ metadata: {
47520
+ ...runningEndpoint.metadata ?? {},
47521
+ lastNotice: message,
47522
+ lastInterruptedAt: interruptedAt,
47523
+ lastInterruptionStage: failureStage
47524
+ }
47525
+ });
47526
+ await postInvocationStatusMessage(invocation, interruptedFlight);
45565
47527
  return;
45566
47528
  }
45567
47529
  const failedFlight = {
@@ -45861,6 +47823,7 @@ async function handleCommand(command) {
45861
47823
  });
45862
47824
  const mesh2 = await forwardPeerBrokerDeliveries(command.message, deliveries2);
45863
47825
  await applyProjectedEntries(entries);
47826
+ await reconcileStaleLocalDeliveries();
45864
47827
  console.log(`[openscout-runtime] message ${command.message.id} posted by ${command.message.actorId} to ${command.message.conversationId} with ${deliveries2.length} deliveries`);
45865
47828
  return { ok: true, message: command.message, deliveries: deliveries2, mesh: mesh2 };
45866
47829
  }
@@ -45893,7 +47856,7 @@ async function handleCommand(command) {
45893
47856
  }
45894
47857
  async function handleInvocationRequest(payload) {
45895
47858
  await syncRegisteredLocalAgentsIfChanged("invocation");
45896
- const resolved = resolveInvocationTarget(payload);
47859
+ const resolved = await resolveInvocationTarget(payload);
45897
47860
  if (resolved.kind !== "resolved") {
45898
47861
  const envelope = buildDispatchEnvelope(resolved, askedLabelForRouteTarget(payload), nodeId, runtime.snapshot(), { homeEndpointFor: homeEndpointForAgent });
45899
47862
  const { record: record2 } = await recordScoutDispatchDurably(envelope, {
@@ -45944,8 +47907,9 @@ async function dispatchAcceptedInvocation(invocation) {
45944
47907
  }
45945
47908
  if (targetAgent.authorityNodeId && targetAgent.authorityNodeId !== nodeId) {
45946
47909
  const authorityNode = runtime.node(targetAgent.authorityNodeId);
45947
- if (!authorityNode) {
45948
- await failAcceptedInvocation(invocation, `authority node ${targetAgent.authorityNodeId} is not reachable`);
47910
+ const authorityIssue = describeRemoteAuthorityIssue(targetAgent, authorityNode);
47911
+ if (authorityIssue) {
47912
+ await failAcceptedInvocation(invocation, authorityIssue.detail);
45949
47913
  return;
45950
47914
  }
45951
47915
  await peerDelivery.enqueue(invocation, authorityNode);
@@ -46361,6 +48325,17 @@ async function routeRequest(request, response) {
46361
48325
  json2(response, 200, journal.listDeliveryAttempts(deliveryId));
46362
48326
  return;
46363
48327
  }
48328
+ const invocationLifecycleMatch = method === "GET" ? url2.pathname.match(/^\/v1\/invocations\/([^/]+)\/lifecycle$/) : null;
48329
+ if (invocationLifecycleMatch) {
48330
+ const invocationId = decodeURIComponent(invocationLifecycleMatch[1] ?? "");
48331
+ const lifecycle2 = await brokerService.readInvocationLifecycle?.({ invocationId });
48332
+ if (!lifecycle2) {
48333
+ notFound(response);
48334
+ return;
48335
+ }
48336
+ json2(response, 200, lifecycle2);
48337
+ return;
48338
+ }
46364
48339
  if (method === "GET" && url2.pathname === "/v1/mesh/nodes") {
46365
48340
  json2(response, 200, runtime.snapshot().nodes);
46366
48341
  return;
@@ -46713,6 +48688,39 @@ data: ${JSON.stringify({ nodeId, meshId })}
46713
48688
  }
46714
48689
  return;
46715
48690
  }
48691
+ if (method === "POST" && url2.pathname === "/v1/local-sessions/ensure") {
48692
+ try {
48693
+ const input = await readRequestBody(request);
48694
+ const snapshot = runtime.snapshot();
48695
+ const endpoint = input.endpointId?.trim() ? snapshot.endpoints[input.endpointId.trim()] : Object.values(snapshot.endpoints).find((candidate) => candidate.agentId === input.agentId?.trim() && candidate.nodeId === nodeId && (candidate.transport === "codex_app_server" || candidate.transport === "claude_stream_json") && candidate.state !== "offline");
48696
+ if (!endpoint) {
48697
+ throw new Error("local session endpoint not found");
48698
+ }
48699
+ if (endpoint.transport !== "codex_app_server" && endpoint.transport !== "claude_stream_json") {
48700
+ throw new Error(`endpoint ${endpoint.id} does not use a local session transport`);
48701
+ }
48702
+ const sessionResult = await ensureLocalSessionEndpointOnline(endpoint);
48703
+ const externalSessionId = sessionResult.externalSessionId?.trim();
48704
+ const nextEndpoint = {
48705
+ ...endpoint,
48706
+ state: endpoint.state === "offline" ? "waiting" : endpoint.state,
48707
+ ...externalSessionId ? { sessionId: externalSessionId } : {},
48708
+ metadata: {
48709
+ ...endpoint.metadata ?? {},
48710
+ ...externalSessionId ? {
48711
+ externalSessionId,
48712
+ threadId: endpoint.transport === "codex_app_server" ? externalSessionId : endpoint.metadata?.threadId
48713
+ } : {},
48714
+ lastEnsuredAt: Date.now()
48715
+ }
48716
+ };
48717
+ await persistEndpoint(nextEndpoint);
48718
+ json2(response, 200, { ok: true, endpoint: nextEndpoint, externalSessionId: externalSessionId ?? null });
48719
+ } catch (error48) {
48720
+ badRequest(response, error48);
48721
+ }
48722
+ return;
48723
+ }
46716
48724
  if (method === "POST" && url2.pathname === "/v1/local-sessions/detach") {
46717
48725
  try {
46718
48726
  const input = await readRequestBody(request);
@@ -47042,18 +49050,99 @@ function callerContextForDelivery(payload) {
47042
49050
  requesterNodeId: payload.caller?.nodeId?.trim() || payload.requesterNodeId?.trim() || nodeId
47043
49051
  };
47044
49052
  }
49053
+ function agentLabelForRouteParams(payload) {
49054
+ if (payload.target?.kind === "agent_label") {
49055
+ return payload.target.label;
49056
+ }
49057
+ if (!payload.target && payload.targetLabel?.trim()) {
49058
+ return payload.targetLabel;
49059
+ }
49060
+ return;
49061
+ }
49062
+ function supportedRouteHarness(value) {
49063
+ const normalized = value?.trim();
49064
+ if (normalized && SUPPORTED_SCOUT_HARNESSES.includes(normalized)) {
49065
+ return normalized;
49066
+ }
49067
+ return;
49068
+ }
49069
+ function executionWithRouteParams(payload) {
49070
+ const label = agentLabelForRouteParams(payload);
49071
+ const identity2 = label ? parseAgentIdentity(label.startsWith("@") ? label : `@${label}`) : null;
49072
+ const harness = supportedRouteHarness(identity2?.harness);
49073
+ if (!harness || payload.execution?.harness) {
49074
+ return payload.execution;
49075
+ }
49076
+ return {
49077
+ ...payload.execution ?? {},
49078
+ harness
49079
+ };
49080
+ }
47045
49081
  function resolveBrokerDeliveryTarget(input) {
47046
49082
  return resolveBrokerRouteTarget(runtime.snapshot(), input, {
47047
49083
  preferLocalNodeId: nodeId,
47048
- helpers: { isStale: isStaleLocalAgent }
49084
+ helpers: { isStale: isInactiveLocalAgent }
47049
49085
  });
47050
49086
  }
47051
- function resolveInvocationTarget(payload) {
47052
- return resolveBrokerDeliveryTarget({
49087
+ function projectPathRouteTarget(input) {
49088
+ return input.target?.kind === "project_path" ? input.target.projectPath.trim() || undefined : undefined;
49089
+ }
49090
+ function implicitProjectAgentName(projectPath) {
49091
+ const base = normalizeAgentSelectorSegment(basename11(projectPath)) || "agent";
49092
+ return `${base}-card-${createRuntimeId2("one").slice(-8)}`;
49093
+ }
49094
+ async function resolveBrokerDeliveryTargetWithImplicitProjectCard(input, options) {
49095
+ const resolved = resolveBrokerDeliveryTarget(input);
49096
+ const projectPath = projectPathRouteTarget(input);
49097
+ const shouldCreateImplicitProjectCard = projectPath && (resolved.kind === "unknown" || resolved.kind === "ambiguous" && (input.execution?.session ?? "new") === "new");
49098
+ if (!shouldCreateImplicitProjectCard) {
49099
+ return resolved;
49100
+ }
49101
+ const createdAt = Date.now();
49102
+ const requesterId = options.requesterId?.trim();
49103
+ const status = await startLocalAgent({
49104
+ projectPath,
49105
+ agentName: implicitProjectAgentName(projectPath),
49106
+ currentDirectory: options.currentDirectory ?? projectPath,
49107
+ harness: input.execution?.harness,
49108
+ ensureOnline: false,
49109
+ card: {
49110
+ kind: "one_time",
49111
+ createdAt,
49112
+ ...requesterId ? { createdById: requesterId } : {},
49113
+ expiresAt: createdAt + DEFAULT_IMPLICIT_PROJECT_CARD_TTL_MS,
49114
+ maxUses: 1
49115
+ }
49116
+ }).catch((error48) => {
49117
+ 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)}`);
49126
+ });
49127
+ await syncRegisteredLocalAgentsIfChanged(options.reason);
49128
+ const agent = runtime.snapshot().agents[status.agentId];
49129
+ if (agent && !isInactiveLocalAgent(agent)) {
49130
+ return { kind: "resolved", agent };
49131
+ }
49132
+ return resolveBrokerDeliveryTarget(input);
49133
+ }
49134
+ async function resolveInvocationTarget(payload) {
49135
+ return resolveBrokerDeliveryTargetWithImplicitProjectCard({
47053
49136
  target: payload.target,
47054
49137
  targetAgentId: payload.targetAgentId,
49138
+ targetSessionId: payload.targetSessionId,
47055
49139
  targetLabel: payload.targetLabel,
47056
- routePolicy: payload.routePolicy
49140
+ routePolicy: payload.routePolicy,
49141
+ execution: payload.execution
49142
+ }, {
49143
+ requesterId: payload.requesterId,
49144
+ currentDirectory: projectPathRouteTarget(payload),
49145
+ reason: "implicit project invocation card"
47057
49146
  });
47058
49147
  }
47059
49148
  function remediationForDispatch(dispatch) {
@@ -47088,6 +49177,7 @@ function buildDeliveryReceipt(input) {
47088
49177
  requesterId: input.requesterId,
47089
49178
  requesterNodeId: input.requesterNodeId,
47090
49179
  targetAgentId: input.targetAgentId,
49180
+ targetSessionId: input.targetSessionId,
47091
49181
  targetLabel: input.targetLabel,
47092
49182
  ...input.bindingRef ? { bindingRef: input.bindingRef } : {},
47093
49183
  conversationId: input.conversationId,
@@ -47402,10 +49492,13 @@ async function acceptBrokerDelivery(payload) {
47402
49492
  const createdAt = typeof payload.createdAt === "number" && Number.isFinite(payload.createdAt) ? payload.createdAt : Date.now();
47403
49493
  const { requesterId, requesterNodeId } = callerContextForDelivery(payload);
47404
49494
  const askedLabel = askedLabelForRouteTarget(payload);
49495
+ const execution = executionWithRouteParams(payload);
47405
49496
  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;
49498
+ const replyToSessionId = payload.replyToSessionId?.trim() || metadataStringValue2(payload.invocationMetadata, "replyToSessionId") || metadataStringValue2(payload.messageMetadata, "replyToSessionId") || undefined;
47406
49499
  const labels = normalizeScoutLabels(payload.labels);
47407
49500
  const typedChannelTarget = payload.target?.kind === "channel" || payload.target?.kind === "broadcast";
47408
- const hasAgentTarget = Boolean(payload.target?.kind === "agent_id" || payload.target?.kind === "agent_label" || payload.target?.kind === "project_path") || !payload.target && Boolean(payload.targetAgentId?.trim() || payload.targetLabel?.trim());
49501
+ const hasAgentTarget = Boolean(payload.target?.kind === "agent_id" || payload.target?.kind === "agent_label" || payload.target?.kind === "session_id" || payload.target?.kind === "project_path") || !payload.target && Boolean(payload.targetSessionId?.trim() || payload.targetAgentId?.trim() || payload.targetLabel?.trim());
47409
49502
  const messageRef = messageRefCandidateForRouteTarget(payload);
47410
49503
  const replyTarget = messageRef ? resolveBrokerMessageRef(runtime.snapshot(), messageRef) : null;
47411
49504
  if (replyTarget) {
@@ -47442,7 +49535,8 @@ async function acceptBrokerDelivery(payload) {
47442
49535
  relayTargetIds: notifyTargets,
47443
49536
  returnAddress: buildBrokerReturnAddressForActor(snapshot2, requesterId, {
47444
49537
  conversationId: conversation2.id,
47445
- replyToMessageId: messageId2
49538
+ replyToMessageId: messageId2,
49539
+ sessionId: replyToSessionId
47446
49540
  })
47447
49541
  }
47448
49542
  };
@@ -47503,7 +49597,8 @@ async function acceptBrokerDelivery(payload) {
47503
49597
  relayMessageId: messageId2,
47504
49598
  returnAddress: buildBrokerReturnAddressForActor(snapshot2, requesterId, {
47505
49599
  conversationId: conversation2.id,
47506
- replyToMessageId: messageId2
49600
+ replyToMessageId: messageId2,
49601
+ sessionId: replyToSessionId
47507
49602
  })
47508
49603
  }
47509
49604
  };
@@ -47564,7 +49659,8 @@ async function acceptBrokerDelivery(payload) {
47564
49659
  relayMessageId: messageId2,
47565
49660
  returnAddress: buildBrokerReturnAddressForActor(snapshot2, requesterId, {
47566
49661
  conversationId: conversation2.id,
47567
- replyToMessageId: messageId2
49662
+ replyToMessageId: messageId2,
49663
+ sessionId: replyToSessionId
47568
49664
  })
47569
49665
  }
47570
49666
  };
@@ -47629,7 +49725,8 @@ async function acceptBrokerDelivery(payload) {
47629
49725
  relayMessageId: messageId2,
47630
49726
  returnAddress: buildBrokerReturnAddressForActor(snapshot2, requesterId, {
47631
49727
  conversationId: conversation2.id,
47632
- replyToMessageId: messageId2
49728
+ replyToMessageId: messageId2,
49729
+ sessionId: replyToSessionId
47633
49730
  })
47634
49731
  }
47635
49732
  };
@@ -47651,7 +49748,14 @@ async function acceptBrokerDelivery(payload) {
47651
49748
  message: message2
47652
49749
  };
47653
49750
  }
47654
- const resolved = resolveBrokerDeliveryTarget(payload);
49751
+ const resolved = await resolveBrokerDeliveryTargetWithImplicitProjectCard({
49752
+ ...payload,
49753
+ execution
49754
+ }, {
49755
+ requesterId,
49756
+ currentDirectory: projectPathRouteTarget(payload),
49757
+ reason: "implicit project delivery card"
49758
+ });
47655
49759
  if (resolved.kind !== "resolved") {
47656
49760
  const { record: record2 } = await recordScoutDispatchDurably(buildDispatchEnvelope(resolved, askedLabel, nodeId, runtime.snapshot(), { homeEndpointFor: homeEndpointForAgent }), {
47657
49761
  requesterId
@@ -47732,6 +49836,7 @@ async function acceptBrokerDelivery(payload) {
47732
49836
  metadata: {
47733
49837
  ...payload.messageMetadata ?? {},
47734
49838
  ...labels.length ? { labels } : {},
49839
+ ...targetSessionId ? { targetSessionId } : {},
47735
49840
  relayChannel: deliveryChannel || (conversation.kind === "direct" ? "dm" : "shared"),
47736
49841
  relayTarget: resolved.agent.id,
47737
49842
  relayTargetIds: [resolved.agent.id],
@@ -47739,7 +49844,8 @@ async function acceptBrokerDelivery(payload) {
47739
49844
  ...collaborationRecordId ? { collaborationRecordId, workId: collaborationRecordId } : {},
47740
49845
  returnAddress: buildBrokerReturnAddressForActor(snapshot, requesterId, {
47741
49846
  conversationId: conversation.id,
47742
- replyToMessageId: messageId
49847
+ replyToMessageId: messageId,
49848
+ sessionId: replyToSessionId
47743
49849
  })
47744
49850
  }
47745
49851
  };
@@ -47752,6 +49858,7 @@ async function acceptBrokerDelivery(payload) {
47752
49858
  requesterId,
47753
49859
  requesterNodeId,
47754
49860
  targetAgentId: resolved.agent.id,
49861
+ targetSessionId,
47755
49862
  targetLabel,
47756
49863
  conversationId: conversation.id,
47757
49864
  messageId
@@ -47764,14 +49871,20 @@ async function acceptBrokerDelivery(payload) {
47764
49871
  conversation,
47765
49872
  message,
47766
49873
  targetAgentId: resolved.agent.id,
49874
+ ...targetSessionId ? { targetSessionId } : {},
47767
49875
  ...workRecord?.kind === "work_item" ? { workItem: workRecord } : {}
47768
49876
  };
47769
49877
  }
47770
49878
  const invocationMetadata = {
47771
49879
  ...typeof payload.messageMetadata?.source === "string" && payload.invocationMetadata?.source === undefined ? { source: payload.messageMetadata.source } : {},
47772
49880
  ...payload.invocationMetadata ?? {},
49881
+ ...targetSessionId ? { targetSessionId } : {},
47773
49882
  ...payload.intent === "tell" && payload.invocationMetadata?.sourceIntent === undefined ? { sourceIntent: "direct_message" } : {}
47774
49883
  };
49884
+ const invocationExecution = {
49885
+ ...execution ?? (payload.intent === "tell" ? { session: "any" } : {}),
49886
+ ...targetSessionId ? { session: "existing", targetSessionId } : {}
49887
+ };
47775
49888
  const invocation = {
47776
49889
  id: createRuntimeId2("inv"),
47777
49890
  requesterId,
@@ -47782,7 +49895,7 @@ async function acceptBrokerDelivery(payload) {
47782
49895
  ...collaborationRecordId ? { collaborationRecordId } : {},
47783
49896
  conversationId: conversation.id,
47784
49897
  messageId,
47785
- execution: payload.execution ?? (payload.intent === "tell" ? { session: "any" } : undefined),
49898
+ ...Object.keys(invocationExecution).length > 0 ? { execution: invocationExecution } : {},
47786
49899
  ensureAwake: payload.ensureAwake ?? true,
47787
49900
  stream: false,
47788
49901
  createdAt,
@@ -47790,12 +49903,14 @@ async function acceptBrokerDelivery(payload) {
47790
49903
  metadata: {
47791
49904
  ...invocationMetadata,
47792
49905
  ...labels.length ? { labels } : {},
49906
+ ...targetSessionId ? { targetSessionId } : {},
47793
49907
  relayChannel: deliveryChannel || (conversation.kind === "direct" ? "dm" : "shared"),
47794
49908
  relayTarget: resolved.agent.id,
47795
49909
  ...collaborationRecordId ? { collaborationRecordId, workId: collaborationRecordId } : {},
47796
49910
  returnAddress: buildBrokerReturnAddressForActor(snapshot, requesterId, {
47797
49911
  conversationId: conversation.id,
47798
- replyToMessageId: messageId
49912
+ replyToMessageId: messageId,
49913
+ sessionId: replyToSessionId
47799
49914
  })
47800
49915
  }
47801
49916
  };
@@ -47814,6 +49929,7 @@ async function acceptBrokerDelivery(payload) {
47814
49929
  requesterId,
47815
49930
  requesterNodeId,
47816
49931
  targetAgentId: resolved.agent.id,
49932
+ targetSessionId,
47817
49933
  targetLabel,
47818
49934
  bindingRef,
47819
49935
  conversationId: conversation.id,
@@ -47823,6 +49939,7 @@ async function acceptBrokerDelivery(payload) {
47823
49939
  conversation,
47824
49940
  message,
47825
49941
  targetAgentId: resolved.agent.id,
49942
+ ...targetSessionId ? { targetSessionId } : {},
47826
49943
  bindingRef,
47827
49944
  flight,
47828
49945
  ...workRecord?.kind === "work_item" ? { workItem: workRecord } : {}
@@ -47906,6 +50023,9 @@ setTimeout(() => {
47906
50023
  reconcileStaleWorkingFlights().catch((error48) => {
47907
50024
  console.error("[openscout-runtime] stale flight reconciliation failed:", error48);
47908
50025
  });
50026
+ reconcileStaleLocalDeliveries().catch((error48) => {
50027
+ console.error("[openscout-runtime] stale delivery reconciliation failed:", error48);
50028
+ });
47909
50029
  }, 0).unref();
47910
50030
  discoverPeers().catch((error48) => {
47911
50031
  console.error("[openscout-runtime] initial mesh discovery failed:", error48);