orkestrator 2.26.3-beta → 2.28.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.3-beta",
3
+ "version": "2.28.0-beta",
4
4
  "description": "Run the Orkestrator backend service without the desktop application",
5
5
  "homepage": "https://orkestrator.dev",
6
6
  "repository": {
@@ -34,13 +34,13 @@
34
34
  "typecheck": "bunx tsc --noEmit -p tsconfig.json"
35
35
  },
36
36
  "dependencies": {
37
- "@anthropic-ai/claude-agent-sdk": "0.3.296",
37
+ "@anthropic-ai/claude-agent-sdk": "0.3.309",
38
38
  "playwright-core": "1.64.0",
39
39
  "sharp": "0.35.5"
40
40
  },
41
41
  "devDependencies": {
42
- "@orkestrator/protocol": "2.26.3-beta",
43
- "@orkestrator/toolchain": "2.26.3-beta"
42
+ "@orkestrator/protocol": "2.28.0-beta",
43
+ "@orkestrator/toolchain": "2.28.0-beta"
44
44
  },
45
45
  "engines": {
46
46
  "bun": ">=1.4.3"
@@ -3344,6 +3344,24 @@ function createStructuredUsageRefreshCoordinator(session, queryControl) {
3344
3344
  }
3345
3345
  };
3346
3346
  }
3347
+ var CLAUDE_CONTEXT_CATEGORY_IDS = {
3348
+ system_prompt: "system-prompt",
3349
+ system_tools: "tools",
3350
+ system_tools_deferred: "deferred-tools",
3351
+ mcp_tools: "mcp-tools",
3352
+ mcp_tools_deferred: "deferred-mcp-tools",
3353
+ mcp_instructions: "mcp-instructions",
3354
+ custom_agents: "agents",
3355
+ memory_files: "memory",
3356
+ skills: "skills",
3357
+ messages: "messages",
3358
+ autocompact_buffer: "reserved",
3359
+ compact_buffer: "reserved",
3360
+ free_space: "free-space"
3361
+ };
3362
+ function claudeContextCategoryId(value) {
3363
+ return typeof value === "string" && Object.hasOwn(CLAUDE_CONTEXT_CATEGORY_IDS, value) ? CLAUDE_CONTEXT_CATEGORY_IDS[value] : undefined;
3364
+ }
3347
3365
  function parseClaudeContextUsage(raw) {
3348
3366
  if (!raw || typeof raw !== "object")
3349
3367
  return;
@@ -3361,8 +3379,10 @@ function parseClaudeContextUsage(raw) {
3361
3379
  const tokens = parseTokenValue(item.tokens);
3362
3380
  if (!name || tokens === undefined)
3363
3381
  return [];
3382
+ const id = claudeContextCategoryId(item.id);
3364
3383
  return [
3365
3384
  {
3385
+ ...id ? { id } : {},
3366
3386
  name,
3367
3387
  tokens,
3368
3388
  color: typeof item.color === "string" ? item.color : undefined
@@ -3502,6 +3522,7 @@ async function buildClaudeUsageSnapshot(session, result, queryControl, fallbackM
3502
3522
  const turnId = typeof result.user_message_uuid === "string" && result.user_message_uuid.trim() || typeof result.uuid === "string" && result.uuid.trim() || `claude-turn-${session.latestTurnGeneration ?? updatedAt}`;
3503
3523
  const turn = {
3504
3524
  turnId,
3525
+ at: updatedAt,
3505
3526
  costUsd: totals.cost,
3506
3527
  inputTokens: totals.input,
3507
3528
  outputTokens: totals.output,
@@ -4243,6 +4264,7 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4243
4264
  ]
4244
4265
  });
4245
4266
  var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
4267
+ var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4246
4268
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
4247
4269
  claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
4248
4270
  codex: Object.freeze(["parameter:summary"]),
@@ -4358,6 +4380,7 @@ var PROJECTION_FIELD_SET = new Set([
4358
4380
  "runtimeHealthAuthoritative",
4359
4381
  "notices",
4360
4382
  "recoverableDispatch",
4383
+ "retiredDispatch",
4361
4384
  "backgroundTasks",
4362
4385
  "suggestedPrompt",
4363
4386
  "completionBlockedByBackgroundTasks",
@@ -4381,6 +4404,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4381
4404
  "runtimeHealthAuthoritative",
4382
4405
  "notices",
4383
4406
  "recoverableDispatch",
4407
+ "retiredDispatch",
4384
4408
  "backgroundTasks",
4385
4409
  "suggestedPrompt",
4386
4410
  "completionBlockedByBackgroundTasks",
@@ -4389,6 +4413,15 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4389
4413
  "slashCommandCatalogue"
4390
4414
  ]);
4391
4415
 
4416
+ // ../../bridges/claude-bridge/src/services/mcp-tool-prefix.ts
4417
+ function claudeMcpToolPrefix(name) {
4418
+ let normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
4419
+ if (name.startsWith("claude.ai ")) {
4420
+ normalized = normalized.replace(/_+/g, "_").replace(/^_|_$/g, "");
4421
+ }
4422
+ return `mcp__${normalized}__`;
4423
+ }
4424
+
4392
4425
  // ../../bridges/claude-bridge/src/services/read-only-policy.ts
4393
4426
  var CAPABILITY_TOOLS = new Map([
4394
4427
  ["file.write", ["Write", "NotebookEdit"]],
@@ -4414,8 +4447,7 @@ var WAITING_TOOLS = new Set([
4414
4447
  "CronCreate",
4415
4448
  "CronList",
4416
4449
  "CronDelete",
4417
- "SendMessage",
4418
- "TaskOutput"
4450
+ "SendMessage"
4419
4451
  ]);
4420
4452
  var READ_ONLY_COMMANDS = Object.freeze(new Set([
4421
4453
  "awk",
@@ -4604,8 +4636,8 @@ function claudeReadOnlyAllowedTools(options) {
4604
4636
  return [
4605
4637
  ...READ_ONLY_TOOLS,
4606
4638
  ...options.policy && !nativeAgentPolicyDeniesWebSearch(options.policy) ? ["WebSearch"] : [],
4607
- ...(options.agentMcpServerNames ?? []).map((name) => `mcp__${name}__*`),
4608
- ...(options.researchMcpServerNames ?? []).map((name) => `mcp__${name}__*`)
4639
+ ...(options.agentMcpServerNames ?? []).map((name) => `${claudeMcpToolPrefix(name)}*`),
4640
+ ...(options.researchMcpServerNames ?? []).map((name) => `${claudeMcpToolPrefix(name)}*`)
4609
4641
  ];
4610
4642
  }
4611
4643
  function claudeReadOnlySandbox(policy) {
@@ -4676,8 +4708,8 @@ function deny(reason) {
4676
4708
  function createCoordinatorReadOnlyHook(policy, researchMcpServerNames = coordinatorResearchMcpServersFromEnv(process.env).map((server) => server.name)) {
4677
4709
  const deniedTools = new Set(claudeDeniedTools(policy));
4678
4710
  const allowedMcpPrefixes = [
4679
- "mcp__orkestrator__",
4680
- ...researchMcpServerNames.map((name) => `mcp__${name}__`)
4711
+ claudeMcpToolPrefix("orkestrator"),
4712
+ ...researchMcpServerNames.map(claudeMcpToolPrefix)
4681
4713
  ];
4682
4714
  return async (input) => {
4683
4715
  if (input.hook_event_name !== "PreToolUse")
@@ -5160,7 +5192,15 @@ async function stopBackgroundTask(sessionId, taskId) {
5160
5192
  }
5161
5193
  return { ok: true };
5162
5194
  }
5195
+ var backgroundTaskRevisions = new WeakMap;
5196
+ function noteBackgroundTasksChanged(session) {
5197
+ backgroundTaskRevisions.set(session, (backgroundTaskRevisions.get(session) ?? 0) + 1);
5198
+ }
5199
+ function backgroundTaskRevision(session) {
5200
+ return backgroundTaskRevisions.get(session) ?? 0;
5201
+ }
5163
5202
  function emitBackgroundTaskSnapshot(session) {
5203
+ noteBackgroundTasksChanged(session);
5164
5204
  eventEmitter.emit({
5165
5205
  type: "session.updated",
5166
5206
  sessionId: session.id,
@@ -6771,6 +6811,34 @@ function getPromptDispatchState(sessionId, requestId) {
6771
6811
  }
6772
6812
  return "new";
6773
6813
  }
6814
+ function idleSteerUserMessageId(requestId) {
6815
+ return `idle-steer:${requestId}`;
6816
+ }
6817
+ function restoredDispatchClaimLost(session, requestId) {
6818
+ if (!session?.restoredDispatchedRequestIds?.has(requestId))
6819
+ return false;
6820
+ const localReplyId = idleSteerUserMessageId(requestId);
6821
+ return !session.localTranscript?.some((message) => message.id === localReplyId);
6822
+ }
6823
+ async function persistedDispatchClaimLost(sessionId, requestId) {
6824
+ if (sessions.has(sessionId))
6825
+ return false;
6826
+ const sdkSessionId = sdkSessionIdFromBridgeId(sessionId);
6827
+ if (!sdkSessionId)
6828
+ return false;
6829
+ let preferences;
6830
+ try {
6831
+ preferences = await readSessionPreferences(sdkSessionId);
6832
+ } catch {
6833
+ return false;
6834
+ }
6835
+ if (!preferences || sessionPreferencesUnavailable(preferences))
6836
+ return false;
6837
+ if (!preferences.dispatchedRequestIds?.includes(requestId))
6838
+ return false;
6839
+ const localReplyId = idleSteerUserMessageId(requestId);
6840
+ return !preferences.localTranscript?.some((entry) => entry.id === localReplyId);
6841
+ }
6774
6842
  async function claimPromptDispatch(sessionId, requestId, startDispatch, testHooks) {
6775
6843
  const session = sessions.get(sessionId);
6776
6844
  if (!session)
@@ -12179,7 +12247,7 @@ function displayCommandName(name) {
12179
12247
  function normalizeAvailability(value) {
12180
12248
  if (!isRecord(value))
12181
12249
  return;
12182
- if (value.state !== "available" && value.state !== "unavailable")
12250
+ if (typeof value.state !== "string")
12183
12251
  return;
12184
12252
  const reason = typeof value.reason === "string" && UNAVAILABLE_REASONS.has(value.reason) ? value.reason : undefined;
12185
12253
  const message = boundedText(value.message, COMMAND_CATALOGUE_LIMITS.maxAvailabilityMessageBytes);
@@ -12696,7 +12764,10 @@ function idleSteerMessageIds(requestId) {
12696
12764
  if (!requestId) {
12697
12765
  return { userId: generateMessageId(), assistantId: generateMessageId() };
12698
12766
  }
12699
- return { userId: `idle-steer:${requestId}`, assistantId: `idle-steer-reply:${requestId}` };
12767
+ return {
12768
+ userId: idleSteerUserMessageId(requestId),
12769
+ assistantId: `idle-steer-reply:${requestId}`
12770
+ };
12700
12771
  }
12701
12772
  async function answerIdleSteerPrompt(session, prompt, requestId) {
12702
12773
  const reply = idleSteerPromptReply(prompt, "Claude");
@@ -13609,9 +13680,6 @@ function reportedStaleServers(mcp) {
13609
13680
  return;
13610
13681
  return Array.from(mcp.stale).sort().slice(0, MAX_REPORTED_STALE_SERVERS);
13611
13682
  }
13612
- function claudeMcpToolPrefix(name) {
13613
- return `mcp__${name.replace(/[^a-zA-Z0-9_-]/g, "_")}__`;
13614
- }
13615
13683
  function staleMcpServerFor(mcp, toolName, toolInput) {
13616
13684
  if (!mcp.stale.size)
13617
13685
  return;
@@ -15405,6 +15473,7 @@ async function handleResultMessage(turn, message) {
15405
15473
 
15406
15474
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/system-tasks.ts
15407
15475
  function emitBackgroundTasks(turn) {
15476
+ noteBackgroundTasksChanged(turn.session);
15408
15477
  eventEmitter.emit({
15409
15478
  type: "session.updated",
15410
15479
  sessionId: turn.sessionId,
@@ -16871,7 +16940,9 @@ var MCP_MANAGEMENT_LIMITS = {
16871
16940
  operationStoreMaxBytes: 4 * 1024 * 1024,
16872
16941
  runtimesPerOperation: 64,
16873
16942
  runtimesTrackedPerOperation: 256,
16874
- queuedApplyTargets: 32
16943
+ queuedApplyTargets: 32,
16944
+ overlayRecordMaxBytes: 3 * 1024 * 1024,
16945
+ containerPathProbesMax: 32
16875
16946
  };
16876
16947
  var MCP_SCOPE_LABELS = Object.freeze({
16877
16948
  "backend-user": "Backend user",
@@ -17015,6 +17086,7 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
17015
17086
  maxResponseBytes: 512 * 1024,
17016
17087
  maxAsyncQuestionItemIds: 64,
17017
17088
  maxAsyncQuestionItemIdLength: 2048,
17089
+ maxContentRevisionLength: 256,
17018
17090
  readConcurrency: 4
17019
17091
  };
17020
17092
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
@@ -17029,6 +17101,7 @@ function parseSessionActivityObservation(value) {
17029
17101
  if (!isRecord3(value))
17030
17102
  return;
17031
17103
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
17104
+ const { contentRevision } = value;
17032
17105
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
17033
17106
  return;
17034
17107
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -17037,6 +17110,8 @@ function parseSessionActivityObservation(value) {
17037
17110
  return;
17038
17111
  if (closing !== undefined && typeof closing !== "boolean")
17039
17112
  return;
17113
+ if (contentRevision !== undefined && !isContentRevision(contentRevision))
17114
+ return;
17040
17115
  let itemIds;
17041
17116
  if (asyncQuestionItemIds !== undefined) {
17042
17117
  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 +17124,13 @@ function parseSessionActivityObservation(value) {
17049
17124
  ...readyForInput !== undefined ? { readyForInput } : {},
17050
17125
  ...completionPending !== undefined ? { completionPending } : {},
17051
17126
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
17052
- ...closing !== undefined ? { closing } : {}
17127
+ ...closing !== undefined ? { closing } : {},
17128
+ ...contentRevision !== undefined ? { contentRevision } : {}
17053
17129
  };
17054
17130
  }
17131
+ function isContentRevision(value) {
17132
+ return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
17133
+ }
17055
17134
  function parseSessionActivityBatchRequest(value) {
17056
17135
  if (!isRecord3(value))
17057
17136
  return { ok: false, error: "Expected a JSON object" };
@@ -17176,9 +17255,23 @@ async function readSessionActivityObservation(sessionId) {
17176
17255
  const completionPending = resident ? sessionCompletionPending(resident) : undefined;
17177
17256
  return {
17178
17257
  activity: activity === "idle" && completionPending ? "working" : activity,
17179
- ...resident?.status === "idle" ? { readyForInput: true, completionPending } : {}
17258
+ ...resident?.status === "idle" ? { readyForInput: true, completionPending } : {},
17259
+ ...activity === "missing" ? {} : contentRevisionField(resident)
17180
17260
  };
17181
17261
  }
17262
+ function contentRevisionField(resident) {
17263
+ try {
17264
+ return { contentRevision: sessionContentRevision(resident) };
17265
+ } catch {
17266
+ return {};
17267
+ }
17268
+ }
17269
+ function sessionContentRevision(resident) {
17270
+ if (!resident)
17271
+ return eventEmitter.generation;
17272
+ const transcript = readTranscriptVersion(resident);
17273
+ return `${eventEmitter.generation}:${transcript.epoch}.${transcript.revision}:${backgroundTaskRevision(resident)}`;
17274
+ }
17182
17275
  var sessionActivityBatch = new Hono2;
17183
17276
  sessionActivityBatch.post(SESSION_ACTIVITY_BATCH_PATH, async (c) => {
17184
17277
  const answer = await answerSessionActivityBatch(c.req.raw.body, readSessionActivityObservation, {
@@ -18449,13 +18542,20 @@ session.get("/:id/activity", async (c) => {
18449
18542
  session.get("/:id/runtime-health", (c) => {
18450
18543
  return c.json(sessionRuntimeHealthBody(peekSession(c.req.param("id"))));
18451
18544
  });
18452
- session.get("/:id/dispatch", (c) => {
18545
+ session.get("/:id/dispatch", async (c) => {
18546
+ const answer = (dispatch) => c.json({ dispatch });
18453
18547
  const requestId = c.req.query("requestId")?.trim();
18454
18548
  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" });
18549
+ return answer("unknown");
18550
+ const id = c.req.param("id");
18551
+ const state = getPromptDispatchState(id, requestId);
18552
+ if (state === "processing" || state === "already-processed")
18553
+ return answer("dispatched");
18554
+ if (state === "new") {
18555
+ return answer(restoredDispatchClaimLost(peekSession(id), requestId) ? "lost" : "unknown");
18556
+ }
18557
+ const lost = await persistedDispatchClaimLost(id, requestId).catch(() => false);
18558
+ return answer(lost ? "lost" : "unknown");
18459
18559
  });
18460
18560
  session.get("/:id/questions", (c) => {
18461
18561
  const id = c.req.param("id");
@@ -18869,6 +18969,9 @@ function serializeEventData(event) {
18869
18969
  }
18870
18970
  var MAX_PENDING_SSE_FRAMES = 1000;
18871
18971
  var MAX_PENDING_SSE_BYTES = 16 * 1024 * 1024;
18972
+ function observationFrameData(sessionId) {
18973
+ return JSON.stringify(sessionId === undefined ? {} : { sessionId });
18974
+ }
18872
18975
  var MAX_REPLAY_SSE_FRAMES = 512;
18873
18976
  var MAX_REPLAY_SSE_BYTES = 4 * 1024 * 1024;
18874
18977
  var REPLAY_IDLE_RETENTION_MS = 60000;
@@ -18885,7 +18988,8 @@ function createReplayBuffer(limits = {}) {
18885
18988
  id: `${generation}:${revision}`,
18886
18989
  event: event.type,
18887
18990
  data,
18888
- bytes: Buffer.byteLength(data) + Buffer.byteLength(event.type)
18991
+ bytes: Buffer.byteLength(data) + Buffer.byteLength(event.type),
18992
+ ...event.sessionId === undefined ? {} : { sessionId: event.sessionId }
18889
18993
  };
18890
18994
  frames.push(frame);
18891
18995
  bytes += frame.bytes;
@@ -19063,6 +19167,7 @@ function createEventsRouter(limits = {}) {
19063
19167
  if (!isOpen)
19064
19168
  throw new Error("SSE request aborted");
19065
19169
  }, () => closeConnection("write backlog exceeded its cap"), { maxPendingFrames, maxPendingBytes });
19170
+ const observe = c.req.query("observe") === "1";
19066
19171
  const requestedCursorValue = c.req.header("last-event-id") ?? c.req.query("since");
19067
19172
  const requestedCursor = parseReplayCursor(requestedCursorValue);
19068
19173
  const cursorWasSupplied = requestedCursorValue !== undefined;
@@ -19071,12 +19176,12 @@ function createEventsRouter(limits = {}) {
19071
19176
  let replaying = true;
19072
19177
  let keepaliveInterval;
19073
19178
  let unsubscribe = () => {};
19074
- const releaseReplayRetention = replayRetention.acquire();
19179
+ const releaseReplayRetention = observe ? () => {} : replayRetention.acquire();
19075
19180
  try {
19076
19181
  unsubscribe = eventEmitter.subscribe((event, revision) => {
19077
19182
  if (!isOpen)
19078
19183
  return;
19079
- const data = serializeEventData(event);
19184
+ const data = observe ? observationFrameData(event.sessionId) : serializeEventData(event);
19080
19185
  const frame = {
19081
19186
  revision,
19082
19187
  id: formatReplayCursor(revision),
@@ -19113,7 +19218,8 @@ function createEventsRouter(limits = {}) {
19113
19218
  status: "connected",
19114
19219
  timestamp: connectedAt,
19115
19220
  replayed: replay.frames.length,
19116
- resetRequired: replay.resetRequired
19221
+ resetRequired: replay.resetRequired,
19222
+ ...observe ? { observe: true } : {}
19117
19223
  })
19118
19224
  });
19119
19225
  if (replay.resetRequired) {
@@ -19123,8 +19229,9 @@ function createEventsRouter(limits = {}) {
19123
19229
  data: JSON.stringify({ through: formatReplayCursor(replayCeiling) })
19124
19230
  });
19125
19231
  } else {
19126
- for (const frame of replay.frames)
19127
- await writeFrame(frame);
19232
+ for (const frame of replay.frames) {
19233
+ await writeFrame(observe ? { ...frame, data: observationFrameData(frame.sessionId) } : frame);
19234
+ }
19128
19235
  }
19129
19236
  const drained = pendingLiveFrames.splice(0);
19130
19237
  pendingLiveBytes = 0;