orkestrator 2.26.2-beta → 2.27.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orkestrator",
3
- "version": "2.26.2-beta",
3
+ "version": "2.27.0-beta",
4
4
  "description": "Run the Orkestrator backend service without the desktop application",
5
5
  "homepage": "https://orkestrator.dev",
6
6
  "repository": {
@@ -39,8 +39,8 @@
39
39
  "sharp": "0.35.5"
40
40
  },
41
41
  "devDependencies": {
42
- "@orkestrator/protocol": "2.26.2-beta",
43
- "@orkestrator/toolchain": "2.26.2-beta"
42
+ "@orkestrator/protocol": "2.27.0-beta",
43
+ "@orkestrator/toolchain": "2.27.0-beta"
44
44
  },
45
45
  "engines": {
46
46
  "bun": ">=1.4.3"
@@ -4243,6 +4243,7 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4243
4243
  ]
4244
4244
  });
4245
4245
  var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
4246
+ var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4246
4247
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
4247
4248
  claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
4248
4249
  codex: Object.freeze(["parameter:summary"]),
@@ -4358,6 +4359,7 @@ var PROJECTION_FIELD_SET = new Set([
4358
4359
  "runtimeHealthAuthoritative",
4359
4360
  "notices",
4360
4361
  "recoverableDispatch",
4362
+ "retiredDispatch",
4361
4363
  "backgroundTasks",
4362
4364
  "suggestedPrompt",
4363
4365
  "completionBlockedByBackgroundTasks",
@@ -4381,6 +4383,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4381
4383
  "runtimeHealthAuthoritative",
4382
4384
  "notices",
4383
4385
  "recoverableDispatch",
4386
+ "retiredDispatch",
4384
4387
  "backgroundTasks",
4385
4388
  "suggestedPrompt",
4386
4389
  "completionBlockedByBackgroundTasks",
@@ -5160,7 +5163,15 @@ async function stopBackgroundTask(sessionId, taskId) {
5160
5163
  }
5161
5164
  return { ok: true };
5162
5165
  }
5166
+ var backgroundTaskRevisions = new WeakMap;
5167
+ function noteBackgroundTasksChanged(session) {
5168
+ backgroundTaskRevisions.set(session, (backgroundTaskRevisions.get(session) ?? 0) + 1);
5169
+ }
5170
+ function backgroundTaskRevision(session) {
5171
+ return backgroundTaskRevisions.get(session) ?? 0;
5172
+ }
5163
5173
  function emitBackgroundTaskSnapshot(session) {
5174
+ noteBackgroundTasksChanged(session);
5164
5175
  eventEmitter.emit({
5165
5176
  type: "session.updated",
5166
5177
  sessionId: session.id,
@@ -6771,6 +6782,34 @@ function getPromptDispatchState(sessionId, requestId) {
6771
6782
  }
6772
6783
  return "new";
6773
6784
  }
6785
+ function idleSteerUserMessageId(requestId) {
6786
+ return `idle-steer:${requestId}`;
6787
+ }
6788
+ function restoredDispatchClaimLost(session, requestId) {
6789
+ if (!session?.restoredDispatchedRequestIds?.has(requestId))
6790
+ return false;
6791
+ const localReplyId = idleSteerUserMessageId(requestId);
6792
+ return !session.localTranscript?.some((message) => message.id === localReplyId);
6793
+ }
6794
+ async function persistedDispatchClaimLost(sessionId, requestId) {
6795
+ if (sessions.has(sessionId))
6796
+ return false;
6797
+ const sdkSessionId = sdkSessionIdFromBridgeId(sessionId);
6798
+ if (!sdkSessionId)
6799
+ return false;
6800
+ let preferences;
6801
+ try {
6802
+ preferences = await readSessionPreferences(sdkSessionId);
6803
+ } catch {
6804
+ return false;
6805
+ }
6806
+ if (!preferences || sessionPreferencesUnavailable(preferences))
6807
+ return false;
6808
+ if (!preferences.dispatchedRequestIds?.includes(requestId))
6809
+ return false;
6810
+ const localReplyId = idleSteerUserMessageId(requestId);
6811
+ return !preferences.localTranscript?.some((entry) => entry.id === localReplyId);
6812
+ }
6774
6813
  async function claimPromptDispatch(sessionId, requestId, startDispatch, testHooks) {
6775
6814
  const session = sessions.get(sessionId);
6776
6815
  if (!session)
@@ -12179,7 +12218,7 @@ function displayCommandName(name) {
12179
12218
  function normalizeAvailability(value) {
12180
12219
  if (!isRecord(value))
12181
12220
  return;
12182
- if (value.state !== "available" && value.state !== "unavailable")
12221
+ if (typeof value.state !== "string")
12183
12222
  return;
12184
12223
  const reason = typeof value.reason === "string" && UNAVAILABLE_REASONS.has(value.reason) ? value.reason : undefined;
12185
12224
  const message = boundedText(value.message, COMMAND_CATALOGUE_LIMITS.maxAvailabilityMessageBytes);
@@ -12696,7 +12735,10 @@ function idleSteerMessageIds(requestId) {
12696
12735
  if (!requestId) {
12697
12736
  return { userId: generateMessageId(), assistantId: generateMessageId() };
12698
12737
  }
12699
- return { userId: `idle-steer:${requestId}`, assistantId: `idle-steer-reply:${requestId}` };
12738
+ return {
12739
+ userId: idleSteerUserMessageId(requestId),
12740
+ assistantId: `idle-steer-reply:${requestId}`
12741
+ };
12700
12742
  }
12701
12743
  async function answerIdleSteerPrompt(session, prompt, requestId) {
12702
12744
  const reply = idleSteerPromptReply(prompt, "Claude");
@@ -15405,6 +15447,7 @@ async function handleResultMessage(turn, message) {
15405
15447
 
15406
15448
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/system-tasks.ts
15407
15449
  function emitBackgroundTasks(turn) {
15450
+ noteBackgroundTasksChanged(turn.session);
15408
15451
  eventEmitter.emit({
15409
15452
  type: "session.updated",
15410
15453
  sessionId: turn.sessionId,
@@ -16871,7 +16914,9 @@ var MCP_MANAGEMENT_LIMITS = {
16871
16914
  operationStoreMaxBytes: 4 * 1024 * 1024,
16872
16915
  runtimesPerOperation: 64,
16873
16916
  runtimesTrackedPerOperation: 256,
16874
- queuedApplyTargets: 32
16917
+ queuedApplyTargets: 32,
16918
+ overlayRecordMaxBytes: 3 * 1024 * 1024,
16919
+ containerPathProbesMax: 32
16875
16920
  };
16876
16921
  var MCP_SCOPE_LABELS = Object.freeze({
16877
16922
  "backend-user": "Backend user",
@@ -17015,6 +17060,7 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
17015
17060
  maxResponseBytes: 512 * 1024,
17016
17061
  maxAsyncQuestionItemIds: 64,
17017
17062
  maxAsyncQuestionItemIdLength: 2048,
17063
+ maxContentRevisionLength: 256,
17018
17064
  readConcurrency: 4
17019
17065
  };
17020
17066
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
@@ -17029,6 +17075,7 @@ function parseSessionActivityObservation(value) {
17029
17075
  if (!isRecord3(value))
17030
17076
  return;
17031
17077
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
17078
+ const { contentRevision } = value;
17032
17079
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
17033
17080
  return;
17034
17081
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -17037,6 +17084,8 @@ function parseSessionActivityObservation(value) {
17037
17084
  return;
17038
17085
  if (closing !== undefined && typeof closing !== "boolean")
17039
17086
  return;
17087
+ if (contentRevision !== undefined && !isContentRevision(contentRevision))
17088
+ return;
17040
17089
  let itemIds;
17041
17090
  if (asyncQuestionItemIds !== undefined) {
17042
17091
  if (!Array.isArray(asyncQuestionItemIds) || asyncQuestionItemIds.length > SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIds || !asyncQuestionItemIds.every((itemId) => typeof itemId === "string" && itemId.length > 0 && itemId.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIdLength)) {
@@ -17049,9 +17098,13 @@ function parseSessionActivityObservation(value) {
17049
17098
  ...readyForInput !== undefined ? { readyForInput } : {},
17050
17099
  ...completionPending !== undefined ? { completionPending } : {},
17051
17100
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
17052
- ...closing !== undefined ? { closing } : {}
17101
+ ...closing !== undefined ? { closing } : {},
17102
+ ...contentRevision !== undefined ? { contentRevision } : {}
17053
17103
  };
17054
17104
  }
17105
+ function isContentRevision(value) {
17106
+ return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
17107
+ }
17055
17108
  function parseSessionActivityBatchRequest(value) {
17056
17109
  if (!isRecord3(value))
17057
17110
  return { ok: false, error: "Expected a JSON object" };
@@ -17176,9 +17229,23 @@ async function readSessionActivityObservation(sessionId) {
17176
17229
  const completionPending = resident ? sessionCompletionPending(resident) : undefined;
17177
17230
  return {
17178
17231
  activity: activity === "idle" && completionPending ? "working" : activity,
17179
- ...resident?.status === "idle" ? { readyForInput: true, completionPending } : {}
17232
+ ...resident?.status === "idle" ? { readyForInput: true, completionPending } : {},
17233
+ ...activity === "missing" ? {} : contentRevisionField(resident)
17180
17234
  };
17181
17235
  }
17236
+ function contentRevisionField(resident) {
17237
+ try {
17238
+ return { contentRevision: sessionContentRevision(resident) };
17239
+ } catch {
17240
+ return {};
17241
+ }
17242
+ }
17243
+ function sessionContentRevision(resident) {
17244
+ if (!resident)
17245
+ return eventEmitter.generation;
17246
+ const transcript = readTranscriptVersion(resident);
17247
+ return `${eventEmitter.generation}:${transcript.epoch}.${transcript.revision}:${backgroundTaskRevision(resident)}`;
17248
+ }
17182
17249
  var sessionActivityBatch = new Hono2;
17183
17250
  sessionActivityBatch.post(SESSION_ACTIVITY_BATCH_PATH, async (c) => {
17184
17251
  const answer = await answerSessionActivityBatch(c.req.raw.body, readSessionActivityObservation, {
@@ -18449,13 +18516,20 @@ session.get("/:id/activity", async (c) => {
18449
18516
  session.get("/:id/runtime-health", (c) => {
18450
18517
  return c.json(sessionRuntimeHealthBody(peekSession(c.req.param("id"))));
18451
18518
  });
18452
- session.get("/:id/dispatch", (c) => {
18519
+ session.get("/:id/dispatch", async (c) => {
18520
+ const answer = (dispatch) => c.json({ dispatch });
18453
18521
  const requestId = c.req.query("requestId")?.trim();
18454
18522
  if (!requestId)
18455
- return c.json({ dispatch: "unknown" });
18456
- const state = getPromptDispatchState(c.req.param("id"), requestId);
18457
- const dispatched = state === "processing" || state === "already-processed";
18458
- return c.json({ dispatch: dispatched ? "dispatched" : "unknown" });
18523
+ return answer("unknown");
18524
+ const id = c.req.param("id");
18525
+ const state = getPromptDispatchState(id, requestId);
18526
+ if (state === "processing" || state === "already-processed")
18527
+ return answer("dispatched");
18528
+ if (state === "new") {
18529
+ return answer(restoredDispatchClaimLost(peekSession(id), requestId) ? "lost" : "unknown");
18530
+ }
18531
+ const lost = await persistedDispatchClaimLost(id, requestId).catch(() => false);
18532
+ return answer(lost ? "lost" : "unknown");
18459
18533
  });
18460
18534
  session.get("/:id/questions", (c) => {
18461
18535
  const id = c.req.param("id");
@@ -18869,6 +18943,9 @@ function serializeEventData(event) {
18869
18943
  }
18870
18944
  var MAX_PENDING_SSE_FRAMES = 1000;
18871
18945
  var MAX_PENDING_SSE_BYTES = 16 * 1024 * 1024;
18946
+ function observationFrameData(sessionId) {
18947
+ return JSON.stringify(sessionId === undefined ? {} : { sessionId });
18948
+ }
18872
18949
  var MAX_REPLAY_SSE_FRAMES = 512;
18873
18950
  var MAX_REPLAY_SSE_BYTES = 4 * 1024 * 1024;
18874
18951
  var REPLAY_IDLE_RETENTION_MS = 60000;
@@ -18885,7 +18962,8 @@ function createReplayBuffer(limits = {}) {
18885
18962
  id: `${generation}:${revision}`,
18886
18963
  event: event.type,
18887
18964
  data,
18888
- bytes: Buffer.byteLength(data) + Buffer.byteLength(event.type)
18965
+ bytes: Buffer.byteLength(data) + Buffer.byteLength(event.type),
18966
+ ...event.sessionId === undefined ? {} : { sessionId: event.sessionId }
18889
18967
  };
18890
18968
  frames.push(frame);
18891
18969
  bytes += frame.bytes;
@@ -19063,6 +19141,7 @@ function createEventsRouter(limits = {}) {
19063
19141
  if (!isOpen)
19064
19142
  throw new Error("SSE request aborted");
19065
19143
  }, () => closeConnection("write backlog exceeded its cap"), { maxPendingFrames, maxPendingBytes });
19144
+ const observe = c.req.query("observe") === "1";
19066
19145
  const requestedCursorValue = c.req.header("last-event-id") ?? c.req.query("since");
19067
19146
  const requestedCursor = parseReplayCursor(requestedCursorValue);
19068
19147
  const cursorWasSupplied = requestedCursorValue !== undefined;
@@ -19071,12 +19150,12 @@ function createEventsRouter(limits = {}) {
19071
19150
  let replaying = true;
19072
19151
  let keepaliveInterval;
19073
19152
  let unsubscribe = () => {};
19074
- const releaseReplayRetention = replayRetention.acquire();
19153
+ const releaseReplayRetention = observe ? () => {} : replayRetention.acquire();
19075
19154
  try {
19076
19155
  unsubscribe = eventEmitter.subscribe((event, revision) => {
19077
19156
  if (!isOpen)
19078
19157
  return;
19079
- const data = serializeEventData(event);
19158
+ const data = observe ? observationFrameData(event.sessionId) : serializeEventData(event);
19080
19159
  const frame = {
19081
19160
  revision,
19082
19161
  id: formatReplayCursor(revision),
@@ -19113,7 +19192,8 @@ function createEventsRouter(limits = {}) {
19113
19192
  status: "connected",
19114
19193
  timestamp: connectedAt,
19115
19194
  replayed: replay.frames.length,
19116
- resetRequired: replay.resetRequired
19195
+ resetRequired: replay.resetRequired,
19196
+ ...observe ? { observe: true } : {}
19117
19197
  })
19118
19198
  });
19119
19199
  if (replay.resetRequired) {
@@ -19123,8 +19203,9 @@ function createEventsRouter(limits = {}) {
19123
19203
  data: JSON.stringify({ through: formatReplayCursor(replayCeiling) })
19124
19204
  });
19125
19205
  } else {
19126
- for (const frame of replay.frames)
19127
- await writeFrame(frame);
19206
+ for (const frame of replay.frames) {
19207
+ await writeFrame(observe ? { ...frame, data: observationFrameData(frame.sessionId) } : frame);
19208
+ }
19128
19209
  }
19129
19210
  const drained = pendingLiveFrames.splice(0);
19130
19211
  pendingLiveBytes = 0;
@@ -4060,6 +4060,7 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4060
4060
  }
4061
4061
  ]
4062
4062
  });
4063
+ var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4063
4064
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
4064
4065
  claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
4065
4066
  codex: Object.freeze(["parameter:summary"]),
@@ -4191,6 +4192,7 @@ var PROJECTION_FIELD_SET = new Set([
4191
4192
  "runtimeHealthAuthoritative",
4192
4193
  "notices",
4193
4194
  "recoverableDispatch",
4195
+ "retiredDispatch",
4194
4196
  "backgroundTasks",
4195
4197
  "suggestedPrompt",
4196
4198
  "completionBlockedByBackgroundTasks",
@@ -4214,6 +4216,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4214
4216
  "runtimeHealthAuthoritative",
4215
4217
  "notices",
4216
4218
  "recoverableDispatch",
4219
+ "retiredDispatch",
4217
4220
  "backgroundTasks",
4218
4221
  "suggestedPrompt",
4219
4222
  "completionBlockedByBackgroundTasks",
@@ -5089,6 +5092,7 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
5089
5092
  maxResponseBytes: 512 * 1024,
5090
5093
  maxAsyncQuestionItemIds: 64,
5091
5094
  maxAsyncQuestionItemIdLength: 2048,
5095
+ maxContentRevisionLength: 256,
5092
5096
  readConcurrency: 4
5093
5097
  };
5094
5098
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
@@ -5103,6 +5107,7 @@ function parseSessionActivityObservation(value) {
5103
5107
  if (!isRecord(value))
5104
5108
  return;
5105
5109
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
5110
+ const { contentRevision } = value;
5106
5111
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
5107
5112
  return;
5108
5113
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -5111,6 +5116,8 @@ function parseSessionActivityObservation(value) {
5111
5116
  return;
5112
5117
  if (closing !== undefined && typeof closing !== "boolean")
5113
5118
  return;
5119
+ if (contentRevision !== undefined && !isContentRevision(contentRevision))
5120
+ return;
5114
5121
  let itemIds;
5115
5122
  if (asyncQuestionItemIds !== undefined) {
5116
5123
  if (!Array.isArray(asyncQuestionItemIds) || asyncQuestionItemIds.length > SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIds || !asyncQuestionItemIds.every((itemId) => typeof itemId === "string" && itemId.length > 0 && itemId.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIdLength)) {
@@ -5123,9 +5130,13 @@ function parseSessionActivityObservation(value) {
5123
5130
  ...readyForInput !== undefined ? { readyForInput } : {},
5124
5131
  ...completionPending !== undefined ? { completionPending } : {},
5125
5132
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
5126
- ...closing !== undefined ? { closing } : {}
5133
+ ...closing !== undefined ? { closing } : {},
5134
+ ...contentRevision !== undefined ? { contentRevision } : {}
5127
5135
  };
5128
5136
  }
5137
+ function isContentRevision(value) {
5138
+ return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
5139
+ }
5129
5140
  function parseSessionActivityBatchRequest(value) {
5130
5141
  if (!isRecord(value))
5131
5142
  return { ok: false, error: "Expected a JSON object" };
@@ -14024,7 +14035,7 @@ async function publishAssistantRows(context, state, turn, options) {
14024
14035
  }
14025
14036
 
14026
14037
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
14027
- import { createHash as createHash9 } from "node:crypto";
14038
+ import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
14028
14039
 
14029
14040
  // ../protocol/src/agent-mail.ts
14030
14041
  var AGENT_MAIL_MAX_BODY_BYTES = 32 * 1024;
@@ -20092,6 +20103,7 @@ class ThreadRegistry {
20092
20103
 
20093
20104
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
20094
20105
  var SESSION_CLOSING_ERROR = "Session is closing";
20106
+ var ACTIVITY_PROCESS = randomUUID6();
20095
20107
 
20096
20108
  class CodexRuntimeSessions {
20097
20109
  ctx;
@@ -20949,7 +20961,8 @@ class CodexRuntimeSessions {
20949
20961
  return { activity: "missing" };
20950
20962
  return {
20951
20963
  activity,
20952
- ...session.asyncQuestionItemIds.length > 0 ? { asyncQuestionItemIds: [...session.asyncQuestionItemIds] } : {}
20964
+ ...session.asyncQuestionItemIds.length > 0 ? { asyncQuestionItemIds: [...session.asyncQuestionItemIds] } : {},
20965
+ contentRevision: `${ACTIVITY_PROCESS}:${session.contentEpoch}.${session.messageRevision}:${session.asyncQuestionItemIds.length}`
20953
20966
  };
20954
20967
  }
20955
20968
  hasParkedInput(session) {
@@ -26082,6 +26095,21 @@ var codexPathOverride = process.env.CODEX_PATH || "codex";
26082
26095
  var BRIDGE_VERSION = "1.0.0";
26083
26096
  var execFile4 = promisify2(execFileCallback2);
26084
26097
  var subscribers = new Set;
26098
+ var observers = new Set;
26099
+ function observationFrameData(sessionId) {
26100
+ return JSON.stringify(sessionId === undefined ? {} : { sessionId });
26101
+ }
26102
+ function notifyObservers(event, revision) {
26103
+ for (const observer of observers) {
26104
+ try {
26105
+ Promise.resolve(observer(event, revision, "{}")).catch((error) => {
26106
+ console.error("[codex-bridge] Failed to notify SSE observer:", error);
26107
+ });
26108
+ } catch (error) {
26109
+ console.error("[codex-bridge] Failed to notify SSE observer:", error);
26110
+ }
26111
+ }
26112
+ }
26085
26113
  var MAX_REPLAY_SSE_BYTES = 4 * 1024 * 1024;
26086
26114
  var REPLAY_IDLE_RETENTION_MS = 60000;
26087
26115
  var SUPERSEDED_FRAME_BYTES = 64;
@@ -26349,7 +26377,7 @@ function retainedMessageId(type, payload) {
26349
26377
  }
26350
26378
  function emit(event) {
26351
26379
  if (!replayRetentionEnabled) {
26352
- eventRing.advance();
26380
+ notifyObservers(event, eventRing.advance());
26353
26381
  return;
26354
26382
  }
26355
26383
  const sessionId = event.sessionId;
@@ -26376,6 +26404,7 @@ function emit(event) {
26376
26404
  serialize: () => eager ?? serializeSseEventData({ sessionId, data: payload }),
26377
26405
  bytes
26378
26406
  });
26407
+ notifyObservers(event, revision);
26379
26408
  for (const subscriber of subscribers) {
26380
26409
  try {
26381
26410
  Promise.resolve(subscriber(event, revision, eager ?? "{}")).catch((error) => {
@@ -26704,7 +26733,7 @@ app.get("/global/health", (c) => {
26704
26733
  notificationDepth: health.notificationQueueDepth,
26705
26734
  notificationHighWaterMark: health.notificationQueueHighWaterMark
26706
26735
  },
26707
- events: { ...eventRing.getStats(), subscribers: subscribers.size }
26736
+ events: { ...eventRing.getStats(), subscribers: subscribers.size, observers: observers.size }
26708
26737
  }, terminal ? 503 : 200);
26709
26738
  });
26710
26739
  app.get("/global/auth-check", (c) => {
@@ -27219,6 +27248,7 @@ app.delete("/session/:id", async (c) => {
27219
27248
  app.get("/event/subscribe", (c) => {
27220
27249
  const cursor = parseEventCursor(c.req.query("since")) ?? parseEventCursor(c.req.header("last-event-id"));
27221
27250
  const sessionFilter = c.req.query("sessionId")?.trim() || null;
27251
+ const observe = c.req.query("observe") === "1";
27222
27252
  return streamSSE(c, async (stream) => {
27223
27253
  let open = true;
27224
27254
  let resolveConnectionClosed;
@@ -27244,10 +27274,10 @@ app.get("/event/subscribe", (c) => {
27244
27274
  return {
27245
27275
  event: event.type,
27246
27276
  id: String(revision),
27247
- data: serializeData()
27277
+ data: observe ? observationFrameData(event.sessionId) : serializeData()
27248
27278
  };
27249
27279
  };
27250
- const releaseReplayRetention = acquireReplayRetention();
27280
+ const releaseReplayRetention = observe ? () => {} : acquireReplayRetention();
27251
27281
  const anchorRevision = cursor ?? eventRing.latestRevision;
27252
27282
  let buffered = [];
27253
27283
  let highestSent = anchorRevision;
@@ -27269,7 +27299,7 @@ app.get("/event/subscribe", (c) => {
27269
27299
  highestSent = revision;
27270
27300
  await writeWhileOpen(frameFor(event, revision, () => serializedData));
27271
27301
  };
27272
- subscribers.add(listener);
27302
+ (observe ? observers : subscribers).add(listener);
27273
27303
  let keepalive;
27274
27304
  try {
27275
27305
  if (sseRouteTestHooks?.afterSubscriberRegistered) {
@@ -27283,7 +27313,8 @@ app.get("/event/subscribe", (c) => {
27283
27313
  status: "connected",
27284
27314
  timestamp: new Date().toISOString(),
27285
27315
  revision: replay?.latestRevision ?? anchorRevision,
27286
- replayed: replay?.complete === true ? replay.events.length : 0
27316
+ replayed: replay?.complete === true ? replay.events.length : 0,
27317
+ ...observe ? { observe: true } : {}
27287
27318
  })
27288
27319
  });
27289
27320
  if (replay && !replay.complete) {
@@ -27329,6 +27360,8 @@ app.get("/event/subscribe", (c) => {
27329
27360
  }
27330
27361
  buffered = null;
27331
27362
  keepalive = startSseKeepalive(writeWhileOpen, 5000, () => {
27363
+ if (observe)
27364
+ return false;
27332
27365
  if (!sessionFilter)
27333
27366
  return true;
27334
27367
  const activity = appServerRuntime.getActivity(sessionFilter);
@@ -27348,6 +27381,7 @@ app.get("/event/subscribe", (c) => {
27348
27381
  if (keepalive)
27349
27382
  clearInterval(keepalive);
27350
27383
  subscribers.delete(listener);
27384
+ observers.delete(listener);
27351
27385
  releaseReplayRetention();
27352
27386
  }
27353
27387
  });