orkestrator 2.35.0-beta → 2.35.1-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/dist/client.js CHANGED
@@ -8334,7 +8334,7 @@ import path5 from "path";
8334
8334
  // ../../apps/backend/package.json
8335
8335
  var package_default = {
8336
8336
  name: "@orkestrator/backend",
8337
- version: "2.35.0-beta",
8337
+ version: "2.35.1-beta",
8338
8338
  private: true,
8339
8339
  type: "module",
8340
8340
  exports: {
package/dist/main.js CHANGED
@@ -7238,7 +7238,7 @@ var package_default;
7238
7238
  var init_package = __esm(() => {
7239
7239
  package_default = {
7240
7240
  name: "@orkestrator/backend",
7241
- version: "2.35.0-beta",
7241
+ version: "2.35.1-beta",
7242
7242
  private: true,
7243
7243
  type: "module",
7244
7244
  exports: {
@@ -11594,6 +11594,7 @@ class StorageDatabase {
11594
11594
  }
11595
11595
  const update = mutate(current);
11596
11596
  let document2 = header.document;
11597
+ let nextRow = null;
11597
11598
  if (update.delete) {
11598
11599
  if (!existing)
11599
11600
  return update.result;
@@ -11613,6 +11614,7 @@ class StorageDatabase {
11613
11614
  VALUES (?, ?, ?, ?, ?, ?)
11614
11615
  ON CONFLICT (store, collection, key) DO UPDATE SET
11615
11616
  owner = excluded.owner, value = excluded.value`).run(store, collection, key, position, owner ?? null, value);
11617
+ nextRow = { collection, key, position, value };
11616
11618
  if (collection && document2 !== null) {
11617
11619
  const envelope = JSON.parse(document2);
11618
11620
  if (envelope[collection] !== STORAGE_COLLECTION_MARKER) {
@@ -11625,7 +11627,17 @@ class StorageDatabase {
11625
11627
  }
11626
11628
  const revision = header.revision + 1;
11627
11629
  this.db.query("UPDATE stores SET revision = ?, document = ?, updated_at = ? WHERE name = ?").run(revision, document2, Date.now(), store);
11628
- this.setCache(store, null);
11630
+ const cached = this.pendingCache?.has(store) ? this.pendingCache.get(store) : this.cache.get(store);
11631
+ if (cached?.revision === header.revision && cached.mode === "rows") {
11632
+ const rows = new Map(cached.rows);
11633
+ if (nextRow)
11634
+ rows.set(rowId(collection, key), nextRow);
11635
+ else
11636
+ rows.delete(rowId(collection, key));
11637
+ this.setCache(store, { ...cached, revision, document: document2, rows });
11638
+ } else {
11639
+ this.setCache(store, null);
11640
+ }
11629
11641
  return update.result;
11630
11642
  });
11631
11643
  }
@@ -13555,6 +13567,12 @@ class StorageContext {
13555
13567
  throw error;
13556
13568
  }
13557
13569
  }
13570
+ storeHeldAsDocument(filePath) {
13571
+ const store = this.storeForFile(filePath);
13572
+ if (store === null)
13573
+ throw new Error("storeHeldAsDocument requires a database store");
13574
+ return this.database.readStoreHeader(store)?.mode === "document";
13575
+ }
13558
13576
  loadStoreSerialized(filePath) {
13559
13577
  const store = this.storeForFile(filePath);
13560
13578
  if (store === null)
@@ -24321,7 +24339,8 @@ class DraftsRepository {
24321
24339
  async getComposeDraft(draftKey) {
24322
24340
  if (!isNonBlankString(draftKey))
24323
24341
  throw new Error("Compose draft key must not be blank");
24324
- return (await this.repos.prompts.loadComposeDrafts())[draftKey] ?? null;
24342
+ const stored = this.context.loadStoreRow(this.files.composeDraftsFile(), draftKey);
24343
+ return isPersistedComposeDraft(stored, draftKey) ? stored : null;
24325
24344
  }
24326
24345
  async listComposeDrafts(ownerType, ownerId) {
24327
24346
  if (ownerType !== "environment" && ownerType !== "project") {
@@ -24675,8 +24694,8 @@ class DraftsRepository {
24675
24694
  });
24676
24695
  }
24677
24696
  async getSession(sessionId) {
24678
- const sessions = await this.context.loadJson(this.files.sessionsFile(), () => []);
24679
- return sessions.find((session) => session.id === sessionId) ?? null;
24697
+ const stored = this.context.loadStoreRow(this.files.sessionsFile(), sessionId);
24698
+ return isRecord6(stored) && stored.id === sessionId ? stored : null;
24680
24699
  }
24681
24700
  async getSessionsByEnvironment(environmentId) {
24682
24701
  const sessions = this.context.loadStoreRowsByOwner(this.files.sessionsFile(), environmentId);
@@ -43989,17 +44008,25 @@ class NativeAgentSessionsRepository {
43989
44008
  });
43990
44009
  }
43991
44010
  assertReadableNativeAgentSession(loaded, key) {
43992
- if (key in loaded.opaque) {
43993
- throw new Error("Stored native agent session metadata is invalid or uses an unsupported version");
43994
- }
44011
+ if (key in loaded.opaque)
44012
+ throw new Error(UNREADABLE_NATIVE_SESSION);
43995
44013
  }
43996
44014
  async getNativeAgentSession(key) {
43997
44015
  if (!isNonBlankString(key)) {
43998
44016
  throw new Error("Native agent session key must not be blank");
43999
44017
  }
44000
- const loaded = await this.loadNativeAgentSessions();
44001
- this.assertReadableNativeAgentSession(loaded, key);
44002
- return loaded.sessions[key] ?? null;
44018
+ const file = this.files.nativeAgentSessionsFile();
44019
+ const stored = this.context.loadStoreRow(file, key);
44020
+ if (stored === undefined) {
44021
+ if (!this.context.storeHeldAsDocument(file))
44022
+ return null;
44023
+ const loaded = await this.loadNativeAgentSessions();
44024
+ this.assertReadableNativeAgentSession(loaded, key);
44025
+ return loaded.sessions[key] ?? null;
44026
+ }
44027
+ if (!isPersistedNativeAgentSession(stored, key))
44028
+ throw new Error(UNREADABLE_NATIVE_SESSION);
44029
+ return stored;
44003
44030
  }
44004
44031
  async getNativeAgentSessionAfterMutations(key) {
44005
44032
  return this.enqueueNativeAgentSessionMutation(key, () => this.getNativeAgentSession(key));
@@ -44543,6 +44570,7 @@ class NativeAgentSessionsRepository {
44543
44570
  return environment;
44544
44571
  }
44545
44572
  }
44573
+ var UNREADABLE_NATIVE_SESSION = "Stored native agent session metadata is invalid or uses an unsupported version";
44546
44574
  var init_storage_native = __esm(() => {
44547
44575
  init_native_agent_session_controls();
44548
44576
  init_models();
@@ -46206,6 +46234,14 @@ class PromptQueueStore {
46206
46234
  }
46207
46235
  return added.length === 0 ? existing : [...existing, ...added].slice(-64);
46208
46236
  }
46237
+ loadPromptQueue(queueKey) {
46238
+ const stored = this.context.loadStoreRow(this.files.promptQueuesFile(), queueKey);
46239
+ if (stored === undefined)
46240
+ return null;
46241
+ const restored = restoreLegacyPromptQueueClaims({ [queueKey]: stored });
46242
+ const queue = restored[queueKey];
46243
+ return isPersistedPromptQueue(queue, queueKey) ? queue : null;
46244
+ }
46209
46245
  async loadPromptQueues() {
46210
46246
  const stored = await this.context.loadJson(this.files.promptQueuesFile(), () => ({}));
46211
46247
  return Object.fromEntries(Object.entries(restoreLegacyPromptQueueClaims(stored)).filter(([storedKey, queue]) => isPersistedPromptQueue(queue, storedKey)));
@@ -47033,6 +47069,18 @@ function parseWorktreeMoveRecord(value) {
47033
47069
  startedAt: value.startedAt
47034
47070
  };
47035
47071
  }
47072
+ function normalizeStoredEnvironment(environment) {
47073
+ environment.setupPhase ??= environment.setupScriptsComplete ? "ready" : "pending";
47074
+ environment.setupOverride ??= false;
47075
+ const migrated = migrateEnvironmentAgentSettings(environment);
47076
+ if (isEmptyAgentSettings(migrated))
47077
+ delete environment.agentSettings;
47078
+ else
47079
+ environment.agentSettings = migrated;
47080
+ for (const key of LEGACY_ENVIRONMENT_AGENT_KEYS) {
47081
+ delete environment[key];
47082
+ }
47083
+ }
47036
47084
 
47037
47085
  class ProjectsRepository {
47038
47086
  context;
@@ -47173,18 +47221,8 @@ class ProjectsRepository {
47173
47221
  }
47174
47222
  async loadEnvironments() {
47175
47223
  const environments = await this.context.loadJsonCached(this.files.environmentsFile(), () => []);
47176
- for (const environment of environments) {
47177
- environment.setupPhase ??= environment.setupScriptsComplete ? "ready" : "pending";
47178
- environment.setupOverride ??= false;
47179
- const migrated = migrateEnvironmentAgentSettings(environment);
47180
- if (isEmptyAgentSettings(migrated))
47181
- delete environment.agentSettings;
47182
- else
47183
- environment.agentSettings = migrated;
47184
- for (const key of LEGACY_ENVIRONMENT_AGENT_KEYS) {
47185
- delete environment[key];
47186
- }
47187
- }
47224
+ for (const environment of environments)
47225
+ normalizeStoredEnvironment(environment);
47188
47226
  return environments.sort((a, b) => a.order - b.order);
47189
47227
  }
47190
47228
  async saveEnvironments(environments, options2 = {}) {
@@ -47208,7 +47246,12 @@ class ProjectsRepository {
47208
47246
  return (await this.loadEnvironments()).filter((environment) => environment.projectId === projectId);
47209
47247
  }
47210
47248
  async getEnvironment(environmentId) {
47211
- return (await this.loadEnvironments()).find((environment) => environment.id === environmentId) ?? null;
47249
+ const stored = this.context.loadStoreRow(this.files.environmentsFile(), environmentId);
47250
+ if (!isRecord6(stored) || stored.id !== environmentId)
47251
+ return null;
47252
+ const environment = stored;
47253
+ normalizeStoredEnvironment(environment);
47254
+ return environment;
47212
47255
  }
47213
47256
  projectRemovalFencesFile() {
47214
47257
  return this.context.file("project-removal-fences.json");
@@ -48136,7 +48179,7 @@ class PromptsRepository {
48136
48179
  if (!isNonBlankString(queueKey)) {
48137
48180
  throw new Error("Prompt queue key must not be blank");
48138
48181
  }
48139
- return (await this.repos.promptQueues.loadPromptQueues())[queueKey] ?? null;
48182
+ return this.repos.promptQueues.loadPromptQueue(queueKey);
48140
48183
  }
48141
48184
  async listPromptQueues(environmentId) {
48142
48185
  if (!isNonBlankString(environmentId)) {
@@ -52535,8 +52578,8 @@ class SessionsRepository {
52535
52578
  return session;
52536
52579
  }
52537
52580
  async getPaneLayout(environmentId) {
52538
- const layouts = await this.context.loadJsonCached(this.files.paneLayoutsFile(), () => ({}));
52539
- return layouts[environmentId] ?? null;
52581
+ const stored = this.context.loadStoreRow(this.files.paneLayoutsFile(), environmentId);
52582
+ return stored === undefined ? null : stored;
52540
52583
  }
52541
52584
  async removeMultiReviewTabs(environmentId, workflowId) {
52542
52585
  return this.enqueuePaneLayoutMutation(async () => {
@@ -57469,7 +57512,14 @@ function executionFromState(state) {
57469
57512
  };
57470
57513
  }
57471
57514
  async function stopEnvironmentExecWorkers(environmentId, context, options2 = {}) {
57472
- await stopGoalCheckWorkers(environmentId, context);
57515
+ try {
57516
+ await stopGoalCheckWorkers(environmentId, context);
57517
+ } catch (error) {
57518
+ const environment = await context.storage.getEnvironment(environmentId);
57519
+ if (options2.requireStopped || environment?.environmentType === "local")
57520
+ throw error;
57521
+ console.warn(`[public-api] Could not stop goal checks: ${boundedMessage(error)}`);
57522
+ }
57473
57523
  if (typeof context.storage.publicOperations?.listActivePublicOperations !== "function")
57474
57524
  return;
57475
57525
  const environment = await context.storage.getEnvironment(environmentId);
@@ -61862,11 +61912,13 @@ function parseGatewayEventData(data) {
61862
61912
  return null;
61863
61913
  return { event: parsed.event, payload: parsed.payload };
61864
61914
  }
61865
- function exceedsUtf8Bytes(value, maxBytes) {
61866
- if (value.length > maxBytes)
61867
- return true;
61868
- if (value.length * 3 <= maxBytes)
61869
- return false;
61915
+ function isHighSurrogate(code) {
61916
+ return code >= 55296 && code <= 56319;
61917
+ }
61918
+ function isLowSurrogate(code) {
61919
+ return code >= 56320 && code <= 57343;
61920
+ }
61921
+ function utf8Bytes4(value, limit = Number.POSITIVE_INFINITY) {
61870
61922
  let bytes = 0;
61871
61923
  for (let index = 0;index < value.length; index += 1) {
61872
61924
  const code = value.charCodeAt(index);
@@ -61874,25 +61926,30 @@ function exceedsUtf8Bytes(value, maxBytes) {
61874
61926
  bytes += 1;
61875
61927
  else if (code <= 2047)
61876
61928
  bytes += 2;
61877
- else if (code >= 55296 && code <= 56319 && index + 1 < value.length) {
61878
- const next = value.charCodeAt(index + 1);
61879
- if (next >= 56320 && next <= 57343) {
61880
- bytes += 4;
61881
- index += 1;
61882
- } else {
61883
- bytes += 3;
61884
- }
61929
+ else if (isHighSurrogate(code) && isLowSurrogate(value.charCodeAt(index + 1))) {
61930
+ bytes += 4;
61931
+ index += 1;
61885
61932
  } else
61886
61933
  bytes += 3;
61887
- if (bytes > maxBytes)
61888
- return true;
61934
+ if (bytes > limit)
61935
+ return bytes;
61889
61936
  }
61890
- return false;
61937
+ return bytes;
61938
+ }
61939
+ function exceedsUtf8Bytes(value, maxBytes) {
61940
+ if (value.length > maxBytes)
61941
+ return true;
61942
+ if (value.length * 3 <= maxBytes)
61943
+ return false;
61944
+ return utf8Bytes4(value, maxBytes) > maxBytes;
61891
61945
  }
61892
61946
 
61893
61947
  class SseFrameDecoder {
61894
61948
  maxFrameBytes;
61895
- pending = "";
61949
+ chunks = [];
61950
+ pendingLength = 0;
61951
+ pendingBytes = 0;
61952
+ tail = "";
61896
61953
  constructor(maxFrameBytes = MAX_GATEWAY_EVENT_FRAME_BYTES) {
61897
61954
  this.maxFrameBytes = maxFrameBytes;
61898
61955
  if (!Number.isSafeInteger(maxFrameBytes) || maxFrameBytes < 1) {
@@ -61900,19 +61957,39 @@ class SseFrameDecoder {
61900
61957
  }
61901
61958
  }
61902
61959
  push(chunk) {
61903
- this.pending += chunk;
61960
+ if (!/\r?\n\r?\n/.test(this.tail + chunk)) {
61961
+ this.pendingBytes += utf8Bytes4(chunk);
61962
+ if (isHighSurrogate(this.tail.charCodeAt(this.tail.length - 1))) {
61963
+ if (isLowSurrogate(chunk.charCodeAt(0)))
61964
+ this.pendingBytes -= 2;
61965
+ }
61966
+ this.chunks.push(chunk);
61967
+ this.pendingLength += chunk.length;
61968
+ this.tail = (this.tail + chunk).slice(-MAX_SPLIT_BOUNDARY_PREFIX);
61969
+ if (this.pendingBytes > this.maxFrameBytes) {
61970
+ throw new GatewayEventFrameTooLargeError(this.maxFrameBytes);
61971
+ }
61972
+ return [];
61973
+ }
61974
+ const text = this.chunks.join("") + chunk;
61975
+ const boundary = /\r?\n\r?\n/g;
61976
+ boundary.lastIndex = this.pendingLength - this.tail.length;
61904
61977
  const frames = [];
61905
- let boundary = /\r?\n\r?\n/.exec(this.pending);
61906
- while (boundary) {
61907
- const block = this.pending.slice(0, boundary.index);
61908
- this.pending = this.pending.slice(boundary.index + boundary[0].length);
61978
+ let start = 0;
61979
+ for (let match = boundary.exec(text);match; match = boundary.exec(text)) {
61980
+ const block = text.slice(start, match.index);
61981
+ start = match.index + match[0].length;
61909
61982
  if (exceedsUtf8Bytes(block, this.maxFrameBytes)) {
61910
61983
  throw new GatewayEventFrameTooLargeError(this.maxFrameBytes);
61911
61984
  }
61912
61985
  frames.push(parseSseBlock(block));
61913
- boundary = /\r?\n\r?\n/.exec(this.pending);
61914
61986
  }
61915
- if (exceedsUtf8Bytes(this.pending, this.maxFrameBytes)) {
61987
+ const remainder = text.slice(start);
61988
+ this.chunks = remainder ? [remainder] : [];
61989
+ this.pendingLength = remainder.length;
61990
+ this.pendingBytes = utf8Bytes4(remainder);
61991
+ this.tail = remainder.slice(-MAX_SPLIT_BOUNDARY_PREFIX);
61992
+ if (this.pendingBytes > this.maxFrameBytes) {
61916
61993
  throw new GatewayEventFrameTooLargeError(this.maxFrameBytes);
61917
61994
  }
61918
61995
  return frames;
@@ -61980,7 +62057,7 @@ class GatewayEventStreamState {
61980
62057
  return { kind: "event", event: parsed.event, payload: parsed.payload };
61981
62058
  }
61982
62059
  }
61983
- var NATIVE_EVENT_STREAM_CONNECTED_EVENT = "native-event-stream-connected", TERMINAL_EVENT_STREAM_CONNECTED_EVENT = "terminal-event-stream-connected", GATEWAY_CONNECTED_EVENT = "gateway.connected", GATEWAY_RECONCILE_REQUIRED_EVENT = "gateway.reconcile-required", GATEWAY_CURSOR_EVENT = "gateway.cursor", GATEWAY_EVENT_CURSOR_PARAM = "since", GATEWAY_KEEPALIVE_EVENTS_PARAM = "keepaliveEvents", GATEWAY_KEEPALIVE_EVENT = "gateway.keepalive", MAX_GATEWAY_EVENT_FRAME_BYTES, GatewayEventFrameTooLargeError;
62060
+ var NATIVE_EVENT_STREAM_CONNECTED_EVENT = "native-event-stream-connected", TERMINAL_EVENT_STREAM_CONNECTED_EVENT = "terminal-event-stream-connected", GATEWAY_CONNECTED_EVENT = "gateway.connected", GATEWAY_RECONCILE_REQUIRED_EVENT = "gateway.reconcile-required", GATEWAY_CURSOR_EVENT = "gateway.cursor", GATEWAY_EVENT_CURSOR_PARAM = "since", GATEWAY_KEEPALIVE_EVENTS_PARAM = "keepaliveEvents", GATEWAY_KEEPALIVE_EVENT = "gateway.keepalive", MAX_GATEWAY_EVENT_FRAME_BYTES, GatewayEventFrameTooLargeError, MAX_SPLIT_BOUNDARY_PREFIX = 3;
61984
62061
  var init_gateway_event_stream = __esm(() => {
61985
62062
  MAX_GATEWAY_EVENT_FRAME_BYTES = 8 * 1024 * 1024;
61986
62063
  GatewayEventFrameTooLargeError = class GatewayEventFrameTooLargeError extends Error {
@@ -101206,7 +101283,7 @@ class WebAnnotationToolLimiter {
101206
101283
  }
101207
101284
  }
101208
101285
  }
101209
- function utf8Bytes4(value) {
101286
+ function utf8Bytes5(value) {
101210
101287
  return Buffer.byteLength(value, "utf8");
101211
101288
  }
101212
101289
  function success2(value) {
@@ -101308,11 +101385,11 @@ function boundedEvidence(value) {
101308
101385
  entries,
101309
101386
  truncated
101310
101387
  });
101311
- while (utf8Bytes4(serialize()) > MAX_EVIDENCE_RESPONSE_BYTES2 && entries.length > 0) {
101388
+ while (utf8Bytes5(serialize()) > MAX_EVIDENCE_RESPONSE_BYTES2 && entries.length > 0) {
101312
101389
  entries.pop();
101313
101390
  truncated = true;
101314
101391
  }
101315
- if (utf8Bytes4(serialize()) > MAX_EVIDENCE_RESPONSE_BYTES2) {
101392
+ if (utf8Bytes5(serialize()) > MAX_EVIDENCE_RESPONSE_BYTES2) {
101316
101393
  return { ok: true, annotation: value.annotation, capture: null, entries: [], truncated: true };
101317
101394
  }
101318
101395
  return { ok: true, annotation: value.annotation, capture: value.capture, entries, truncated };
@@ -101333,7 +101410,7 @@ function registerWebAnnotationTools(server, host, scope, limiter) {
101333
101410
  return await limiter.run(scope, async () => {
101334
101411
  const { request, brief, latestResult } = await assigned(host, scope);
101335
101412
  const summary = requestSummary(request, latestResult ?? null);
101336
- const briefTruncated = utf8Bytes4(brief) > MAX_BRIEF_RESPONSE_BYTES;
101413
+ const briefTruncated = utf8Bytes5(brief) > MAX_BRIEF_RESPONSE_BYTES;
101337
101414
  return success2({
101338
101415
  ok: true,
101339
101416
  request: summary,
@@ -135283,10 +135360,10 @@ function terminalOutputPayload(data, revision, generation) {
135283
135360
  function createTerminalOutputBuffer() {
135284
135361
  return { chunks: [], headIndex: 0, headOffset: 0, length: 0 };
135285
135362
  }
135286
- function isHighSurrogate(codeUnit) {
135363
+ function isHighSurrogate2(codeUnit) {
135287
135364
  return codeUnit >= 55296 && codeUnit <= 56319;
135288
135365
  }
135289
- function isLowSurrogate(codeUnit) {
135366
+ function isLowSurrogate2(codeUnit) {
135290
135367
  return codeUnit >= 56320 && codeUnit <= 57343;
135291
135368
  }
135292
135369
  function trimOrphanedLowSurrogate(buffer) {
@@ -135295,11 +135372,11 @@ function trimOrphanedLowSurrogate(buffer) {
135295
135372
  const head = buffer.chunks[buffer.headIndex];
135296
135373
  if (head === undefined || buffer.length === 0)
135297
135374
  return;
135298
- if (!isLowSurrogate(head.charCodeAt(buffer.headOffset)))
135375
+ if (!isLowSurrogate2(head.charCodeAt(buffer.headOffset)))
135299
135376
  return;
135300
135377
  const previousChunk = buffer.chunks[buffer.headIndex - 1] ?? "";
135301
135378
  const precedingCodeUnit = buffer.headOffset > 0 ? head.charCodeAt(buffer.headOffset - 1) : previousChunk.charCodeAt(previousChunk.length - 1);
135302
- if (!isHighSurrogate(precedingCodeUnit))
135379
+ if (!isHighSurrogate2(precedingCodeUnit))
135303
135380
  return;
135304
135381
  buffer.headOffset += 1;
135305
135382
  buffer.length -= 1;
@@ -139823,6 +139900,9 @@ function toNodeProjectSummary(nodeId, raw, index) {
139823
139900
  ...icon ? { icon } : {}
139824
139901
  };
139825
139902
  }
139903
+ function isMailCount(value) {
139904
+ return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= MAX_SUMMARY_MAIL_COUNT;
139905
+ }
139826
139906
  function toNodeEnvironmentSummary(nodeId, raw, index = 0) {
139827
139907
  if (typeof raw.id !== "string" || !raw.id || typeof raw.projectId !== "string")
139828
139908
  return null;
@@ -139846,7 +139926,10 @@ function toNodeEnvironmentSummary(nodeId, raw, index = 0) {
139846
139926
  deleting: raw.lifecycleOperation === "deleting" || typeof raw.deletionRequestedAt === "string" && raw.deletionRequestedAt.length > 0,
139847
139927
  isProjectHome: raw.projectHome === true,
139848
139928
  ...raw.buildPipeline === true ? { buildPipeline: true } : {},
139929
+ ...raw.buildActive === true ? { buildActive: true } : {},
139849
139930
  ...raw.autoMerge === true ? { autoMerge: true } : {},
139931
+ ...isMailCount(raw.agentMailUnread) ? { agentMailUnread: raw.agentMailUnread } : {},
139932
+ ...raw.agentMailFailed === true ? { agentMailFailed: true } : {},
139850
139933
  ...PR_STATES2.has(raw.prState) ? { prState: raw.prState } : {},
139851
139934
  ...typeof raw.hasMergeConflicts === "boolean" ? { prHasMergeConflicts: raw.hasMergeConflicts } : {},
139852
139935
  ...isPrMergeability(raw.prMergeability) ? { prMergeability: raw.prMergeability } : {},
@@ -139857,7 +139940,7 @@ function toNodeEnvironmentSummary(nodeId, raw, index = 0) {
139857
139940
  ...typeof raw.prViewerCanMerge === "boolean" ? { prViewerCanMerge: raw.prViewerCanMerge } : {}
139858
139941
  };
139859
139942
  }
139860
- var NODE_SUMMARY_CHANGED_EVENT = "node-summary-changed", NODE_SUMMARY_ENVIRONMENT_READ_ARGS, MAX_SUMMARY_PROJECTS = 500, MAX_SUMMARY_ENVIRONMENTS_PER_PROJECT = 500, MAX_TEXT2 = 256, MAX_PATH_NAME = 128, MAX_TIMESTAMP = 64, PR_STATES2;
139943
+ var NODE_SUMMARY_CHANGED_EVENT = "node-summary-changed", NODE_SUMMARY_ENVIRONMENT_READ_ARGS, MAX_SUMMARY_PROJECTS = 500, MAX_SUMMARY_ENVIRONMENTS_PER_PROJECT = 500, MAX_TEXT2 = 256, MAX_PATH_NAME = 128, MAX_TIMESTAMP = 64, PR_STATES2, MAX_SUMMARY_MAIL_COUNT = 9999;
139861
139944
  var init_node_summaries = __esm(() => {
139862
139945
  init_federation();
139863
139946
  init_pr_merge_readiness();
@@ -146990,7 +147073,7 @@ class NodeReadModel {
146990
147073
  noteLocalEvent(event, payload) {
146991
147074
  if (event === RESOURCE_CHANGED_EVENT) {
146992
147075
  const resource = payload?.resource;
146993
- if (resource !== "project" && resource !== "environment" && resource !== "environment-pr-health" && resource !== "build-pipeline" && resource !== "config") {
147076
+ if (resource !== "project" && resource !== "environment" && resource !== "environment-pr-health" && resource !== "build-pipeline" && resource !== "agent-mail-summary" && resource !== "config") {
146994
147077
  return;
146995
147078
  }
146996
147079
  } else if (event !== BACKEND_PROFILE_CHANGED_EVENT) {
@@ -147173,6 +147256,10 @@ class NodeReadModel {
147173
147256
  this.scheduleProjectRefresh(state, projectId);
147174
147257
  else
147175
147258
  this.requestHydrate(nodeId);
147259
+ } else if (change.resource === "agent-mail-summary") {
147260
+ for (const project of state.projects) {
147261
+ this.scheduleProjectRefresh(state, project.ref.projectId);
147262
+ }
147176
147263
  }
147177
147264
  }
147178
147265
  projectOfEnvironment(state, environmentId) {
@@ -151459,6 +151546,41 @@ var init_node_ssh = __esm(() => {
151459
151546
  init_ssh();
151460
151547
  });
151461
151548
 
151549
+ // ../../apps/backend/src/core/nodes/node-summary-markers.ts
151550
+ function environmentMailMarkers(summary) {
151551
+ const totals = new Map;
151552
+ for (const mailbox of summary.mailboxes) {
151553
+ const total = totals.get(mailbox.environmentId) ?? { unread: 0, failed: false };
151554
+ total.unread += mailbox.userUnseenCount ?? mailbox.unreadCount;
151555
+ total.failed ||= mailbox.failedInjectCount > 0;
151556
+ totals.set(mailbox.environmentId, total);
151557
+ }
151558
+ const markers = new Map;
151559
+ for (const [environmentId, total] of totals) {
151560
+ const unread = Math.min(Math.max(0, Math.trunc(total.unread)), MAX_SUMMARY_MAIL_COUNT);
151561
+ if (unread === 0 && !total.failed)
151562
+ continue;
151563
+ markers.set(environmentId, {
151564
+ ...unread > 0 ? { agentMailUnread: unread } : {},
151565
+ ...total.failed ? { agentMailFailed: true } : {}
151566
+ });
151567
+ }
151568
+ return markers;
151569
+ }
151570
+ function isWorkingPipelineRecord(record) {
151571
+ const snapshot = record.snapshot;
151572
+ if (!snapshot || typeof snapshot !== "object" || typeof snapshot.phase !== "string")
151573
+ return false;
151574
+ return isWorkingBuildPipeline({
151575
+ phase: snapshot.phase,
151576
+ stallWarning: snapshot.stallWarning === undefined ? undefined : snapshot.stallWarning
151577
+ });
151578
+ }
151579
+ var init_node_summary_markers = __esm(() => {
151580
+ init_build_pipeline();
151581
+ init_node_summaries();
151582
+ });
151583
+
151462
151584
  // ../../apps/backend/src/core/nodes/index.ts
151463
151585
  var init_nodes = __esm(() => {
151464
151586
  init_node_action_requests();
@@ -151473,6 +151595,7 @@ var init_nodes = __esm(() => {
151473
151595
  init_node_seed();
151474
151596
  init_node_service();
151475
151597
  init_node_ssh();
151598
+ init_node_summary_markers();
151476
151599
  init_project_summary_icons();
151477
151600
  });
151478
151601
 
@@ -168453,7 +168576,7 @@ async function sendInteractiveData(data, sendLiteral, sendKeys) {
168453
168576
  }
168454
168577
  await flushLiteral();
168455
168578
  }
168456
- var CLAUDE_TMUX_EVENT = "claude-tmux:event", POLL_INTERVAL_MS = 250, TMUX_OBSERVATION_INTERVAL_MS = 3000, TMUX_BUSY_OBSERVATION_INTERVAL_MS = 500, LIVENESS_CHECK_EVERY_TICKS = 8, HOOK_TIMEOUT_SECS, HOOK_HOLD_CEILING_SECS, HOOK_COMMAND_TIMEOUT_SECS, HOOK_LIMIT_MARGIN_SECS = 60, HOOK_HOLD_PROTOCOL = "hold-v1", COMMAND_IDLE_TIMEOUT_MS = 8000, COMMAND_NO_HOOK_SETTLE_MS = 2000, COMMAND_AFTER_IDLE_SETTLE_MS = 400, PERMISSION_MODE_SWITCH_TIMEOUT_MS = 1500, PERMISSION_MODE_POLL_MS = 100, FAST_MODE_SWITCH_TIMEOUT_MS = 2500, FAST_MODE_POLL_MS = 100, FAST_MODE_TMUX_OPTION = "@orkestrator_fast_mode", BACKUP_SENTINEL_NO_ORIGINAL = "__orkestrator_no_original__", CLAUDE_SETTINGS_LOCAL_GIT_EXCLUDE_PATTERN = ".claude/settings.local.json", RUNTIME_ROOT_PREFIX = "/tmp/orkestrator-v2-claude-tmux", THINKING_MODE_ARGS, THINKING_DISPLAY_FLAG = "--thinking-display", THINKING_DISPLAY_VALUE = "summarized", THINKING_DISPLAY_PROBE_VALUE = "__orkestrator_probe__", THINKING_DISPLAY_PROBE_TIMEOUT_MS = 1e4, HOOK_EVENT_KINDS, POLL_SNAPSHOT_PENDING_MARKER = "__ork_pending__", POLL_SNAPSHOT_TIMEOUT_MARKER = "__ork_timeout__", POLL_SNAPSHOT_SIZE_MARKER = "__ork_size__", TRANSCRIPT_HEAD_MARKER = "__ork_head__", MAX_PREVIOUS_SESSIONS = 50, PREVIOUS_SESSION_STAT_CONCURRENCY = 8, INTERACTIVE_KEY_SEQUENCES;
168579
+ var CLAUDE_TMUX_EVENT = "claude-tmux:event", POLL_INTERVAL_MS = 250, TMUX_OBSERVATION_INTERVAL_MS = 3000, TMUX_BUSY_OBSERVATION_INTERVAL_MS = 500, LIVENESS_CHECK_EVERY_TICKS = 8, TMUX_IDLE_AFTER_QUIET_TICKS = 8, TMUX_IDLE_POLL_INTERVAL_MS = 1000, TMUX_IDLE_LIVENESS_CHECK_EVERY_TICKS = 2, TMUX_DISCOVERY_MIN_INTERVAL_MS = 2000, TMUX_DISCOVERY_MAX_INTERVAL_MS = 1e4, HOOK_TIMEOUT_SECS, HOOK_HOLD_CEILING_SECS, HOOK_COMMAND_TIMEOUT_SECS, HOOK_LIMIT_MARGIN_SECS = 60, HOOK_HOLD_PROTOCOL = "hold-v1", COMMAND_IDLE_TIMEOUT_MS = 8000, COMMAND_NO_HOOK_SETTLE_MS = 2000, COMMAND_AFTER_IDLE_SETTLE_MS = 400, PERMISSION_MODE_SWITCH_TIMEOUT_MS = 1500, PERMISSION_MODE_POLL_MS = 100, FAST_MODE_SWITCH_TIMEOUT_MS = 2500, FAST_MODE_POLL_MS = 100, FAST_MODE_TMUX_OPTION = "@orkestrator_fast_mode", BACKUP_SENTINEL_NO_ORIGINAL = "__orkestrator_no_original__", CLAUDE_SETTINGS_LOCAL_GIT_EXCLUDE_PATTERN = ".claude/settings.local.json", RUNTIME_ROOT_PREFIX = "/tmp/orkestrator-v2-claude-tmux", THINKING_MODE_ARGS, THINKING_DISPLAY_FLAG = "--thinking-display", THINKING_DISPLAY_VALUE = "summarized", THINKING_DISPLAY_PROBE_VALUE = "__orkestrator_probe__", THINKING_DISPLAY_PROBE_TIMEOUT_MS = 1e4, HOOK_EVENT_KINDS, POLL_SNAPSHOT_PENDING_MARKER = "__ork_pending__", POLL_SNAPSHOT_TIMEOUT_MARKER = "__ork_timeout__", POLL_SNAPSHOT_SIZE_MARKER = "__ork_size__", TRANSCRIPT_HEAD_MARKER = "__ork_head__", MAX_PREVIOUS_SESSIONS = 50, PREVIOUS_SESSION_STAT_CONCURRENCY = 8, INTERACTIVE_KEY_SEQUENCES;
168457
168580
  var init_tmux_shared = __esm(() => {
168458
168581
  init_agent_tools();
168459
168582
  init_shell();
@@ -172987,14 +173110,19 @@ ${help.stderr}`;
172987
173110
  const emittedBlockingIds = new Set;
172988
173111
  let tail;
172989
173112
  let tick = 0;
173113
+ let quietTicks = 0;
173114
+ let nextDiscoveryAt = 0;
173115
+ let discoveryInterval = TMUX_DISCOVERY_MIN_INTERVAL_MS;
172990
173116
  (async () => {
172991
173117
  try {
172992
173118
  while (!this.stopRequested) {
172993
- await this.pollTick(POLL_INTERVAL_MS);
173119
+ const idle = !this.busy && quietTicks >= TMUX_IDLE_AFTER_QUIET_TICKS;
173120
+ await this.pollTick(idle ? TMUX_IDLE_POLL_INTERVAL_MS : POLL_INTERVAL_MS);
172994
173121
  if (this.stopRequested)
172995
173122
  break;
172996
173123
  tick += 1;
172997
- if (!tail) {
173124
+ quietTicks += 1;
173125
+ if (!tail && Date.now() >= nextDiscoveryAt) {
172998
173126
  try {
172999
173127
  const transcriptPath = await this.discoverTranscriptPath();
173000
173128
  if (transcriptPath)
@@ -173002,6 +173130,8 @@ ${help.stderr}`;
173002
173130
  } catch (error) {
173003
173131
  console.warn("[tmux] transcript discovery failed", error);
173004
173132
  }
173133
+ nextDiscoveryAt = Date.now() + discoveryInterval;
173134
+ discoveryInterval = Math.min(discoveryInterval * 2, TMUX_DISCOVERY_MAX_INTERVAL_MS);
173005
173135
  }
173006
173136
  let snapshot;
173007
173137
  try {
@@ -173010,6 +173140,11 @@ ${help.stderr}`;
173010
173140
  console.warn("[tmux] poll snapshot failed", error);
173011
173141
  }
173012
173142
  if (snapshot) {
173143
+ if (snapshot.pending.length > 0 || snapshot.timeouts.length > 0) {
173144
+ quietTicks = 0;
173145
+ nextDiscoveryAt = 0;
173146
+ discoveryInterval = TMUX_DISCOVERY_MIN_INTERVAL_MS;
173147
+ }
173013
173148
  try {
173014
173149
  const events = await drainPending(this.backend, this.sessionHookPaths, snapshot.pending, emittedBlockingIds);
173015
173150
  for (const event of events)
@@ -173036,6 +173171,8 @@ ${help.stderr}`;
173036
173171
  if (tail) {
173037
173172
  try {
173038
173173
  const lines = await tail.readNew(this.backend, snapshot.transcriptSize);
173174
+ if (lines.length > 0)
173175
+ quietTicks = 0;
173039
173176
  for (const line of lines) {
173040
173177
  const permissionMode = permissionModeFromTranscriptLine(line);
173041
173178
  if (permissionMode)
@@ -173070,7 +173207,8 @@ ${help.stderr}`;
173070
173207
  });
173071
173208
  this.observationInFlight = observation;
173072
173209
  }
173073
- if (tick % LIVENESS_CHECK_EVERY_TICKS !== 0)
173210
+ const livenessEvery = idle ? TMUX_IDLE_LIVENESS_CHECK_EVERY_TICKS : LIVENESS_CHECK_EVERY_TICKS;
173211
+ if (tick % livenessEvery !== 0)
173074
173212
  continue;
173075
173213
  if (!await this.tmuxAlive().catch(() => false)) {
173076
173214
  let removed = false;
@@ -200213,13 +200351,14 @@ async function stopEnvironmentOnce(environmentId, context, invalidateDiscovery,
200213
200351
  const environment = await storage.getEnvironment(environmentId);
200214
200352
  if (!environment)
200215
200353
  throw new Error(`Environment not found: ${environmentId}`);
200354
+ if (environment.containerId)
200355
+ await assertDockerContainerOwned(environment.containerId, context);
200216
200356
  await stopEnvironmentReviewValidation(environmentId, context);
200217
200357
  await stopEnvironmentExecWorkers(environmentId, context);
200218
200358
  invalidateDiscovery(environment.id);
200219
200359
  context.previews?.registry.beforeEnvironmentTargetChange(environment.id);
200220
200360
  if (environment.containerId) {
200221
200361
  const containerId = environment.containerId;
200222
- await assertDockerContainerOwned(containerId, context);
200223
200362
  await context.nativeAgents?.runtimeStopRequested(environment.id);
200224
200363
  const outcome = await runContainerOperation(context, environment.id, "stop", identity, async (operation) => {
200225
200364
  const runtime = currentRuntimeIdentity(environment, context);
@@ -280152,7 +280291,7 @@ async function readRun(context, runId) {
280152
280291
  function changed(run) {
280153
280292
  return { status: "changed", run: graphRunViewSnapshot(run) };
280154
280293
  }
280155
- function utf8Bytes5(text) {
280294
+ function utf8Bytes6(text) {
280156
280295
  return new TextEncoder().encode(text).byteLength;
280157
280296
  }
280158
280297
  async function readArtifact(context, runId, artifactId) {
@@ -280169,7 +280308,7 @@ async function readArtifact(context, runId, artifactId) {
280169
280308
  return view;
280170
280309
  let size;
280171
280310
  try {
280172
- size = utf8Bytes5(canonicalJson2(body.value, 128));
280311
+ size = utf8Bytes6(canonicalJson2(body.value, 128));
280173
280312
  } catch {
280174
280313
  size = Number.POSITIVE_INFINITY;
280175
280314
  }
@@ -291179,6 +291318,8 @@ init_container_resources();
291179
291318
  init_portable_input_status();
291180
291319
  init_agent_settings();
291181
291320
  init_agent_platforms();
291321
+ init_agent_mail();
291322
+ init_nodes();
291182
291323
  init_storage();
291183
291324
  init_docker_ownership();
291184
291325
  init_storage_projects();
@@ -292101,11 +292242,16 @@ function registerEnvironmentCommands(register, dependencies) {
292101
292242
  await runtime.prMonitor.sync(context);
292102
292243
  const environments = await load();
292103
292244
  const announced = runtime.prMonitor.announcedStates();
292104
- const buildEnvironmentIds = new Set((await storage.listBuildPipelines(asString4(args.projectId, "projectId"))).map((pipeline) => pipeline.environmentId));
292245
+ const pipelines = await storage.listBuildPipelines(asString4(args.projectId, "projectId"));
292246
+ const buildEnvironmentIds = new Set(pipelines.map((pipeline) => pipeline.environmentId));
292247
+ const activeBuildEnvironmentIds = new Set(pipelines.filter(isWorkingPipelineRecord).map((pipeline) => pipeline.environmentId));
292248
+ const mail = normalizeAgentMessagingSettings((await storage.loadConfig()).global.agentMessaging).enabled ? environmentMailMarkers(await storage.getAgentMailSummary()) : new Map;
292105
292249
  return environments.map((environment) => ({
292106
292250
  ...environment,
292107
292251
  ...runtime.prMonitor.relayedHealth(environment, announced),
292108
- ...buildEnvironmentIds.has(environment.id) ? { buildPipeline: true } : {}
292252
+ ...buildEnvironmentIds.has(environment.id) ? { buildPipeline: true } : {},
292253
+ ...activeBuildEnvironmentIds.has(environment.id) ? { buildActive: true } : {},
292254
+ ...mail.get(environment.id)
292109
292255
  }));
292110
292256
  });
292111
292257
  register("get_environment_pr_health", async (args, { storage, runtime }) => {
@@ -302710,10 +302856,12 @@ import { createInterface } from "readline";
302710
302856
  var MAX_TRACKED = 256;
302711
302857
  var RESTART_MIN_MS = 1000;
302712
302858
  var RESTART_MAX_MS = 60000;
302859
+ var FOLLOWED_EVENTS = ["oom", "start", "die", "stop", "destroy"];
302713
302860
 
302714
302861
  class OomEventWatcher {
302715
302862
  owner;
302716
302863
  spawn;
302864
+ onLifecycle;
302717
302865
  counts = new Map;
302718
302866
  child = null;
302719
302867
  restartTimer = null;
@@ -302722,9 +302870,13 @@ class OomEventWatcher {
302722
302870
  running = false;
302723
302871
  resumeFrom = null;
302724
302872
  lastSeenNano = 0n;
302725
- constructor(owner, spawn = spawnParentBoundCommand) {
302873
+ lastOomNano = 0n;
302874
+ constructor(owner, spawn = spawnParentBoundCommand, onLifecycle = () => {
302875
+ return;
302876
+ }) {
302726
302877
  this.owner = owner;
302727
302878
  this.spawn = spawn;
302879
+ this.onLifecycle = onLifecycle;
302728
302880
  }
302729
302881
  start() {
302730
302882
  if (this.stopped || this.child)
@@ -302733,13 +302885,12 @@ class OomEventWatcher {
302733
302885
  "events",
302734
302886
  "--filter",
302735
302887
  "type=container",
302736
- "--filter",
302737
- "event=oom",
302888
+ ...FOLLOWED_EVENTS.flatMap((event) => ["--filter", `event=${event}`]),
302738
302889
  "--filter",
302739
302890
  `label=orkestrator-owner=${this.owner}`,
302740
302891
  ...this.resumeFrom !== null ? ["--since", String(this.resumeFrom)] : [],
302741
302892
  "--format",
302742
- "{{.Actor.ID}}\t{{.TimeNano}}"
302893
+ "{{.Actor.ID}}\t{{.TimeNano}}\t{{.Action}}"
302743
302894
  ]);
302744
302895
  this.child = child;
302745
302896
  this.running = true;
@@ -302770,14 +302921,20 @@ class OomEventWatcher {
302770
302921
  child.kill("SIGTERM");
302771
302922
  }
302772
302923
  record(line) {
302773
- const [id = "", nanos = ""] = line.trim().split("\t");
302924
+ const [id = "", nanos = "", action = "oom"] = line.trim().split("\t");
302774
302925
  if (!/^[0-9a-f]{12,64}$/.test(id) || !/^\d{1,20}$/.test(nanos))
302775
302926
  return;
302776
302927
  this.restartDelay = RESTART_MIN_MS;
302777
302928
  const at = BigInt(nanos);
302778
- if (at <= this.lastSeenNano)
302929
+ if (at > this.lastSeenNano)
302930
+ this.lastSeenNano = at;
302931
+ if (action !== "oom") {
302932
+ this.onLifecycle();
302933
+ return;
302934
+ }
302935
+ if (at <= this.lastOomNano)
302779
302936
  return;
302780
- this.lastSeenNano = at;
302937
+ this.lastOomNano = at;
302781
302938
  const lastAt = new Date(Number(at / 1000000n)).toISOString();
302782
302939
  const current = this.counts.get(id);
302783
302940
  this.counts.delete(id);
@@ -302809,14 +302966,16 @@ class OomEventWatcher {
302809
302966
 
302810
302967
  class ContainerOomEvents {
302811
302968
  spawn;
302969
+ onLifecycle;
302812
302970
  watcher = null;
302813
- constructor(spawn = spawnParentBoundCommand) {
302971
+ constructor(spawn = spawnParentBoundCommand, onLifecycle) {
302814
302972
  this.spawn = spawn;
302973
+ this.onLifecycle = onLifecycle;
302815
302974
  }
302816
302975
  start(owner) {
302817
302976
  if (this.watcher)
302818
302977
  return;
302819
- this.watcher = new OomEventWatcher(owner, this.spawn);
302978
+ this.watcher = new OomEventWatcher(owner, this.spawn, this.onLifecycle);
302820
302979
  this.watcher.start();
302821
302980
  }
302822
302981
  count(containerId) {
@@ -308269,7 +308428,7 @@ class TerminalRuntime {
308269
308428
  if (available > excess) {
308270
308429
  let trim = excess;
308271
308430
  const boundary = buffer.headOffset + trim;
308272
- if (isLowSurrogate(head.charCodeAt(boundary)) && isHighSurrogate(head.charCodeAt(boundary - 1))) {
308431
+ if (isLowSurrogate2(head.charCodeAt(boundary)) && isHighSurrogate2(head.charCodeAt(boundary - 1))) {
308273
308432
  trim += 1;
308274
308433
  }
308275
308434
  buffer.headOffset += trim;
@@ -308798,7 +308957,7 @@ class BackendRuntime {
308798
308957
  this.prMonitor = new PrMonitorRuntime(options2.prMonitor);
308799
308958
  this.tmux = new TmuxRuntime(options2.tmux);
308800
308959
  this.accounts = new AgentAccountsRuntime(options2.accounts);
308801
- this.containerOom = new ContainerOomEvents(options2.containerOom?.spawn);
308960
+ this.containerOom = new ContainerOomEvents(options2.containerOom?.spawn, () => this.containerStates.invalidate());
308802
308961
  this.containerLogs = new ContainerLogService(options2.containerLogs?.spawn, options2.containerLogs?.now);
308803
308962
  }
308804
308963
  attachChangeTracking(service) {
@@ -309020,6 +309179,13 @@ function firewallDisplayName(kind) {
309020
309179
  return "your firewall";
309021
309180
  }
309022
309181
  }
309182
+ function sudoForEachSource(sources, rule, then) {
309183
+ if (sources.length === 1) {
309184
+ return [`sudo ${rule(sources[0])}`, ...then ? [`sudo ${then}`] : []].join(" && ");
309185
+ }
309186
+ const loop = `for net in ${sources.join(" ")}; do ${rule("$net")} || exit 1; done`;
309187
+ return `sudo sh -c '${loop}${then ? ` && ${then}` : ""}'`;
309188
+ }
309023
309189
  function buildContainerHostRemediation(input) {
309024
309190
  const { firewall, port } = input;
309025
309191
  const blocking = input.outcomes.filter(isBlockingOutcome);
@@ -309051,15 +309217,16 @@ function buildContainerHostRemediation(input) {
309051
309217
  return {
309052
309218
  title: "Allow Docker containers through ufw",
309053
309219
  steps: [intro, scope, after],
309054
- commands: cidrs.map((cidr) => `sudo ufw allow proto tcp from ${cidr} to any port ${port} comment 'Orkestrator agent tools'`)
309220
+ commands: [
309221
+ sudoForEachSource(cidrs, (source) => `ufw allow proto tcp from ${source} to any port ${port} comment "Orkestrator agent tools"`)
309222
+ ]
309055
309223
  };
309056
309224
  case "firewalld":
309057
309225
  return {
309058
309226
  title: "Allow Docker containers through firewalld",
309059
309227
  steps: [intro, scope, after],
309060
309228
  commands: [
309061
- ...cidrs.map((cidr) => `sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="${cidr}" port port="${port}" protocol="tcp" accept'`),
309062
- "sudo firewall-cmd --reload"
309229
+ sudoForEachSource(cidrs, (source) => `firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${source} port port=${port} protocol=tcp accept"`, "firewall-cmd --reload")
309063
309230
  ]
309064
309231
  };
309065
309232
  default:
@@ -309071,7 +309238,9 @@ function buildContainerHostRemediation(input) {
309071
309238
  "These iptables rules take effect immediately but are lost on reboot. Add the same rule to your firewall's saved configuration to keep it.",
309072
309239
  after
309073
309240
  ],
309074
- commands: cidrs.map((cidr) => `sudo iptables -I INPUT -p tcp -s ${cidr} --dport ${port} -j ACCEPT`)
309241
+ commands: [
309242
+ sudoForEachSource(cidrs, (source) => `iptables -I INPUT -p tcp -s ${source} --dport ${port} -j ACCEPT`)
309243
+ ]
309075
309244
  };
309076
309245
  }
309077
309246
  }
@@ -309086,12 +309255,14 @@ function buildRootlessHostRemediation(port) {
309086
309255
  `Then press \u201CCheck again\u201D. New containers reach Orkestrator through RootlessKit's host address (${ROOTLESSKIT_HOST_LOOPBACK_ADDRESS}); rebuild existing environments so their network policy includes it.`
309087
309256
  ],
309088
309257
  commands: [
309089
- "mkdir -p ~/.config/systemd/user/docker.service.d",
309090
- `printf '[Service]
309258
+ [
309259
+ "mkdir -p ~/.config/systemd/user/docker.service.d",
309260
+ `printf '[Service]
309091
309261
  Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=false"
309092
309262
  ' > ${ROOTLESS_HOST_LOOPBACK_DROP_IN}`,
309093
- "systemctl --user daemon-reload",
309094
- "systemctl --user restart docker"
309263
+ "systemctl --user daemon-reload",
309264
+ "systemctl --user restart docker"
309265
+ ].join(" && ")
309095
309266
  ]
309096
309267
  };
309097
309268
  }
@@ -327197,6 +327368,8 @@ class AgentMailService {
327197
327368
  return task;
327198
327369
  }
327199
327370
  async refreshPresenceOnce() {
327371
+ if (!(await this.storage.loadConfig()).global.agentMessaging?.enabled)
327372
+ return;
327200
327373
  const mailboxes = [];
327201
327374
  let offset = 0;
327202
327375
  while (true) {
@@ -338477,7 +338650,7 @@ function parseGatewayCompressionMode(value, source = "gateway compression mode")
338477
338650
  throw new Error(`Invalid ${source}: ${value}. Expected one of off, body, on.`);
338478
338651
  }
338479
338652
  function resolveGatewayCompressionMode(explicit, env = process.env) {
338480
- return explicit ?? parseGatewayCompressionMode(env.ORKESTRATOR_GATEWAY_COMPRESSION, "ORKESTRATOR_GATEWAY_COMPRESSION") ?? "body";
338653
+ return explicit ?? parseGatewayCompressionMode(env.ORKESTRATOR_GATEWAY_COMPRESSION, "ORKESTRATOR_GATEWAY_COMPRESSION") ?? "on";
338481
338654
  }
338482
338655
  function compressionModeForListener(compression, listenerKind) {
338483
338656
  return listenerKind === "control" ? "off" : compression;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orkestrator",
3
- "version": "2.35.0-beta",
3
+ "version": "2.35.1-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.35.0-beta",
43
- "@orkestrator/toolchain": "2.35.0-beta"
42
+ "@orkestrator/protocol": "2.35.1-beta",
43
+ "@orkestrator/toolchain": "2.35.1-beta"
44
44
  },
45
45
  "engines": {
46
46
  "bun": ">=1.4.4"
@@ -10114,7 +10114,7 @@ var MAX_SESSIONS = parseBoundedInteger(process.env.ACP_MAX_SESSIONS, 256, 1, 256
10114
10114
  var MAX_APPROVALS_PER_SESSION = 64;
10115
10115
  var MAX_STRUCTURED_RESULTS = 4;
10116
10116
  var MAX_STRUCTURED_RESULT_BYTES = 1024 * 1024;
10117
- var TRANSCRIPT_CHECK_INTERVAL_BYTES = 64 * 1024;
10117
+ var TRANSCRIPT_CHECK_INTERVAL_BYTES = 1024 * 1024;
10118
10118
  var MAX_PROMPT_JOURNAL = 512;
10119
10119
  var MAX_PERSISTED_GROK_INTERJECTION_BYTES = 512 * 1024;
10120
10120
  var MAX_STATE_FILE_BYTES = parseBoundedInteger(process.env.ACP_MAX_STATE_FILE_BYTES, 16 * 1024 * 1024, 512 * 1024, 16 * 1024 * 1024);
@@ -11279,6 +11279,24 @@ function persistedSnapshot() {
11279
11279
  };
11280
11280
  return boundPersistedSnapshot(snapshot);
11281
11281
  }
11282
+ var STREAMING_PERSIST_INTERVAL_MS = 1000;
11283
+ var streamingPersistTimer = null;
11284
+ function scheduleStreamingPersist() {
11285
+ if (!stateFile || streamingPersistTimer)
11286
+ return;
11287
+ streamingPersistTimer = setTimeout(() => {
11288
+ streamingPersistTimer = null;
11289
+ schedulePersist();
11290
+ }, STREAMING_PERSIST_INTERVAL_MS);
11291
+ streamingPersistTimer.unref();
11292
+ }
11293
+ function flushStreamingPersist() {
11294
+ if (streamingPersistTimer) {
11295
+ clearTimeout(streamingPersistTimer);
11296
+ streamingPersistTimer = null;
11297
+ }
11298
+ return persistState();
11299
+ }
11282
11300
  function schedulePersist() {
11283
11301
  if (!stateFile || persistenceScheduled)
11284
11302
  return;
@@ -12084,7 +12102,7 @@ function commitToolPartMutation(state, part, source) {
12084
12102
  failTranscriptLimit(state);
12085
12103
  return false;
12086
12104
  }
12087
- schedulePersist();
12105
+ scheduleStreamingPersist();
12088
12106
  return true;
12089
12107
  }
12090
12108
  function activeSubagentLaunch(state, toolUseId) {
@@ -19481,7 +19499,7 @@ function applySessionUpdate(state, params) {
19481
19499
  failTranscriptLimit(state);
19482
19500
  return;
19483
19501
  }
19484
- schedulePersist();
19502
+ scheduleStreamingPersist();
19485
19503
  }
19486
19504
  function composerPatchFromSpawn(options) {
19487
19505
  const patch = {};
@@ -22427,9 +22445,7 @@ function shutdown() {
22427
22445
  ...[...sessions.values()].map((state) => state.child?.close()),
22428
22446
  ...[...cursorToolReplayProcesses].map((child) => child.close())
22429
22447
  ]);
22430
- await persistenceTail.catch(() => {
22431
- return;
22432
- });
22448
+ await flushStreamingPersist();
22433
22449
  await new Promise((resolvePromise) => server.close(() => resolvePromise()));
22434
22450
  })();
22435
22451
  return shutdownPromise;
@@ -22624,6 +22640,7 @@ export {
22624
22640
  findHistoryMessage,
22625
22641
  finishSubagentTool,
22626
22642
  flattenedRetriableProviderTail,
22643
+ flushStreamingPersist,
22627
22644
  grokBridgeGeneration,
22628
22645
  grokInterjectionDigest,
22629
22646
  grokQuestionTimeoutMs,
@@ -22745,6 +22762,7 @@ export {
22745
22762
  saturatedText,
22746
22763
  scheduleCursorToolMetadataReconcile,
22747
22764
  schedulePersist,
22765
+ scheduleStreamingPersist,
22748
22766
  sendJson,
22749
22767
  server,
22750
22768
  sessionCreations,