@axiom-lattice/core 6.2.0 → 6.3.0

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/index.mjs CHANGED
@@ -2115,7 +2115,7 @@ var InMemoryModelProviderStore = class {
2115
2115
  */
2116
2116
  async listProviders(tenantId2) {
2117
2117
  return Array.from(this.providers.values()).filter(
2118
- (provider) => provider.tenantId === tenantId2
2118
+ (provider2) => provider2.tenantId === tenantId2
2119
2119
  );
2120
2120
  }
2121
2121
  /**
@@ -2129,7 +2129,7 @@ var InMemoryModelProviderStore = class {
2129
2129
  */
2130
2130
  async getProviderByName(tenantId2, name) {
2131
2131
  const providers = await this.listProviders(tenantId2);
2132
- return providers.find((provider) => provider.name === name) || null;
2132
+ return providers.find((provider2) => provider2.name === name) || null;
2133
2133
  }
2134
2134
  /**
2135
2135
  * Create a new provider configuration
@@ -2313,8 +2313,8 @@ var InMemoryModelProviderStore = class {
2313
2313
  * Clear providers and models for a specific tenant
2314
2314
  */
2315
2315
  clearByTenant(tenantId2) {
2316
- for (const [key5, provider] of Array.from(this.providers.entries())) {
2317
- if (provider.tenantId === tenantId2) {
2316
+ for (const [key5, provider2] of Array.from(this.providers.entries())) {
2317
+ if (provider2.tenantId === tenantId2) {
2318
2318
  this.providers.delete(key5);
2319
2319
  }
2320
2320
  }
@@ -3190,6 +3190,123 @@ function entryKey(tenantId2, userId, endpoint) {
3190
3190
  return JSON.stringify([tenantId2, userId, endpoint]);
3191
3191
  }
3192
3192
 
3193
+ // src/store_lattice/InMemoryLeaseStore.ts
3194
+ var InMemoryLeaseStore = class {
3195
+ /**
3196
+ * @param now Injectable clock in epoch milliseconds (tests use a fake clock).
3197
+ */
3198
+ constructor(now = Date.now) {
3199
+ this.leases = /* @__PURE__ */ new Map();
3200
+ this.now = now;
3201
+ }
3202
+ /** Atomically acquires a free/expired key (or the caller's own key). */
3203
+ async tryAcquire(key5, holder, ttlMs) {
3204
+ const existing = this.leases.get(key5);
3205
+ if (existing && Date.parse(existing.expiresAt) > this.now()) {
3206
+ if (existing.holder !== holder) return false;
3207
+ this.leases.set(key5, { ...existing, expiresAt: new Date(this.now() + ttlMs).toISOString() });
3208
+ return true;
3209
+ }
3210
+ const acquiredAt = existing && existing.holder === holder ? existing.acquiredAt : new Date(this.now()).toISOString();
3211
+ this.leases.set(key5, {
3212
+ key: key5,
3213
+ holder,
3214
+ acquiredAt,
3215
+ expiresAt: new Date(this.now() + ttlMs).toISOString()
3216
+ });
3217
+ return true;
3218
+ }
3219
+ /** Extends a lease only while `holder` still owns it. */
3220
+ async renew(key5, holder, ttlMs) {
3221
+ const existing = this.leases.get(key5);
3222
+ if (!existing || existing.holder !== holder) return false;
3223
+ this.leases.set(key5, { ...existing, expiresAt: new Date(this.now() + ttlMs).toISOString() });
3224
+ return true;
3225
+ }
3226
+ /** Deletes a lease only when `holder` owns it. */
3227
+ async release(key5, holder) {
3228
+ const existing = this.leases.get(key5);
3229
+ if (!existing || existing.holder !== holder) return false;
3230
+ this.leases.delete(key5);
3231
+ return true;
3232
+ }
3233
+ /** Returns the live lease, pruning an expired one. */
3234
+ async get(key5) {
3235
+ const existing = this.leases.get(key5);
3236
+ if (!existing) return null;
3237
+ if (Date.parse(existing.expiresAt) <= this.now()) {
3238
+ this.leases.delete(key5);
3239
+ return null;
3240
+ }
3241
+ return { ...existing };
3242
+ }
3243
+ };
3244
+
3245
+ // src/store_lattice/InMemoryOutboxStore.ts
3246
+ var InMemoryOutboxStore = class {
3247
+ /**
3248
+ * Creates an in-memory outbox store.
3249
+ *
3250
+ * @param now Optional clock override (defaults to the system clock); it stamps
3251
+ * `createdAt` and is used to evaluate age-based retention.
3252
+ */
3253
+ constructor(now = () => /* @__PURE__ */ new Date()) {
3254
+ this.events = [];
3255
+ this.counter = 0;
3256
+ this.now = now;
3257
+ }
3258
+ /**
3259
+ * Appends one event, assigning the next monotonic id.
3260
+ * @param input Topic, JSON-safe payload, and authoring replica id.
3261
+ * @returns A defensive copy of the stored event.
3262
+ */
3263
+ async append(input) {
3264
+ const event = {
3265
+ id: String(++this.counter),
3266
+ topic: input.topic,
3267
+ payload: input.payload,
3268
+ originReplicaId: input.originReplicaId,
3269
+ createdAt: this.now().toISOString()
3270
+ };
3271
+ this.events.push(event);
3272
+ return { ...event };
3273
+ }
3274
+ /**
3275
+ * Lists events strictly newer than `cursor`, ascending, bounded by `limit`.
3276
+ * @param cursor Last-seen id, or `null` to read from the start.
3277
+ * @param limit Maximum number of events to return.
3278
+ * @returns Defensive copies of the matching events.
3279
+ */
3280
+ async listAfter(cursor, limit) {
3281
+ const floor = cursor === null ? 0 : Number(cursor);
3282
+ return this.events.filter((event) => Number(event.id) > floor).slice(0, limit).map((event) => ({ ...event }));
3283
+ }
3284
+ /**
3285
+ * Returns the newest event id.
3286
+ * @returns The last id, or `null` when the log is empty.
3287
+ */
3288
+ async latestId() {
3289
+ return this.events.length === 0 ? null : this.events[this.events.length - 1].id;
3290
+ }
3291
+ /**
3292
+ * Deletes events older than `olderThanMs`, evaluated against this store's clock.
3293
+ *
3294
+ * @param olderThanMs Age threshold in milliseconds.
3295
+ * @returns The number of events removed.
3296
+ */
3297
+ async pruneOlderThan(olderThanMs) {
3298
+ const cutoff = this.now().getTime() - olderThanMs;
3299
+ let removed = 0;
3300
+ for (let i = this.events.length - 1; i >= 0; i -= 1) {
3301
+ if (new Date(this.events[i].createdAt).getTime() < cutoff) {
3302
+ this.events.splice(i, 1);
3303
+ removed += 1;
3304
+ }
3305
+ }
3306
+ return removed;
3307
+ }
3308
+ };
3309
+
3193
3310
  // src/store_lattice/InMemoryUserStore.ts
3194
3311
  var InMemoryUserStore = class {
3195
3312
  constructor() {
@@ -3396,7 +3513,8 @@ function isMessageContentValue(value) {
3396
3513
 
3397
3514
  // src/store_lattice/InMemoryThreadMessageQueueStore.ts
3398
3515
  var InMemoryThreadMessageQueueStore = class {
3399
- constructor() {
3516
+ constructor(now = Date.now) {
3517
+ this.now = now;
3400
3518
  this.messages = /* @__PURE__ */ new Map();
3401
3519
  this.messageIdCounter = 0;
3402
3520
  this.capacityTail = Promise.resolve();
@@ -3503,6 +3621,36 @@ var InMemoryThreadMessageQueueStore = class {
3503
3621
  const threadMessages = this.getMessagesForThread(threadId);
3504
3622
  return threadMessages.filter((msg) => msg.status === "processing" && this.inScope(msg, scope)).sort((a, b) => (b.priority || 0) - (a.priority || 0) || a.sequence - b.sequence).map(clonePendingMessage);
3505
3623
  }
3624
+ async claimPendingMessages(threadId, scope, options) {
3625
+ const threadMessages = this.getMessagesForThread(threadId);
3626
+ const candidates = threadMessages.filter((msg) => (msg.status === "pending" || !msg.status) && this.inScope(msg, scope)).sort((a, b) => (b.priority || 0) - (a.priority || 0) || a.sequence - b.sequence);
3627
+ const limit = options.limit === void 0 ? void 0 : Math.max(0, Math.floor(options.limit));
3628
+ const selected = limit === void 0 ? candidates : candidates.slice(0, limit);
3629
+ const now = this.now();
3630
+ for (const message of selected) {
3631
+ message.status = "processing";
3632
+ message.lockedBy = options.owner;
3633
+ message.lockedAt = new Date(now);
3634
+ message.leaseExpiresAt = new Date(now + options.ttlMs);
3635
+ }
3636
+ return selected.map(clonePendingMessage);
3637
+ }
3638
+ async reclaimExpiredProcessing(threadId, scope) {
3639
+ const messages = this.messages.get(threadId);
3640
+ if (!messages) return 0;
3641
+ const now = this.now();
3642
+ let count = 0;
3643
+ for (const msg of messages) {
3644
+ if (msg.status === "processing" && this.inScope(msg, scope) && msg.leaseExpiresAt !== void 0 && msg.leaseExpiresAt.getTime() <= now) {
3645
+ msg.status = "pending";
3646
+ msg.lockedBy = void 0;
3647
+ msg.lockedAt = void 0;
3648
+ msg.leaseExpiresAt = void 0;
3649
+ count += 1;
3650
+ }
3651
+ }
3652
+ return count;
3653
+ }
3506
3654
  async getQueueSize(threadId, scope) {
3507
3655
  const pending = await this.getPendingMessages(threadId, scope);
3508
3656
  return pending.length;
@@ -3566,6 +3714,9 @@ var InMemoryThreadMessageQueueStore = class {
3566
3714
  for (const msg of messages) {
3567
3715
  if (msg.status === "processing" && this.inScope(msg, scope)) {
3568
3716
  msg.status = "pending";
3717
+ msg.lockedBy = void 0;
3718
+ msg.lockedAt = void 0;
3719
+ msg.leaseExpiresAt = void 0;
3569
3720
  count++;
3570
3721
  }
3571
3722
  }
@@ -4824,6 +4975,8 @@ var defaultProjectBotMembershipStore = new InMemoryProjectBotMembershipStore(def
4824
4975
  var defaultProjectRoomMessageStore = new InMemoryProjectRoomMessageStore(defaultProjectRoomCoordinator);
4825
4976
  var defaultProjectRoomReadStateStore = new InMemoryProjectRoomReadStateStore();
4826
4977
  var defaultUserPushSubscriptionStore = new InMemoryUserPushSubscriptionStore();
4978
+ var defaultLeaseStore = new InMemoryLeaseStore();
4979
+ var defaultOutboxStore = new InMemoryOutboxStore();
4827
4980
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
4828
4981
  storeLatticeManager.registerLattice(
4829
4982
  "default",
@@ -4845,6 +4998,8 @@ storeLatticeManager.registerLattice("default", "projectBotMembership", defaultPr
4845
4998
  storeLatticeManager.registerLattice("default", "projectRoomMessage", defaultProjectRoomMessageStore);
4846
4999
  storeLatticeManager.registerLattice("default", "projectRoomReadState", defaultProjectRoomReadStateStore);
4847
5000
  storeLatticeManager.registerLattice("default", "userPushSubscription", defaultUserPushSubscriptionStore);
5001
+ storeLatticeManager.registerLattice("default", "lease", defaultLeaseStore);
5002
+ storeLatticeManager.registerLattice("default", "outbox", defaultOutboxStore);
4848
5003
  var defaultUserStore = new InMemoryUserStore();
4849
5004
  var defaultTenantStore = new InMemoryTenantStore();
4850
5005
  var defaultUserTenantLinkStore = new InMemoryUserTenantLinkStore();
@@ -6713,8 +6868,8 @@ var MetricsServerManager = class _MetricsServerManager {
6713
6868
  } catch {
6714
6869
  }
6715
6870
  const tenantConfigs = this.configs.get(tenantId2);
6716
- const cached = tenantConfigs?.get(key5);
6717
- if (cached) return cached;
6871
+ const cached2 = tenantConfigs?.get(key5);
6872
+ if (cached2) return cached2;
6718
6873
  throw new Error(`Metrics server '${key5}' not found for tenant '${tenantId2}'`);
6719
6874
  }
6720
6875
  /**
@@ -8024,8 +8179,8 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8024
8179
  getLatticeType() {
8025
8180
  return "sandbox_manager";
8026
8181
  }
8027
- registerLattice(key5, provider) {
8028
- this.register(key5, provider);
8182
+ registerLattice(key5, provider2) {
8183
+ this.register(key5, provider2);
8029
8184
  }
8030
8185
  getSandboxLattice(key5) {
8031
8186
  const lattice = this.get(key5);
@@ -8076,8 +8231,8 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8076
8231
  return this.getVolumeBackendForPath(config, "/project");
8077
8232
  }
8078
8233
  async getVolumeBackendForPath(config, filePath) {
8079
- const provider = this._requireProvider();
8080
- if (!provider.createVolumeFsClient) {
8234
+ const provider2 = this._requireProvider();
8235
+ if (!provider2.createVolumeFsClient) {
8081
8236
  console.log(`[getVolumeBackendForPath] provider has no createVolumeFsClient, returning null`);
8082
8237
  return null;
8083
8238
  }
@@ -8088,7 +8243,7 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8088
8243
  return null;
8089
8244
  }
8090
8245
  console.log(`[getVolumeBackendForPath] path=${filePath} volume=${mapping.volumeName} prefix=${mapping.pathPrefix}`);
8091
- const client = provider.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
8246
+ const client = provider2.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
8092
8247
  return new VolumeFilesystem(stripPrefixClient(client, mapping.pathPrefix), mapping.pathPrefix);
8093
8248
  }
8094
8249
  _resolveVolumeForPath(config, tenantId2, filePath) {
@@ -8123,35 +8278,35 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8123
8278
  };
8124
8279
  }
8125
8280
  async createSandbox(name, config) {
8126
- const provider = this._requireProvider();
8127
- return provider.createSandbox(name, config);
8281
+ const provider2 = this._requireProvider();
8282
+ return provider2.createSandbox(name, config);
8128
8283
  }
8129
8284
  async getSandbox(name) {
8130
- const provider = this._requireProvider();
8131
- return provider.getSandbox(name);
8285
+ const provider2 = this._requireProvider();
8286
+ return provider2.getSandbox(name);
8132
8287
  }
8133
8288
  async stopSandbox(name) {
8134
- const provider = this._requireProvider();
8135
- return provider.stopSandbox(name);
8289
+ const provider2 = this._requireProvider();
8290
+ return provider2.stopSandbox(name);
8136
8291
  }
8137
8292
  async deleteSandbox(name) {
8138
- const provider = this._requireProvider();
8139
- return provider.deleteSandbox(name);
8293
+ const provider2 = this._requireProvider();
8294
+ return provider2.deleteSandbox(name);
8140
8295
  }
8141
8296
  async listSandboxes() {
8142
- const provider = this._requireProvider();
8143
- return provider.listSandboxes();
8297
+ const provider2 = this._requireProvider();
8298
+ return provider2.listSandboxes();
8144
8299
  }
8145
8300
  _requireProvider() {
8146
8301
  const keys = this.keysByTenant("default");
8147
8302
  if (keys.length === 0) {
8148
8303
  throw new Error("Sandbox provider not configured.");
8149
8304
  }
8150
- const provider = this.get(keys[0]);
8151
- if (!provider) {
8305
+ const provider2 = this.get(keys[0]);
8306
+ if (!provider2) {
8152
8307
  throw new Error("Sandbox provider not configured.");
8153
8308
  }
8154
- return provider;
8309
+ return provider2;
8155
8310
  }
8156
8311
  /**
8157
8312
  * Returns the first registered (default) sandbox provider.
@@ -8181,6 +8336,46 @@ import { tool as tool14 } from "langchain";
8181
8336
  // src/tool_lattice/shell_exec/index.ts
8182
8337
  import z17 from "zod";
8183
8338
  import { tool as tool15 } from "langchain";
8339
+
8340
+ // src/sandbox_lattice/sandboxOpenCredential.ts
8341
+ var SANDBOX_OPEN_GATEWAY_URL_ENV = "API_GATEWAY_URL";
8342
+ var SANDBOX_OPEN_TOKEN_ENV = "OPEN_API_TOKEN";
8343
+ var provider;
8344
+ var providerErrorHandler;
8345
+ function registerSandboxOpenCredentialProvider(next, options = {}) {
8346
+ provider = next;
8347
+ providerErrorHandler = options.onError;
8348
+ return () => {
8349
+ if (provider === next) {
8350
+ provider = void 0;
8351
+ providerErrorHandler = void 0;
8352
+ }
8353
+ };
8354
+ }
8355
+ async function getSandboxOpenCredential(context) {
8356
+ if (!provider) return void 0;
8357
+ try {
8358
+ return await provider(context);
8359
+ } catch (error) {
8360
+ try {
8361
+ providerErrorHandler?.(error);
8362
+ } catch {
8363
+ }
8364
+ return void 0;
8365
+ }
8366
+ }
8367
+ function buildSandboxOpenEnv(credential) {
8368
+ return `export ${SANDBOX_OPEN_GATEWAY_URL_ENV}=${shellSingleQuote(credential.gatewayUrl)} && export ${SANDBOX_OPEN_TOKEN_ENV}=${shellSingleQuote(credential.token)}`;
8369
+ }
8370
+ function shellSingleQuote(value) {
8371
+ return `'${value.replace(/'/g, "'\\''")}'`;
8372
+ }
8373
+ function clearSandboxOpenCredentialProviderForTests() {
8374
+ provider = void 0;
8375
+ providerErrorHandler = void 0;
8376
+ }
8377
+
8378
+ // src/tool_lattice/shell_exec/index.ts
8184
8379
  var SHELL_EXEC_DESCRIPTION = `Execute a shell command in the sandbox environment.
8185
8380
 
8186
8381
  Use this to run CLI tools, install packages, inspect files, or perform any operation that requires a shell.
@@ -8213,8 +8408,19 @@ var createShellExecTool = ({
8213
8408
  projectId: runConfig.projectId,
8214
8409
  vmIsolation
8215
8410
  });
8411
+ const tenantId2 = runConfig.tenantId;
8412
+ const credential = tenantId2 ? await getSandboxOpenCredential({
8413
+ tenantId: tenantId2,
8414
+ workspaceId: runConfig.workspaceId,
8415
+ projectId: runConfig.projectId,
8416
+ sandboxName: sandbox.name,
8417
+ assistantId: runConfig.assistant_id || void 0,
8418
+ threadId: runConfig.thread_id || void 0,
8419
+ depth: runConfig.depth
8420
+ }) : void 0;
8421
+ const command = credential ? `${buildSandboxOpenEnv(credential)} && ${input.command}` : input.command;
8216
8422
  const executeResult = await sandbox.shell.execCommand({
8217
- command: input.command,
8423
+ command,
8218
8424
  exec_dir: input.exec_dir,
8219
8425
  timeout: input.timeout ?? 300
8220
8426
  });
@@ -12952,13 +13158,23 @@ var SandboxPluginExecutor = class {
12952
13158
  validateRelativePath(args.handler, "handler");
12953
13159
  const pluginDir = `${PLUGINS_ROOT}/${args.manifest.type}`;
12954
13160
  const handlerAbs = `${pluginDir}/${args.handler}`;
13161
+ const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
12955
13162
  const sandbox = await this.deps.resolveSandbox({
12956
- tenantId: args.runConfig?.tenantId ?? args.context.tenantId,
13163
+ tenantId: tenantId2,
12957
13164
  assistantId: args.runConfig?.assistantId,
12958
13165
  threadId: args.runConfig?.threadId,
12959
13166
  workspaceId: args.runConfig?.workspaceId,
12960
13167
  projectId: args.runConfig?.projectId
12961
13168
  });
13169
+ const openCredential = tenantId2 ? await getSandboxOpenCredential({
13170
+ tenantId: tenantId2,
13171
+ workspaceId: args.runConfig?.workspaceId,
13172
+ projectId: args.runConfig?.projectId,
13173
+ sandboxName: sandbox.name,
13174
+ assistantId: args.runConfig?.assistantId,
13175
+ threadId: args.runConfig?.threadId,
13176
+ depth: args.runConfig?.depth
13177
+ }) : void 0;
12962
13178
  const timeoutMs = clamp(args.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
12963
13179
  const cap = clamp(
12964
13180
  args.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES,
@@ -12989,6 +13205,12 @@ var SandboxPluginExecutor = class {
12989
13205
  envParts.push(`PLUGIN_CONNECTION_FILE=${quotePosixShellArg(connectionFile)}`);
12990
13206
  }
12991
13207
  envParts.push(`PLUGIN_CONTEXT=${quotePosixShellArg(encode(args.context))}`);
13208
+ if (openCredential) {
13209
+ envParts.push(
13210
+ `${SANDBOX_OPEN_GATEWAY_URL_ENV}=${quotePosixShellArg(openCredential.gatewayUrl)}`
13211
+ );
13212
+ envParts.push(`${SANDBOX_OPEN_TOKEN_ENV}=${quotePosixShellArg(openCredential.token)}`);
13213
+ }
12992
13214
  const command = `${envParts.join(" ")} node ${quotePosixShellArg(handlerAbs)}`;
12993
13215
  const execResult = await sandbox.shell.execCommand({
12994
13216
  command,
@@ -13940,10 +14162,10 @@ var TenantPluginCatalog = class {
13940
14162
  candidates.push({ type, dirPath: dir.path, raw, manifestJson, files: dirFiles });
13941
14163
  }
13942
14164
  const hash = createHash3("sha256").update(hashParts.sort().join("\n")).digest("hex").slice(0, 16);
13943
- const cached = this.cache.get(tenantId2)?.value;
13944
- if (cached && cached.hash === hash) {
13945
- this.cache.set(tenantId2, { value: cached, checkedAt: this.now() });
13946
- return cached;
14165
+ const cached2 = this.cache.get(tenantId2)?.value;
14166
+ if (cached2 && cached2.hash === hash) {
14167
+ this.cache.set(tenantId2, { value: cached2, checkedAt: this.now() });
14168
+ return cached2;
13947
14169
  }
13948
14170
  const plugins = [];
13949
14171
  const manifests = {};
@@ -15304,7 +15526,7 @@ var AgentWorkerState = Annotation.Root({
15304
15526
  default: () => ({})
15305
15527
  }),
15306
15528
  command2: Annotation({
15307
- reducer: (x, y) => y ?? x,
15529
+ reducer: (_x, y) => y,
15308
15530
  default: () => void 0
15309
15531
  }),
15310
15532
  // RunConfig for passing configuration through the graph
@@ -15321,12 +15543,12 @@ var AgentWorkerState = Annotation.Root({
15321
15543
  }),
15322
15544
  // Execution result
15323
15545
  result: Annotation({
15324
- reducer: (x, y) => y ?? x,
15546
+ reducer: (_x, y) => y,
15325
15547
  default: () => void 0
15326
15548
  }),
15327
15549
  // Interrupt data
15328
15550
  interruptValue: Annotation({
15329
- reducer: (x, y) => y ?? x,
15551
+ reducer: (_x, y) => y,
15330
15552
  default: () => void 0
15331
15553
  }),
15332
15554
  // Final state to return
@@ -17016,6 +17238,71 @@ var CapabilityRuntimeResolver = class {
17016
17238
  // src/services/TrustedAgentDispatchCapability.ts
17017
17239
  var TRUSTED_AGENT_DISPATCH = Symbol("axiom.trustedAgentDispatch");
17018
17240
 
17241
+ // src/services/threadRunLease.ts
17242
+ import { randomUUID as randomUUID6 } from "crypto";
17243
+ function threadRunLeaseKey(tenantId2, assistantId, threadId) {
17244
+ return `thread-run:${tenantId2}:${assistantId}:${threadId}`;
17245
+ }
17246
+ async function tryAcquireThreadRun(input) {
17247
+ const key5 = threadRunLeaseKey(input.tenantId, input.assistantId, input.threadId);
17248
+ const holder = `${input.owner}#${randomUUID6()}`;
17249
+ if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
17250
+ const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
17251
+ let released = false;
17252
+ let lost = false;
17253
+ const heartbeat = setInterval(() => {
17254
+ void input.store.renew(key5, holder, input.ttlMs).then((renewed) => {
17255
+ if (released || renewed || lost) return;
17256
+ lost = true;
17257
+ try {
17258
+ input.onLost?.();
17259
+ } catch {
17260
+ }
17261
+ }).catch(() => void 0);
17262
+ }, heartbeatMs);
17263
+ heartbeat.unref?.();
17264
+ return {
17265
+ get lost() {
17266
+ return lost;
17267
+ },
17268
+ release: async () => {
17269
+ if (released) return;
17270
+ released = true;
17271
+ clearInterval(heartbeat);
17272
+ await input.store.release(key5, holder).catch(() => void 0);
17273
+ }
17274
+ };
17275
+ }
17276
+ async function getThreadRunOwner(input) {
17277
+ let store = input.store;
17278
+ if (!store) {
17279
+ try {
17280
+ store = storeLatticeManager.getStoreLattice("default", "lease").store;
17281
+ } catch {
17282
+ return null;
17283
+ }
17284
+ }
17285
+ try {
17286
+ const record = await store.get(threadRunLeaseKey(input.tenantId, input.assistantId, input.threadId));
17287
+ if (!record) return null;
17288
+ const hash = record.holder.lastIndexOf("#");
17289
+ return hash > 0 ? record.holder.slice(0, hash) : record.holder;
17290
+ } catch {
17291
+ return null;
17292
+ }
17293
+ }
17294
+
17295
+ // src/services/replicaIdentity.ts
17296
+ import os from "os";
17297
+ var cached;
17298
+ function getReplicaId() {
17299
+ cached ?? (cached = process.env.AXIOM_REPLICA_ID?.trim() || `${os.hostname()}:${process.pid}`);
17300
+ return cached;
17301
+ }
17302
+ function resetReplicaIdForTests() {
17303
+ cached = void 0;
17304
+ }
17305
+
17019
17306
  // src/services/Agent.ts
17020
17307
  var ThreadStatus2 = /* @__PURE__ */ ((ThreadStatus3) => {
17021
17308
  ThreadStatus3["IDLE"] = "idle";
@@ -17333,6 +17620,10 @@ function checkpointCapabilityRevision(state) {
17333
17620
  var QUEUE_PROCESSOR_RETRY_BASE_MS = 100;
17334
17621
  var QUEUE_PROCESSOR_MAX_RETRIES = 3;
17335
17622
  var AGENT_RECURSION_LIMIT = 1e3;
17623
+ var THREAD_RUN_LEASE_TTL_MS = 6e4;
17624
+ var THREAD_RUN_LEASE_RETRY_BASE_MS = 150;
17625
+ var THREAD_RUN_LEASE_RETRY_CAP_MS = 1500;
17626
+ var THREAD_RUN_LEASE_MAX_RETRIES = 12;
17336
17627
  function hasExactKeys(record, required, optional = []) {
17337
17628
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
17338
17629
  return required.every((key5) => Object.prototype.hasOwnProperty.call(record, key5)) && Reflect.ownKeys(record).every((key5) => typeof key5 === "string" && allowed.has(key5));
@@ -17362,6 +17653,13 @@ var Agent = class {
17362
17653
  this.queueProcessorRetryAttempt = 0;
17363
17654
  this.queueProcessorGeneration = 0;
17364
17655
  this.queueWakeRequested = false;
17656
+ /**
17657
+ * Thread-run ownership lease held while this replica is actively processing
17658
+ * the thread. Acquired non-blockingly in {@link startQueueProcessor}; released
17659
+ * exactly once when the processor definitively stops.
17660
+ */
17661
+ this.threadRunLease = null;
17662
+ this.threadRunLeaseRetryAttempt = 0;
17365
17663
  this.asyncTasks = [];
17366
17664
  this.agentExecutor = async ({ input, command, custom_run_config, expected_capability_revision, trusted_run_context }, signal) => {
17367
17665
  const { runnable_agent, runConfig } = await this.getLatticeClientAndRuntimeConfig(
@@ -17478,7 +17776,7 @@ var Agent = class {
17478
17776
  }
17479
17777
  };
17480
17778
  this.waitingForQueueEnd = async (signal) => {
17481
- while (!signal?.aborted) {
17779
+ while (!signal?.aborted && !this.threadRunLease?.lost) {
17482
17780
  const pendings = await this.getPendingMessages();
17483
17781
  if (pendings.length === 0) {
17484
17782
  const state = await this.getCurrentState();
@@ -18309,6 +18607,29 @@ var Agent = class {
18309
18607
  }
18310
18608
  return this.queueStore;
18311
18609
  }
18610
+ /**
18611
+ * Resolves the shared lease store used to serialize thread runs across
18612
+ * Gateway replicas. Returns `undefined` when no lease store is registered so
18613
+ * single-process deployments keep running unconditionally.
18614
+ */
18615
+ getLeaseStore() {
18616
+ try {
18617
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
18618
+ } catch {
18619
+ return void 0;
18620
+ }
18621
+ }
18622
+ /**
18623
+ * Releases this replica's thread-run ownership lease, if held.
18624
+ *
18625
+ * @remarks Safe to call from any definitive-stop path; a second call is a
18626
+ * no-op. The handle's unique holder makes a stale release harmless.
18627
+ */
18628
+ async releaseThreadRunLease() {
18629
+ const lease = this.threadRunLease;
18630
+ this.threadRunLease = null;
18631
+ if (lease) await lease.release();
18632
+ }
18312
18633
  getDefaultQueueConfig() {
18313
18634
  return {
18314
18635
  mode: "collect" /* COLLECT */,
@@ -18334,6 +18655,8 @@ var Agent = class {
18334
18655
  */
18335
18656
  stopQueueProcessor() {
18336
18657
  this.cancelQueueProcessorRetry();
18658
+ this.cancelThreadRunLeaseRetry();
18659
+ void this.releaseThreadRunLease();
18337
18660
  this.clearRecoveryBlock();
18338
18661
  this.queueProcessorGeneration += 1;
18339
18662
  if (this.abortController) {
@@ -18481,16 +18804,50 @@ var Agent = class {
18481
18804
  startQueueProcessorIfNeeded() {
18482
18805
  return this.startQueueProcessor(true);
18483
18806
  }
18484
- startQueueProcessor(freshWakeup) {
18807
+ async startQueueProcessor(freshWakeup) {
18485
18808
  this.queueWakeRequested = true;
18486
18809
  if (this.queueProcessorPromise) return this.queueProcessorPromise;
18487
- if (this.queueProcessorRetryTimer) return Promise.resolve();
18810
+ if (this.queueProcessorRetryTimer) return;
18488
18811
  if (freshWakeup && this.queueProcessorRetryAttempt >= QUEUE_PROCESSOR_MAX_RETRIES) {
18489
18812
  this.queueProcessorRetryAttempt = 0;
18490
18813
  }
18814
+ if (freshWakeup && this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) {
18815
+ this.threadRunLeaseRetryAttempt = 0;
18816
+ }
18491
18817
  this.queueWakeRequested = false;
18492
- let processorFailed = false;
18493
18818
  const processorGeneration = this.queueProcessorGeneration;
18819
+ if (this.threadRunLease?.lost) await this.releaseThreadRunLease();
18820
+ let acquiredLeaseHere = false;
18821
+ if (this.threadRunLease === null) {
18822
+ const leaseStore = this.getLeaseStore();
18823
+ if (leaseStore) {
18824
+ const lease = await tryAcquireThreadRun({
18825
+ store: leaseStore,
18826
+ tenantId: this.tenant_id,
18827
+ assistantId: this.assistant_id,
18828
+ threadId: this.thread_id,
18829
+ owner: getReplicaId(),
18830
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
18831
+ onLost: () => {
18832
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
18833
+ this.abortController?.abort();
18834
+ }
18835
+ });
18836
+ if (!lease) {
18837
+ this.scheduleThreadRunLeaseRetry();
18838
+ return;
18839
+ }
18840
+ this.threadRunLease = lease;
18841
+ acquiredLeaseHere = true;
18842
+ this.cancelThreadRunLeaseRetry();
18843
+ }
18844
+ }
18845
+ if (this.queueProcessorPromise) return this.queueProcessorPromise;
18846
+ if (this.queueProcessorGeneration !== processorGeneration) {
18847
+ if (acquiredLeaseHere) await this.releaseThreadRunLease();
18848
+ return;
18849
+ }
18850
+ let processorFailed = false;
18494
18851
  const processor = this.runQueueProcessor().catch(async (error) => {
18495
18852
  processorFailed = true;
18496
18853
  console.error(`Queue processing error for thread ${this.thread_id}:`, error);
@@ -18508,7 +18865,11 @@ var Agent = class {
18508
18865
  if (this.queueWakeRequested) void this.startQueueProcessor(false);
18509
18866
  return;
18510
18867
  }
18511
- if (failed) return;
18868
+ if (failed) {
18869
+ this.cancelThreadRunLeaseRetry();
18870
+ await this.releaseThreadRunLease();
18871
+ return;
18872
+ }
18512
18873
  this.queueProcessorRetryAttempt = 0;
18513
18874
  if (this.queueWakeRequested) {
18514
18875
  void this.startQueueProcessor(false);
@@ -18521,7 +18882,11 @@ var Agent = class {
18521
18882
  void this.startQueueProcessor(false);
18522
18883
  return;
18523
18884
  }
18524
- if (pendingMessages.length === 0) return;
18885
+ if (pendingMessages.length === 0) {
18886
+ this.cancelThreadRunLeaseRetry();
18887
+ await this.releaseThreadRunLease();
18888
+ return;
18889
+ }
18525
18890
  const processingMessages = await this.getQueueStore().getProcessingMessages(
18526
18891
  this.thread_id,
18527
18892
  this.queueScope()
@@ -18533,12 +18898,20 @@ var Agent = class {
18533
18898
  }
18534
18899
  if (processingMessages.length > 0) {
18535
18900
  const runStatus = await this.getRunStatus();
18536
- if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) return;
18901
+ if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) {
18902
+ this.cancelThreadRunLeaseRetry();
18903
+ await this.releaseThreadRunLease();
18904
+ return;
18905
+ }
18537
18906
  }
18538
18907
  void this.startQueueProcessor(false);
18539
18908
  } catch (error) {
18540
18909
  console.error(`Queue processor finalization failed for thread ${this.thread_id}:`, error);
18541
18910
  await this.scheduleQueueProcessorRetry(error, generation);
18911
+ if (!this.queueProcessorPromise) {
18912
+ await this.releaseThreadRunLease();
18913
+ this.cancelThreadRunLeaseRetry();
18914
+ }
18542
18915
  }
18543
18916
  }
18544
18917
  async scheduleQueueProcessorRetry(error, generation) {
@@ -18576,6 +18949,36 @@ var Agent = class {
18576
18949
  this.queueProcessorRetryAttempt = 0;
18577
18950
  this.queueWakeRequested = false;
18578
18951
  }
18952
+ /**
18953
+ * Bounded re-attempt after losing the thread-run lease to a peer. Covers the
18954
+ * narrow window where the peer releases the lease without having observed a
18955
+ * just-enqueued message. Bounded so a genuinely long-held thread does not make
18956
+ * this replica poll forever — the owning replica's loop drains new messages.
18957
+ */
18958
+ scheduleThreadRunLeaseRetry() {
18959
+ if (this.threadRunLeaseRetryTimer || this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) return;
18960
+ const delay = Math.min(
18961
+ THREAD_RUN_LEASE_RETRY_BASE_MS * 2 ** this.threadRunLeaseRetryAttempt,
18962
+ THREAD_RUN_LEASE_RETRY_CAP_MS
18963
+ );
18964
+ this.threadRunLeaseRetryAttempt += 1;
18965
+ this.threadRunLeaseRetryTimer = setTimeout(() => {
18966
+ this.threadRunLeaseRetryTimer = void 0;
18967
+ void this.startQueueProcessor(false);
18968
+ }, delay);
18969
+ }
18970
+ /**
18971
+ * Clears any pending thread-run lease retry and resets its backoff budget.
18972
+ *
18973
+ * @remarks Called on lease acquisition and on every definitive processor
18974
+ * stop so the next enqueue starts from the base delay. Never called on
18975
+ * continue/re-arm paths, which must preserve the bounded budget.
18976
+ */
18977
+ cancelThreadRunLeaseRetry() {
18978
+ if (this.threadRunLeaseRetryTimer) clearTimeout(this.threadRunLeaseRetryTimer);
18979
+ this.threadRunLeaseRetryTimer = void 0;
18980
+ this.threadRunLeaseRetryAttempt = 0;
18981
+ }
18579
18982
  async runQueueProcessor() {
18580
18983
  const store = this.getQueueStore();
18581
18984
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
@@ -18720,6 +19123,38 @@ var Agent = class {
18720
19123
  console.error(`[Agent] Failed to initialize agent lattice for ${this.assistant_id} (tenant: ${this.tenant_id}):`, error);
18721
19124
  throw error;
18722
19125
  }
19126
+ let acquiredLeaseHere = false;
19127
+ if (this.threadRunLease === null) {
19128
+ const leaseStore = this.getLeaseStore();
19129
+ if (leaseStore) {
19130
+ const lease = await tryAcquireThreadRun({
19131
+ store: leaseStore,
19132
+ tenantId: this.tenant_id,
19133
+ assistantId: this.assistant_id,
19134
+ threadId: this.thread_id,
19135
+ owner: getReplicaId(),
19136
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
19137
+ onLost: () => {
19138
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
19139
+ this.abortController?.abort();
19140
+ }
19141
+ });
19142
+ if (!lease) {
19143
+ return { status: await this.getRunStatus(), settled: false, leaseContended: true };
19144
+ }
19145
+ this.threadRunLease = lease;
19146
+ acquiredLeaseHere = true;
19147
+ this.cancelThreadRunLeaseRetry();
19148
+ }
19149
+ }
19150
+ try {
19151
+ return await this.resumeTaskLocked();
19152
+ } finally {
19153
+ if (acquiredLeaseHere && !this.queueProcessorPromise) await this.releaseThreadRunLease();
19154
+ }
19155
+ }
19156
+ /** Recovery body of {@link resumeTask}, executed while thread ownership is settled. */
19157
+ async resumeTaskLocked() {
18723
19158
  const store = this.getQueueStore();
18724
19159
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
18725
19160
  const preRunState = await this.getCurrentState();
@@ -18835,6 +19270,8 @@ var Agent = class {
18835
19270
  */
18836
19271
  async abort() {
18837
19272
  this.cancelQueueProcessorRetry();
19273
+ this.cancelThreadRunLeaseRetry();
19274
+ await this.releaseThreadRunLease();
18838
19275
  this.clearRecoveryBlock();
18839
19276
  this.queueProcessorGeneration += 1;
18840
19277
  if (this.abortController) {
@@ -19494,27 +19931,27 @@ function createCheckAsyncTaskTool() {
19494
19931
  if (!mainAgent) {
19495
19932
  return "Cannot access main agent state.";
19496
19933
  }
19497
- const cached = mainAgent.getAsyncTask(task_id);
19498
- if (!cached) {
19934
+ const cached2 = mainAgent.getAsyncTask(task_id);
19935
+ if (!cached2) {
19499
19936
  return `Task ${task_id} not found. Use list_async_tasks to see all tasks.`;
19500
19937
  }
19501
19938
  try {
19502
19939
  if (!agentInstanceManager.hasAgent({
19503
- assistant_id: cached.assistantId,
19940
+ assistant_id: cached2.assistantId,
19504
19941
  thread_id: task_id,
19505
19942
  tenant_id: mainAgent.tenant_id
19506
19943
  })) {
19507
- return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached.status}`;
19944
+ return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached2.status}`;
19508
19945
  }
19509
19946
  const subAgent = agentInstanceManager.getAgent({
19510
- assistant_id: cached.assistantId,
19947
+ assistant_id: cached2.assistantId,
19511
19948
  thread_id: task_id,
19512
19949
  tenant_id: mainAgent.tenant_id
19513
19950
  });
19514
19951
  const status = await subAgent.getRunStatus();
19515
19952
  if (status === "busy") {
19516
19953
  return `Task ${task_id}: still running.
19517
- Description: ${cached.description}`;
19954
+ Description: ${cached2.description}`;
19518
19955
  }
19519
19956
  if (status === "idle") {
19520
19957
  const state = await subAgent.getCurrentState();
@@ -19525,21 +19962,21 @@ Description: ${cached.description}`;
19525
19962
  mainAgent.updateAsyncTaskStatus(task_id, "completed");
19526
19963
  const result = lastAI?.content ?? "(completed, no output)";
19527
19964
  return `Task ${task_id}: COMPLETED.
19528
- Description: ${cached.description}
19965
+ Description: ${cached2.description}
19529
19966
  Result:
19530
19967
  ${result}`;
19531
19968
  }
19532
19969
  if (status === "error") {
19533
19970
  mainAgent.updateAsyncTaskStatus(task_id, "failed");
19534
19971
  return `Task ${task_id}: failed.
19535
- Description: ${cached.description}`;
19972
+ Description: ${cached2.description}`;
19536
19973
  }
19537
19974
  if (status === "interrupted") {
19538
19975
  return `Task ${task_id}: interrupted (waiting for input).
19539
- Description: ${cached.description}`;
19976
+ Description: ${cached2.description}`;
19540
19977
  }
19541
19978
  return `Task ${task_id}: ${status}.
19542
- Description: ${cached.description}`;
19979
+ Description: ${cached2.description}`;
19543
19980
  } catch (err) {
19544
19981
  return `Task ${task_id}: error checking status: ${err instanceof Error ? err.message : String(err)}`;
19545
19982
  }
@@ -19612,25 +20049,25 @@ function createCancelAsyncTaskTool() {
19612
20049
  if (!mainAgent) {
19613
20050
  return "Cannot access main agent state.";
19614
20051
  }
19615
- const cached = mainAgent.getAsyncTask(task_id);
19616
- if (!cached) {
20052
+ const cached2 = mainAgent.getAsyncTask(task_id);
20053
+ if (!cached2) {
19617
20054
  return `Task ${task_id} not found.`;
19618
20055
  }
19619
20056
  try {
19620
20057
  const subAgentExists = agentInstanceManager.hasAgent({
19621
- assistant_id: cached.assistantId,
20058
+ assistant_id: cached2.assistantId,
19622
20059
  thread_id: task_id,
19623
20060
  tenant_id: mainAgent.tenant_id
19624
20061
  });
19625
20062
  if (subAgentExists) {
19626
20063
  const subAgent = agentInstanceManager.getAgent({
19627
- assistant_id: cached.assistantId,
20064
+ assistant_id: cached2.assistantId,
19628
20065
  thread_id: task_id,
19629
20066
  tenant_id: mainAgent.tenant_id
19630
20067
  });
19631
20068
  await subAgent.abort();
19632
20069
  agentInstanceManager.removeAgent({
19633
- assistant_id: cached.assistantId,
20070
+ assistant_id: cached2.assistantId,
19634
20071
  thread_id: task_id,
19635
20072
  tenant_id: mainAgent.tenant_id
19636
20073
  });
@@ -19879,6 +20316,48 @@ var MemoryScheduleStorage = class {
19879
20316
  });
19880
20317
  }
19881
20318
  }
20319
+ /**
20320
+ * Atomically claims one due fire by transitioning the task `pending -> running`.
20321
+ *
20322
+ * @param taskId Task to claim.
20323
+ * @returns True when this caller won the claim (task was `pending`); false if
20324
+ * another replica already claimed it or it is not pending.
20325
+ */
20326
+ async claimScheduledRun(taskId) {
20327
+ const task = this.tasks.get(taskId);
20328
+ if (!task || task.status !== ScheduledTaskStatus.PENDING) {
20329
+ return false;
20330
+ }
20331
+ this.tasks.set(taskId, {
20332
+ ...task,
20333
+ status: ScheduledTaskStatus.RUNNING,
20334
+ updatedAt: Date.now()
20335
+ });
20336
+ return true;
20337
+ }
20338
+ /**
20339
+ * Requeues `running` tasks whose `updatedAt` is older than `olderThanMs` back
20340
+ * to `pending` (recovery after a crash that left a claim unsettled).
20341
+ *
20342
+ * @param olderThanMs Age threshold in milliseconds.
20343
+ * @returns The number of tasks requeued.
20344
+ */
20345
+ async requeueStaleRunning(olderThanMs) {
20346
+ const now = Date.now();
20347
+ const cutoff = now - olderThanMs;
20348
+ let requeued = 0;
20349
+ for (const [taskId, task] of this.tasks.entries()) {
20350
+ if (task.status === ScheduledTaskStatus.RUNNING && task.updatedAt < cutoff) {
20351
+ this.tasks.set(taskId, {
20352
+ ...task,
20353
+ status: ScheduledTaskStatus.PENDING,
20354
+ updatedAt: now
20355
+ });
20356
+ requeued++;
20357
+ }
20358
+ }
20359
+ return requeued;
20360
+ }
19882
20361
  /**
19883
20362
  * Delete task
19884
20363
  */
@@ -20243,6 +20722,7 @@ function describeCronExpression(expression) {
20243
20722
  }
20244
20723
 
20245
20724
  // src/schedule_lattice/DefaultScheduleClient.ts
20725
+ var STALE_RUNNING_MS = 30 * 6e4;
20246
20726
  var _DefaultScheduleClient = class _DefaultScheduleClient {
20247
20727
  constructor(storage) {
20248
20728
  this.handlers = /* @__PURE__ */ new Map();
@@ -20483,6 +20963,19 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20483
20963
  }
20484
20964
  // ===== Recovery =====
20485
20965
  async restore() {
20966
+ try {
20967
+ const requeued = await this.storage.requeueStaleRunning(STALE_RUNNING_MS);
20968
+ if (requeued > 0) {
20969
+ console.log(
20970
+ `[Scheduler] Requeued ${requeued} stale running task(s)`
20971
+ );
20972
+ }
20973
+ } catch (error) {
20974
+ console.error(
20975
+ "[Scheduler] Failed to requeue stale running tasks:",
20976
+ error
20977
+ );
20978
+ }
20486
20979
  const activeTasks = await this.storage.getActiveTasks();
20487
20980
  let restored = 0;
20488
20981
  for (const task of activeTasks) {
@@ -20543,6 +21036,16 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20543
21036
  });
20544
21037
  }
20545
21038
  async executeTask(task) {
21039
+ let claimed = false;
21040
+ try {
21041
+ claimed = await this.storage.claimScheduledRun(task.taskId);
21042
+ } catch (error) {
21043
+ console.error(`[Scheduler] Failed to claim task ${task.taskId}:`, error);
21044
+ return;
21045
+ }
21046
+ if (!claimed) {
21047
+ return;
21048
+ }
20546
21049
  const handler = this.handlers.get(task.taskType);
20547
21050
  if (!handler) {
20548
21051
  console.error(
@@ -20554,9 +21057,6 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20554
21057
  });
20555
21058
  return;
20556
21059
  }
20557
- await this.storage.update(task.taskId, {
20558
- status: ScheduledTaskStatus2.RUNNING
20559
- });
20560
21060
  console.log(`[Scheduler] Executing task: ${task.taskId}`);
20561
21061
  try {
20562
21062
  const latestTask = await this.storage.get(task.taskId);
@@ -25270,8 +25770,8 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
25270
25770
  * @param key Provider key name
25271
25771
  * @param provider VectorStoreProvider implementation
25272
25772
  */
25273
- registerProvider(key5, provider) {
25274
- const lattice = { key: key5, provider };
25773
+ registerProvider(key5, provider2) {
25774
+ const lattice = { key: key5, provider: provider2 };
25275
25775
  this.registerWithTenant("default", key5, lattice);
25276
25776
  }
25277
25777
  /**
@@ -25307,7 +25807,7 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
25307
25807
  };
25308
25808
  var vectorStoreProviderManager = VectorStoreProviderManager.getInstance();
25309
25809
  vectorStoreProviderManager.registerProvider("default", new MemoryVectorStoreProvider());
25310
- var registerVectorStoreProvider = (key5, provider) => vectorStoreProviderManager.registerProvider(key5, provider);
25810
+ var registerVectorStoreProvider = (key5, provider2) => vectorStoreProviderManager.registerProvider(key5, provider2);
25311
25811
  var getVectorStoreProvider = (key5) => vectorStoreProviderManager.getProvider(key5);
25312
25812
 
25313
25813
  // src/store_lattice/configureStores.ts
@@ -28009,7 +28509,110 @@ independently before the workflow's integration eval.
28009
28509
 
28010
28510
  An unsupported architecture conversion interrupts the flow and requests a new
28011
28511
  user decision. Do not route backward to the Charter or to [[agent-build]], and do
28012
- not create a replacement.`
28512
+ not create a replacement.`,
28513
+ "ptc": `---
28514
+ name: ptc
28515
+ description: Programmatic Tool Calling (PTC) \u2014 write sandbox code that discovers and calls platform Open capabilities (MCP tools) through the gateway. Use when orchestration is multi-step, data-heavy, or needs loops/branches across platform tools instead of one-at-a-time model tool calls.
28516
+ metadata:
28517
+ role: workflow
28518
+ domain: orchestration
28519
+ verified: unverified
28520
+ ---
28521
+
28522
+ # Programmatic Tool Calling (PTC)
28523
+
28524
+ PTC means the model writes and runs code that calls platform tools itself, instead
28525
+ of calling tools one at a time. Reach for it when orchestration is multi-step,
28526
+ data-heavy, or needs a loop/branch that would be wasteful as a chain of model tool
28527
+ calls.
28528
+
28529
+ ## Where the code runs
28530
+
28531
+ Run the code with \`code_eval\` (backed by \`shell_exec\`). Each execution process is
28532
+ injected with two environment variables:
28533
+
28534
+ - \`API_GATEWAY_URL\` \u2014 the gateway base URL.
28535
+ - \`OPEN_API_TOKEN\` \u2014 a short-lived bearer token **self-bound to this agent**. It is
28536
+ not reusable elsewhere.
28537
+
28538
+ The token already encodes this agent's authority: the platform reflects the agent's
28539
+ own configured middleware into Open grants, plus a small always-on baseline
28540
+ (\`kb\` read-only and \`sandbox:self\`). So the callable set = that baseline \u222A the
28541
+ domains your configured middleware exposes \u2014 and nothing beyond it.
28542
+
28543
+ ## Client
28544
+
28545
+ The sandbox must have the zero-dependency \`@axiom-lattice/open-mcp-client\` package.
28546
+ Get it one of two ways:
28547
+
28548
+ - **Preinstalled in the sandbox image** (recommended): \`open-mcp\` is on \`PATH\`; for
28549
+ \`require("@axiom-lattice/open-mcp-client")\` to resolve, install it into the working
28550
+ directory or point \`NODE_PATH\` at the global \`node_modules\`.
28551
+ - **Install it in this run** when the sandbox has network + npm:
28552
+
28553
+ \`\`\`bash
28554
+ npm install @axiom-lattice/open-mcp-client
28555
+ # or use without installing:
28556
+ npx @axiom-lattice/open-mcp-client tools list --json
28557
+ \`\`\`
28558
+
28559
+ \`\`\`js
28560
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28561
+ const client = OpenMcpClient.fromEnv(); // reads API_GATEWAY_URL + OPEN_API_TOKEN
28562
+ \`\`\`
28563
+
28564
+ CLI equivalents (config comes from the same env vars):
28565
+
28566
+ \`\`\`bash
28567
+ open-mcp tools list --json
28568
+ open-mcp tools call kb_search --json '{"query":"refund policy"}'
28569
+ \`\`\`
28570
+
28571
+ ## Discover, then call
28572
+
28573
+ 1. **Discover first.** Call \`await client.listTools()\`. This returns exactly the
28574
+ tools this agent is authorized to call (the server filters by the token's grants
28575
+ on every request, so it is the source of truth).
28576
+ 2. **Only call discovered tools.** Pick a tool from that list and call
28577
+ \`await client.callTool(name, args)\` with args matching the tool's \`inputSchema\`.
28578
+ 3. **Print the final result as JSON to stdout** so the calling agent can consume it.
28579
+ Keep intermediate logs minimal.
28580
+
28581
+ An unconfigured domain isn't registered, so calling it returns an \`isError\`
28582
+ tool-not-found result; a registered tool refused by the token's grants surfaces
28583
+ \`GRANT_DENIED\`. Either way the capability is **not configured for this agent**.
28584
+ Do not retry or work around it \u2014 drop that step or tell the user which
28585
+ middleware/domain must be enabled. A denied call is expected behavior, not a
28586
+ transient failure.
28587
+
28588
+ ## Tips
28589
+
28590
+ - Fan out with \`Promise.all\` over many items instead of many sequential model turns;
28591
+ that is the main win of PTC.
28592
+ - Inspect \`tool.inputSchema\` before calling to avoid schema-validation errors.
28593
+ - Reduce large results in code and return only the fields the agent needs.
28594
+ - The token is short-lived; for very long jobs re-read the env per \`code_eval\` run
28595
+ rather than caching the token in a file.
28596
+
28597
+ ## Worked example
28598
+
28599
+ \`\`\`js
28600
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28601
+
28602
+ async function main() {
28603
+ const client = OpenMcpClient.fromEnv(); // API_GATEWAY_URL + OPEN_API_TOKEN
28604
+ const tools = await client.listTools(); // exactly what this agent may call
28605
+ const pick = tools.find((t) => t.name === "kb_search") ?? tools[0];
28606
+ const out = await client.callTool(pick.name, { query: "refund policy" });
28607
+ console.log(JSON.stringify({ called: pick.name, result: out.json() }));
28608
+ }
28609
+
28610
+ main().catch((err) => { console.error(String(err)); process.exit(1); });
28611
+ \`\`\`
28612
+
28613
+ Run this in \`code_eval\`. The first run proves env + client wiring; then extend it
28614
+ into the real fan-out/aggregation the task needs.
28615
+ `
28013
28616
  };
28014
28617
  function getBuiltInSkillMeta(name) {
28015
28618
  const content = BUILTIN_SKILLS[name];
@@ -28606,7 +29209,7 @@ ${body}` : `${frontmatter}
28606
29209
  };
28607
29210
 
28608
29211
  // src/store_lattice/InMemoryMenuStore.ts
28609
- import { randomUUID as randomUUID6 } from "crypto";
29212
+ import { randomUUID as randomUUID7 } from "crypto";
28610
29213
  var InMemoryMenuStore = class {
28611
29214
  constructor() {
28612
29215
  this.items = /* @__PURE__ */ new Map();
@@ -28627,7 +29230,7 @@ var InMemoryMenuStore = class {
28627
29230
  async create(input) {
28628
29231
  const now = /* @__PURE__ */ new Date();
28629
29232
  const item = {
28630
- id: randomUUID6(),
29233
+ id: randomUUID7(),
28631
29234
  tenantId: input.tenantId,
28632
29235
  menuTarget: input.menuTarget,
28633
29236
  group: input.group,
@@ -31139,9 +31742,9 @@ async function getOrCreateCollectionVectorStore(name, embeddingKey, tenantId2, p
31139
31742
  const pendingInitialization = pendingVectorStoreInitializations.get(storeKey);
31140
31743
  if (pendingInitialization) return pendingInitialization;
31141
31744
  const initialization = (async () => {
31142
- const provider = vectorStoreProviderManager.getProvider(providerKey);
31745
+ const provider2 = vectorStoreProviderManager.getProvider(providerKey);
31143
31746
  const tableName = buildTableName(tenantId2, name);
31144
- const vectorStore = await provider.create({ tableName, embeddingKey });
31747
+ const vectorStore = await provider2.create({ tableName, embeddingKey });
31145
31748
  vectorStoreLatticeManager.registerLattice(storeKey, vectorStore);
31146
31749
  return vectorStore;
31147
31750
  })();
@@ -31159,9 +31762,9 @@ async function getCollectionEntryCount(name, embeddingKey, tenantId2) {
31159
31762
  return vs.getDocumentCount();
31160
31763
  }
31161
31764
  try {
31162
- const provider = vectorStoreProviderManager.getProvider("default");
31163
- if (provider.getEntryCount) {
31164
- return provider.getEntryCount(buildTableName(tenantId2, name));
31765
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31766
+ if (provider2.getEntryCount) {
31767
+ return provider2.getEntryCount(buildTableName(tenantId2, name));
31165
31768
  }
31166
31769
  } catch {
31167
31770
  }
@@ -31173,9 +31776,9 @@ async function listCollectionEntries(name, embeddingKey, tenantId2) {
31173
31776
  return vs.getAllDocuments();
31174
31777
  }
31175
31778
  try {
31176
- const provider = vectorStoreProviderManager.getProvider("default");
31177
- if (provider.listEntries) {
31178
- return provider.listEntries(buildTableName(tenantId2, name));
31779
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31780
+ if (provider2.listEntries) {
31781
+ return provider2.listEntries(buildTableName(tenantId2, name));
31179
31782
  }
31180
31783
  } catch {
31181
31784
  }
@@ -31185,9 +31788,9 @@ async function removeCollectionVectorStore(name, tenantId2) {
31185
31788
  const storeKey = buildKey(tenantId2, name);
31186
31789
  const tableName = buildTableName(tenantId2, name);
31187
31790
  try {
31188
- const provider = vectorStoreProviderManager.getProvider("default");
31189
- if (provider.dropTable) {
31190
- await provider.dropTable(tableName);
31791
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31792
+ if (provider2.dropTable) {
31793
+ await provider2.dropTable(tableName);
31191
31794
  }
31192
31795
  } catch {
31193
31796
  }
@@ -33502,7 +34105,7 @@ var DaytonaProvider = class {
33502
34105
 
33503
34106
  // src/sandbox_lattice/providers/LocalSandboxProvider.ts
33504
34107
  import * as path6 from "path";
33505
- import * as os from "os";
34108
+ import * as os2 from "os";
33506
34109
  import * as fs4 from "fs/promises";
33507
34110
 
33508
34111
  // src/sandbox_lattice/LocalSandboxInstance.ts
@@ -33529,6 +34132,32 @@ function assertRegularDeleteTarget(file, stat4) {
33529
34132
  function quoteShellArgument(value) {
33530
34133
  return `'${value.replace(/'/g, "'\\''")}'`;
33531
34134
  }
34135
+ var PLATFORM_SECRET_ENV_VARS = [
34136
+ "OPEN_TOKEN_SECRET",
34137
+ "AUTH_TOKEN_SECRET",
34138
+ "JWT_SECRET",
34139
+ "WEB_APP_ATTACHMENT_SECRET",
34140
+ "LATTICE_ENCRYPTION_KEY",
34141
+ "DATABASE_URL",
34142
+ "REDIS_PASSWORD",
34143
+ "MICROSANDBOX_API_KEY",
34144
+ "DAYTONA_API_KEY",
34145
+ "E2B_API_KEY",
34146
+ "OPENAI_API_KEY",
34147
+ "AZURE_OPENAI_API_KEY",
34148
+ "ANTHROPIC_API_KEY",
34149
+ "TAVILY_API_KEY",
34150
+ "VAPID_PRIVATE_KEY",
34151
+ "SUPABASE_KEY",
34152
+ "A2A_API_KEYS"
34153
+ ];
34154
+ function stripPlatformSecrets(env) {
34155
+ const rest = { ...env };
34156
+ for (const name of PLATFORM_SECRET_ENV_VARS) {
34157
+ delete rest[name];
34158
+ }
34159
+ return rest;
34160
+ }
33532
34161
  function validatePackEntryName(name) {
33533
34162
  if (!name || name === "." || name === ".." || name.includes("\\") || name.includes("/")) {
33534
34163
  throw new Error(`Cannot pack: unsafe entry name '${name}'`);
@@ -33701,7 +34330,7 @@ var LocalSandboxInstance = class {
33701
34330
  timeout: timeoutMs,
33702
34331
  maxBuffer: 10 * 1024 * 1024,
33703
34332
  // 10MB
33704
- env: { ...process.env, HOME: this.rootDir }
34333
+ env: stripPlatformSecrets({ ...process.env, HOME: this.rootDir })
33705
34334
  }
33706
34335
  );
33707
34336
  const output = stderr ? `${stdout}
@@ -33966,7 +34595,7 @@ var LocalSandboxProvider = class {
33966
34595
  this.instances = /* @__PURE__ */ new Map();
33967
34596
  this.creating = /* @__PURE__ */ new Map();
33968
34597
  this.createResourceAddress = createResourceAddress;
33969
- this.basePath = config.basePath ?? path6.join(os.homedir(), ".axiom-local-sandbox");
34598
+ this.basePath = config.basePath ?? path6.join(os2.homedir(), ".axiom-local-sandbox");
33970
34599
  }
33971
34600
  async createSandbox(name, _config) {
33972
34601
  const existing = this.instances.get(name);
@@ -35342,6 +35971,10 @@ var TaskLifecycleNotifier = class {
35342
35971
  this.listeners = /* @__PURE__ */ new Set();
35343
35972
  this.tails = /* @__PURE__ */ new Map();
35344
35973
  }
35974
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
35975
+ setOutbox(outbox2) {
35976
+ this.outbox = outbox2;
35977
+ }
35345
35978
  /**
35346
35979
  * Registers an async lifecycle listener.
35347
35980
  *
@@ -35362,6 +35995,25 @@ var TaskLifecycleNotifier = class {
35362
35995
  * @throws Error when the event is malformed or one or more listeners reject.
35363
35996
  */
35364
35997
  async publish(event) {
35998
+ const snapshot = snapshotNotification(event);
35999
+ const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
36000
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36001
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot));
36002
+ this.tails.set(key5, delivery);
36003
+ if (this.outbox) {
36004
+ try {
36005
+ await this.outbox.append({ topic: "task-lifecycle", payload: snapshot, originReplicaId: getReplicaId() });
36006
+ } catch {
36007
+ }
36008
+ }
36009
+ try {
36010
+ await delivery;
36011
+ } finally {
36012
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36013
+ }
36014
+ }
36015
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36016
+ async deliverFromOutbox(event) {
35365
36017
  const snapshot = snapshotNotification(event);
35366
36018
  const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
35367
36019
  const previous = this.tails.get(key5) ?? Promise.resolve();
@@ -35426,6 +36078,10 @@ var ProjectTaskAdmissionNotifier = class {
35426
36078
  if (!Number.isSafeInteger(timeout) || timeout <= 0) throw new Error("Invalid admission listener timeout");
35427
36079
  this.listenerTimeoutMs = timeout;
35428
36080
  }
36081
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
36082
+ setOutbox(outbox2) {
36083
+ this.outbox = outbox2;
36084
+ }
35429
36085
  /**
35430
36086
  * Registers a Project task admission listener.
35431
36087
  *
@@ -35445,6 +36101,30 @@ var ProjectTaskAdmissionNotifier = class {
35445
36101
  * @returns A promise settled after every snapshotted listener settles.
35446
36102
  */
35447
36103
  async publish(event) {
36104
+ const snapshot = snapshotNotification2(event);
36105
+ const listeners = [...this.listeners];
36106
+ const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
36107
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36108
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot, listeners));
36109
+ this.tails.set(key5, delivery);
36110
+ if (this.outbox) {
36111
+ try {
36112
+ await this.outbox.append({
36113
+ topic: "project-task-admission",
36114
+ payload: snapshot,
36115
+ originReplicaId: getReplicaId()
36116
+ });
36117
+ } catch {
36118
+ }
36119
+ }
36120
+ try {
36121
+ await delivery;
36122
+ } finally {
36123
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36124
+ }
36125
+ }
36126
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36127
+ async deliverFromOutbox(event) {
35448
36128
  const snapshot = snapshotNotification2(event);
35449
36129
  const listeners = [...this.listeners];
35450
36130
  const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
@@ -35514,8 +36194,137 @@ function canonicalTimestamp(value) {
35514
36194
  return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
35515
36195
  }
35516
36196
 
36197
+ // src/services/OutboxPoller.ts
36198
+ var OutboxPoller = class {
36199
+ /**
36200
+ * Creates a poller bound to one durable outbox.
36201
+ *
36202
+ * @param deps Store, owner replica id, topic handlers, and optional tuning.
36203
+ */
36204
+ constructor(deps) {
36205
+ this.deps = deps;
36206
+ this.cursor = null;
36207
+ this.polling = false;
36208
+ }
36209
+ /** Captures the starting cursor and begins polling. */
36210
+ async start() {
36211
+ this.cursor = await this.deps.store.latestId();
36212
+ this.timer = setInterval(() => {
36213
+ void this.pollOnce();
36214
+ }, this.deps.intervalMs ?? 1e3);
36215
+ this.timer.unref?.();
36216
+ }
36217
+ /** Stops polling. */
36218
+ stop() {
36219
+ if (this.timer) clearInterval(this.timer);
36220
+ this.timer = void 0;
36221
+ }
36222
+ /** Runs one drain pass (exported for tests). */
36223
+ async pollOnce() {
36224
+ if (this.polling) return;
36225
+ this.polling = true;
36226
+ try {
36227
+ const batchSize = this.deps.batchSize ?? 100;
36228
+ for (; ; ) {
36229
+ const events = await this.deps.store.listAfter(this.cursor, batchSize);
36230
+ if (events.length === 0) break;
36231
+ for (const event of events) {
36232
+ this.cursor = event.id;
36233
+ if (event.originReplicaId === this.deps.owner) continue;
36234
+ const handler = this.deps.handlers[event.topic];
36235
+ if (!handler) continue;
36236
+ try {
36237
+ await this.deliverWithTimeout(handler, event.payload);
36238
+ } catch {
36239
+ this.deps.log?.warn("Outbox delivery failed", { topic: event.topic, id: event.id });
36240
+ }
36241
+ }
36242
+ if (events.length < batchSize) break;
36243
+ }
36244
+ } catch (error) {
36245
+ this.deps.log?.warn("Outbox poll failed", { error: error instanceof Error ? error.message : String(error) });
36246
+ } finally {
36247
+ this.polling = false;
36248
+ }
36249
+ }
36250
+ /**
36251
+ * Runs one handler bounded by `handlerTimeoutMs` so a hung listener cannot
36252
+ * stall cross-replica delivery for this replica.
36253
+ */
36254
+ async deliverWithTimeout(handler, payload) {
36255
+ const timeoutMs = this.deps.handlerTimeoutMs ?? 5e3;
36256
+ let timer;
36257
+ const timeout = new Promise((_resolve, reject) => {
36258
+ timer = setTimeout(() => reject(new Error("Outbox handler timed out")), timeoutMs);
36259
+ timer.unref?.();
36260
+ });
36261
+ try {
36262
+ await Promise.race([Promise.resolve().then(() => handler(payload)), timeout]);
36263
+ } finally {
36264
+ if (timer) clearTimeout(timer);
36265
+ }
36266
+ }
36267
+ };
36268
+
35517
36269
  // src/services/ProjectMutationMutex.ts
35518
36270
  import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
36271
+
36272
+ // src/services/coordinationLease.ts
36273
+ var configured;
36274
+ function configureCoordinationLeaseStore(store) {
36275
+ configured = store;
36276
+ }
36277
+ function getCoordinationLeaseStore() {
36278
+ return configured;
36279
+ }
36280
+
36281
+ // src/services/withLease.ts
36282
+ import { randomUUID as randomUUID8 } from "crypto";
36283
+ var LeaseAcquisitionTimeoutError = class extends Error {
36284
+ /**
36285
+ * @param key Lease key that could not be acquired.
36286
+ * @param holder Unique holder that attempted acquisition.
36287
+ * @param timeoutMs How long the caller waited before giving up.
36288
+ */
36289
+ constructor(key5, holder, timeoutMs) {
36290
+ super(`Could not acquire lease '${key5}' for '${holder}' within ${timeoutMs}ms`);
36291
+ this.key = key5;
36292
+ this.holder = holder;
36293
+ this.timeoutMs = timeoutMs;
36294
+ /** Stable machine-readable code. */
36295
+ this.code = "LEASE_ACQUISITION_TIMEOUT";
36296
+ this.name = "LeaseAcquisitionTimeoutError";
36297
+ }
36298
+ };
36299
+ function sleep(ms) {
36300
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
36301
+ }
36302
+ async function withLease(store, key5, ownerLabel, fn, options = {}) {
36303
+ const ttlMs = options.ttlMs ?? 3e4;
36304
+ const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
36305
+ const retryDelayMs = options.retryDelayMs ?? 50;
36306
+ const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
36307
+ const holder = `${ownerLabel}#${randomUUID8()}`;
36308
+ const deadline = Date.now() + acquireTimeoutMs;
36309
+ for (; ; ) {
36310
+ if (await store.tryAcquire(key5, holder, ttlMs)) break;
36311
+ if (Date.now() >= deadline) throw new LeaseAcquisitionTimeoutError(key5, holder, acquireTimeoutMs);
36312
+ await sleep(retryDelayMs);
36313
+ }
36314
+ const heartbeat = setInterval(() => {
36315
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
36316
+ }, heartbeatMs);
36317
+ heartbeat.unref?.();
36318
+ try {
36319
+ return await fn();
36320
+ } finally {
36321
+ clearInterval(heartbeat);
36322
+ await store.release(key5, holder).catch(() => void 0);
36323
+ }
36324
+ }
36325
+
36326
+ // src/services/ProjectMutationMutex.ts
36327
+ var MUTATION_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
35519
36328
  function projectKey(scope) {
35520
36329
  const row = snapshotExactRecord4(scope, ["tenantId", "projectId"]);
35521
36330
  if (!row || typeof row.tenantId !== "string" || !row.tenantId || row.tenantId.includes("\0") || typeof row.projectId !== "string" || !row.projectId || row.projectId.includes("\0")) {
@@ -35531,9 +36340,12 @@ var ProjectMutationMutex = class {
35531
36340
  /** Runs one mutation after all earlier mutations for the same Tenant and Project. */
35532
36341
  async runExclusive(scope, operation, signal) {
35533
36342
  const key5 = projectKey(scope);
36343
+ const leaseKey = `project-mutation:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.projectId)}`;
35534
36344
  await this.acquire(key5, signal);
35535
36345
  try {
35536
- return await operation();
36346
+ const store = getCoordinationLeaseStore();
36347
+ if (!store) return await operation();
36348
+ return await withLease(store, leaseKey, getReplicaId(), operation, MUTATION_LEASE_OPTIONS);
35537
36349
  } finally {
35538
36350
  this.release(key5);
35539
36351
  }
@@ -35591,7 +36403,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
35591
36403
  }
35592
36404
 
35593
36405
  // src/services/RoomAgentMessageService.ts
35594
- import { randomUUID as randomUUID7 } from "crypto";
36406
+ import { randomUUID as randomUUID9 } from "crypto";
35595
36407
  import { snapshotExactArray, snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
35596
36408
  var INPUT_KEYS = [
35597
36409
  "tenantId",
@@ -35679,7 +36491,7 @@ var RoomAgentMessageService = class {
35679
36491
  throw new Error("Invalid project room membership");
35680
36492
  }
35681
36493
  const candidate = {
35682
- id: (this.deps.idFactory ?? randomUUID7)(),
36494
+ id: (this.deps.idFactory ?? randomUUID9)(),
35683
36495
  tenantId: input.tenantId,
35684
36496
  workspaceId: input.workspaceId,
35685
36497
  projectId: input.projectId,
@@ -36040,9 +36852,9 @@ var BELIEF_OWNER_WRITE_ATTEMPTS = 3;
36040
36852
  function isWellFormedImpact(impact) {
36041
36853
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(impact.key) && Number.isInteger(impact.after) && impact.after >= 0 && impact.after <= 100 && impact.basis.trim().length > 0 && impact.basis.length <= 1e3 && !/[\r\n]/.test(impact.basis);
36042
36854
  }
36043
- async function applyBeliefImpacts(taskStore, owner, impacts) {
36044
- if (impacts.length === 0) return { description: owner.description ?? "" };
36045
- let current = owner;
36855
+ async function applyBeliefImpacts(taskStore, owner2, impacts) {
36856
+ if (impacts.length === 0) return { description: owner2.description ?? "" };
36857
+ let current = owner2;
36046
36858
  for (let attempt = 0; attempt < BELIEF_OWNER_WRITE_ATTEMPTS; attempt += 1) {
36047
36859
  const parsed = parseTaskBeliefState(current.description ?? "");
36048
36860
  const existingTable = parsed.success;
@@ -36171,14 +36983,14 @@ function sameBeliefOwner(left, right) {
36171
36983
  function sameBeliefOwnerIdentity(left, right) {
36172
36984
  return canonicalJson({ ...left, beliefState: void 0 }) === canonicalJson({ ...right, beliefState: void 0 });
36173
36985
  }
36174
- function canonicalBeliefOwner(owner) {
36986
+ function canonicalBeliefOwner(owner2) {
36175
36987
  const beliefState = {
36176
- entries: owner.beliefState.entries.map((entry) => ({
36988
+ entries: owner2.beliefState.entries.map((entry) => ({
36177
36989
  ...entry,
36178
36990
  basis: entry.basis.trim().replace(/\s+/g, " ")
36179
36991
  })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0)
36180
36992
  };
36181
- return { ...owner, beliefState };
36993
+ return { ...owner2, beliefState };
36182
36994
  }
36183
36995
  function eventKeyFor(taskId, result, impacts, beliefOwner) {
36184
36996
  const base = `belief-impact:${taskId}:${buildBeliefCompletionEventKey(taskId, result, impacts)}`;
@@ -37545,8 +38357,8 @@ var TaskLifecycleService = class {
37545
38357
  return { task: existing, parent: parentResult, eventKey, evidence, result, impacts };
37546
38358
  }
37547
38359
  beliefOwnerSnapshot(task, parent) {
37548
- const owner = parent?.ownerType === "agent" ? parent : task;
37549
- const parsed = parseTaskBeliefState(owner.description ?? "");
38360
+ const owner2 = parent?.ownerType === "agent" ? parent : task;
38361
+ const parsed = parseTaskBeliefState(owner2.description ?? "");
37550
38362
  const beliefState = parsed.success ? parsed.state : { entries: [] };
37551
38363
  if (parent?.ownerType === "agent") {
37552
38364
  return {
@@ -38266,7 +39078,7 @@ function createTaskLifecycleService(deps) {
38266
39078
  }
38267
39079
 
38268
39080
  // src/services/TaskExecutionService.ts
38269
- import { randomUUID as randomUUID8 } from "crypto";
39081
+ import { randomUUID as randomUUID11 } from "crypto";
38270
39082
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
38271
39083
  import { isExecutionResultEventKey as isExecutionResultEventKey2 } from "@axiom-lattice/protocols";
38272
39084
 
@@ -38525,12 +39337,20 @@ var TaskRunRegistry = class {
38525
39337
  }
38526
39338
  };
38527
39339
  var taskRunRegistry = new TaskRunRegistry();
39340
+ async function safelyLive(probe, task) {
39341
+ try {
39342
+ return await probe(task);
39343
+ } catch {
39344
+ return true;
39345
+ }
39346
+ }
38528
39347
  async function reconcileStaleTaskRuns(input) {
38529
39348
  const registry4 = input.registry ?? taskRunRegistry;
38530
39349
  const converged = [];
38531
39350
  const convergedTasks = [];
38532
39351
  for (const task of input.tasks) {
38533
39352
  if (task.status !== "in_progress" || registry4.isLive(task.id)) continue;
39353
+ if (input.isTaskLive && await safelyLive(input.isTaskLive, task)) continue;
38534
39354
  const result = await input.lifecycle.failTask({
38535
39355
  tenantId: task.tenantId,
38536
39356
  taskId: task.id,
@@ -38551,7 +39371,8 @@ async function reconcileTenantTaskRuns(input) {
38551
39371
  tasks: candidates,
38552
39372
  actor: input.actor,
38553
39373
  lifecycle: input.lifecycle,
38554
- ...input.registry ? { registry: input.registry } : {}
39374
+ ...input.registry ? { registry: input.registry } : {},
39375
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
38555
39376
  });
38556
39377
  }
38557
39378
  async function reconcileAllTenantTaskRuns(input) {
@@ -38567,7 +39388,8 @@ async function reconcileAllTenantTaskRuns(input) {
38567
39388
  taskStore: input.taskStore,
38568
39389
  lifecycle: input.lifecycle,
38569
39390
  ...input.registry ? { registry: input.registry } : {},
38570
- ...input.include ? { include: input.include } : {}
39391
+ ...input.include ? { include: input.include } : {},
39392
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
38571
39393
  });
38572
39394
  } catch {
38573
39395
  continue;
@@ -38578,6 +39400,42 @@ async function reconcileAllTenantTaskRuns(input) {
38578
39400
  return { converged, convergedTasks };
38579
39401
  }
38580
39402
 
39403
+ // src/services/taskRunLease.ts
39404
+ import { randomUUID as randomUUID10 } from "crypto";
39405
+ function taskRunLeaseKey(taskId) {
39406
+ return `task-run:${taskId}`;
39407
+ }
39408
+ async function tryAcquireTaskRun(input) {
39409
+ const key5 = taskRunLeaseKey(input.taskId);
39410
+ const holder = `${input.owner}#${randomUUID10()}`;
39411
+ if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
39412
+ const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
39413
+ let released = false;
39414
+ let lost = false;
39415
+ const heartbeat = setInterval(() => {
39416
+ void input.store.renew(key5, holder, input.ttlMs).then((renewed) => {
39417
+ if (released || renewed || lost) return;
39418
+ lost = true;
39419
+ try {
39420
+ input.onLost?.();
39421
+ } catch {
39422
+ }
39423
+ }).catch(() => void 0);
39424
+ }, heartbeatMs);
39425
+ heartbeat.unref?.();
39426
+ return {
39427
+ get lost() {
39428
+ return lost;
39429
+ },
39430
+ release: async () => {
39431
+ if (released) return;
39432
+ released = true;
39433
+ clearInterval(heartbeat);
39434
+ await input.store.release(key5, holder).catch(() => void 0);
39435
+ }
39436
+ };
39437
+ }
39438
+
38581
39439
  // src/services/ExecutionAuthorityRegistry.ts
38582
39440
  var ManagedTaskStartError = class extends Error {
38583
39441
  /**
@@ -38848,6 +39706,14 @@ async function prepareTaskDispatch(deps, input) {
38848
39706
 
38849
39707
  // src/services/TaskExecutionService.ts
38850
39708
  import { GraphInterrupt as GraphInterrupt2 } from "@langchain/langgraph";
39709
+ var TASK_RUN_LEASE_TTL_MS = 12e4;
39710
+ function getLeaseStore() {
39711
+ try {
39712
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
39713
+ } catch {
39714
+ return void 0;
39715
+ }
39716
+ }
38851
39717
  function persistedExecutionObservation(item, attempted) {
38852
39718
  const detail = item.detail;
38853
39719
  const persistedObservation = toJsonSafeValue(detail?.observation);
@@ -38908,11 +39774,11 @@ function createTaskExecutionService(deps) {
38908
39774
  let threadId;
38909
39775
  let runId;
38910
39776
  try {
38911
- threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID8)();
39777
+ threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID11)();
38912
39778
  if (task.status === "interrupted" && reuseCurrentRun) {
38913
39779
  runId = storedRunId;
38914
39780
  } else if (task.status === "interrupted" || task.status === "failed") {
38915
- runId = (deps.createRunId ?? randomUUID8)();
39781
+ runId = (deps.createRunId ?? randomUUID11)();
38916
39782
  }
38917
39783
  } catch (error) {
38918
39784
  return {
@@ -38975,7 +39841,21 @@ function createTaskExecutionService(deps) {
38975
39841
  warnings.push(...begun.warnings);
38976
39842
  }
38977
39843
  taskRunRegistry.register(task.id);
39844
+ let taskRunLease = null;
38978
39845
  try {
39846
+ const leaseStore = getLeaseStore();
39847
+ if (leaseStore) {
39848
+ try {
39849
+ taskRunLease = await tryAcquireTaskRun({
39850
+ store: leaseStore,
39851
+ taskId: task.id,
39852
+ owner: getReplicaId(),
39853
+ ttlMs: TASK_RUN_LEASE_TTL_MS
39854
+ });
39855
+ } catch {
39856
+ taskRunLease = null;
39857
+ }
39858
+ }
38979
39859
  const invocation = {
38980
39860
  tenantId: tenantId2,
38981
39861
  workspaceId: task.workspaceId,
@@ -39143,6 +40023,7 @@ function createTaskExecutionService(deps) {
39143
40023
  }
39144
40024
  } finally {
39145
40025
  taskRunRegistry.unregister(task.id);
40026
+ if (taskRunLease) await taskRunLease.release();
39146
40027
  }
39147
40028
  };
39148
40029
  return {
@@ -39155,6 +40036,43 @@ function createTaskExecutionService(deps) {
39155
40036
  };
39156
40037
  }
39157
40038
 
40039
+ // src/services/leadership.ts
40040
+ import { randomUUID as randomUUID12 } from "crypto";
40041
+ async function runAsLeader(store, name, fn, options = {}) {
40042
+ const key5 = `leader:${name}`;
40043
+ const owner2 = options.owner ?? getReplicaId();
40044
+ const holder = `${owner2}#${randomUUID12()}`;
40045
+ const ttlMs = options.ttlMs ?? 6e4;
40046
+ if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
40047
+ const heartbeat = setInterval(() => {
40048
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
40049
+ }, Math.max(1e3, Math.floor(ttlMs / 3)));
40050
+ heartbeat.unref?.();
40051
+ try {
40052
+ const result = await fn();
40053
+ return { ran: true, result };
40054
+ } finally {
40055
+ clearInterval(heartbeat);
40056
+ if (!options.hold) await store.release(key5, holder).catch(() => void 0);
40057
+ }
40058
+ }
40059
+
40060
+ // src/services/pluginCatalogSync.ts
40061
+ var PLUGIN_CATALOG_CHANGED_TOPIC = "plugin-catalog-changed";
40062
+ var outbox;
40063
+ var owner = "unknown";
40064
+ function configurePluginCatalogSync(next) {
40065
+ outbox = next?.outbox;
40066
+ owner = next?.owner ?? "unknown";
40067
+ }
40068
+ async function publishPluginCatalogChanged(tenantId2) {
40069
+ if (!outbox || !tenantId2) return;
40070
+ try {
40071
+ await outbox.append({ topic: PLUGIN_CATALOG_CHANGED_TOPIC, payload: { tenantId: tenantId2 }, originReplicaId: owner });
40072
+ } catch {
40073
+ }
40074
+ }
40075
+
39158
40076
  // src/index.ts
39159
40077
  import * as Protocols from "@axiom-lattice/protocols";
39160
40078
 
@@ -39297,9 +40215,9 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39297
40215
  if (!skillName) continue;
39298
40216
  if (await isPluginSkillManagedInSandbox(sandbox, skillName)) {
39299
40217
  const enabledDefinition = pluginSkills?.[skillName];
39300
- const owner = pluginSkillOwners?.[skillName];
39301
- const registered = owner && enabledDefinition ? PluginRegistry.getSkillForPlugin(owner, skillName) : void 0;
39302
- if (!registered || registered.definition !== enabledDefinition || !owner || !await isPluginSkillAuthorizedInSandbox(sandbox, skillName, owner) || await getPluginSkillManifestVersion(sandbox, skillName) !== enabledDefinition.version) {
40218
+ const owner2 = pluginSkillOwners?.[skillName];
40219
+ const registered = owner2 && enabledDefinition ? PluginRegistry.getSkillForPlugin(owner2, skillName) : void 0;
40220
+ if (!registered || registered.definition !== enabledDefinition || !owner2 || !await isPluginSkillAuthorizedInSandbox(sandbox, skillName, owner2) || await getPluginSkillManifestVersion(sandbox, skillName) !== enabledDefinition.version) {
39303
40221
  continue;
39304
40222
  }
39305
40223
  }
@@ -39325,8 +40243,8 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39325
40243
  const seenNames = new Set(filteredSkills.map((skill) => skill.name));
39326
40244
  try {
39327
40245
  for (const [name, definition] of Object.entries(pluginSkills ?? {})) {
39328
- const owner = pluginSkillOwners?.[name];
39329
- const registered = owner ? PluginRegistry.getSkillForPlugin(owner, name) : void 0;
40246
+ const owner2 = pluginSkillOwners?.[name];
40247
+ const registered = owner2 ? PluginRegistry.getSkillForPlugin(owner2, name) : void 0;
39330
40248
  if (!registered || registered.definition !== definition) continue;
39331
40249
  const { meta } = parseSkillFrontmatter(definition.content);
39332
40250
  if (!meta.name || !meta.description || seenNames.has(meta.name)) continue;
@@ -39363,24 +40281,24 @@ import z54 from "zod";
39363
40281
  import { tool as tool48 } from "langchain";
39364
40282
 
39365
40283
  // src/sandbox_plugin/pluginSkillResolution.ts
39366
- async function resolveTenantPluginSkill(tenantId2, owner, skillName) {
40284
+ async function resolveTenantPluginSkill(tenantId2, owner2, skillName) {
39367
40285
  try {
39368
40286
  const { plugins } = await getSandboxPluginCatalog().ensureLoaded(tenantId2);
39369
- const plugin = plugins.find((candidate) => candidate.meta.type === owner);
40287
+ const plugin = plugins.find((candidate) => candidate.meta.type === owner2);
39370
40288
  return plugin?.skills?.[skillName];
39371
40289
  } catch {
39372
40290
  return void 0;
39373
40291
  }
39374
40292
  }
39375
- async function resolveEnabledPluginSkill(owner, skillName, enabledDefinition, tenantId2) {
39376
- if (!owner || !enabledDefinition) return void 0;
39377
- const registered = PluginRegistry.getSkillForPlugin(owner, skillName);
39378
- if (registered && registered.pluginType === owner && registered.definition === enabledDefinition) {
39379
- return { pluginType: owner, definition: enabledDefinition };
40293
+ async function resolveEnabledPluginSkill(owner2, skillName, enabledDefinition, tenantId2) {
40294
+ if (!owner2 || !enabledDefinition) return void 0;
40295
+ const registered = PluginRegistry.getSkillForPlugin(owner2, skillName);
40296
+ if (registered && registered.pluginType === owner2 && registered.definition === enabledDefinition) {
40297
+ return { pluginType: owner2, definition: enabledDefinition };
39380
40298
  }
39381
40299
  if (!tenantId2) return void 0;
39382
- const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner, skillName);
39383
- return tenantDefinition ? { pluginType: owner, definition: tenantDefinition } : void 0;
40300
+ const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner2, skillName);
40301
+ return tenantDefinition ? { pluginType: owner2, definition: tenantDefinition } : void 0;
39384
40302
  }
39385
40303
 
39386
40304
  // src/tool_lattice/skill/load_skill_content.ts
@@ -39442,10 +40360,10 @@ var createLoadSkillContentTool = (pluginSkills, pluginSkillOwnersOrStaticSelecti
39442
40360
  return result2;
39443
40361
  }
39444
40362
  const enabledDefinition = pluginSkills?.[input.skill_name];
39445
- const owner = pluginSkillOwners?.[input.skill_name];
40363
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39446
40364
  const runConfig = _exe_config?.configurable?.runConfig || {};
39447
40365
  const pluginSkill = await resolveEnabledPluginSkill(
39448
- owner,
40366
+ owner2,
39449
40367
  input.skill_name,
39450
40368
  enabledDefinition,
39451
40369
  runConfig.tenantId
@@ -39552,9 +40470,9 @@ var createLoadSkillResourceTool = (pluginSkills, pluginSkillOwners) => {
39552
40470
  validateRelativePath(input.resource_path, "resource");
39553
40471
  const sandbox = await getSandboxFromExeConfig3(_exe_config);
39554
40472
  const definition = pluginSkills?.[input.skill_name];
39555
- const owner = pluginSkillOwners?.[input.skill_name];
40473
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39556
40474
  const tenantId2 = _exe_config?.configurable?.runConfig?.tenantId;
39557
- const resolved = await resolveEnabledPluginSkill(owner, input.skill_name, definition, tenantId2);
40475
+ const resolved = await resolveEnabledPluginSkill(owner2, input.skill_name, definition, tenantId2);
39558
40476
  if (definition) {
39559
40477
  if (!resolved || !resolved.definition.resources?.[input.resource_path]) {
39560
40478
  return `Resource "${input.resource_path}" not found for skill "${input.skill_name}".`;
@@ -41786,9 +42704,9 @@ async function renderAvailableExecutors(rc, tenantId2) {
41786
42704
  const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
41787
42705
  const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
41788
42706
  const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
41789
- const cached = executorCache.get(cacheKey);
42707
+ const cached2 = executorCache.get(cacheKey);
41790
42708
  const now = Date.now();
41791
- if (cached && now - cached.at < EXECUTOR_CACHE_TTL_MS) return cached.text;
42709
+ if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
41792
42710
  const lines = [];
41793
42711
  try {
41794
42712
  if (governed && projectId) {
@@ -46074,6 +46992,7 @@ var sandboxPluginsPlugin = {
46074
46992
  }
46075
46993
  case "reload": {
46076
46994
  const loaded = await catalog2.reload(tenantId2);
46995
+ void publishPluginCatalogChanged(tenantId2);
46077
46996
  return JSON.stringify({ reloaded: true, plugins: loaded.plugins.length, diagnostics: loaded.diagnostics });
46078
46997
  }
46079
46998
  case "create":
@@ -46093,12 +47012,14 @@ var sandboxPluginsPlugin = {
46093
47012
  if (!check.manifest) return JSON.stringify({ written: false, diagnostics: check.diagnostics });
46094
47013
  await catalog2.writePlugin(tenantId2, input.type, input.manifest, handlers);
46095
47014
  const loaded = await catalog2.reload(tenantId2);
47015
+ void publishPluginCatalogChanged(tenantId2);
46096
47016
  return JSON.stringify({ written: true, diagnostics: loaded.diagnostics, hash: loaded.hash });
46097
47017
  }
46098
47018
  case "delete": {
46099
47019
  if (!input.type) return "type is required";
46100
47020
  await catalog2.deletePlugin(tenantId2, input.type);
46101
47021
  await catalog2.reload(tenantId2);
47022
+ void publishPluginCatalogChanged(tenantId2);
46102
47023
  return JSON.stringify({ deleted: true });
46103
47024
  }
46104
47025
  case "validate": {
@@ -46210,6 +47131,26 @@ function registerBuiltinPlugins() {
46210
47131
  }
46211
47132
  }
46212
47133
 
47134
+ // src/sandbox_plugin/provisionOpenClient.ts
47135
+ import { readFileSync } from "fs";
47136
+ var SDK_ROOT = "/root/.agents/sdk";
47137
+ var CLIENT_DIR = `${SDK_ROOT}/open-mcp-client`;
47138
+ var MANIFEST_PATH2 = `${SDK_ROOT}/.open-client-manifest.json`;
47139
+ async function provisionSandboxOpenClient(sandbox, bundle) {
47140
+ await sandbox.file.createDirectory(SDK_ROOT);
47141
+ await sandbox.file.createDirectory(CLIENT_DIR);
47142
+ await sandbox.file.writeFile(`${CLIENT_DIR}/index.js`, bundle.indexJs);
47143
+ await sandbox.file.writeFile(`${CLIENT_DIR}/cli.js`, bundle.cliJs);
47144
+ await sandbox.file.writeFile(MANIFEST_PATH2, JSON.stringify({ version: bundle.version }));
47145
+ }
47146
+ function buildBundleFromDist(distDir, version) {
47147
+ return {
47148
+ indexJs: readFileSync(`${distDir}/index.js`, "utf8"),
47149
+ cliJs: readFileSync(`${distDir}/cli.js`, "utf8"),
47150
+ version
47151
+ };
47152
+ }
47153
+
46213
47154
  // src/personal_assistant/PersonalAssistantConfig.ts
46214
47155
  function deepClone(obj) {
46215
47156
  return JSON.parse(JSON.stringify(obj));
@@ -46671,10 +47612,12 @@ export {
46671
47612
  InMemoryChunkBuffer,
46672
47613
  InMemoryCollectionStore,
46673
47614
  InMemoryDatabaseConfigStore,
47615
+ InMemoryLeaseStore,
46674
47616
  InMemoryMailboxStore,
46675
47617
  InMemoryMenuStore,
46676
47618
  InMemoryModelProviderStore,
46677
47619
  InMemoryOpenAuditStore,
47620
+ InMemoryOutboxStore,
46678
47621
  InMemoryProjectBotMembershipStore,
46679
47622
  InMemoryProjectMembershipStore,
46680
47623
  InMemoryProjectRoomCoordinator,
@@ -46694,6 +47637,7 @@ export {
46694
47637
  LatticeEval,
46695
47638
  LatticeEvalProject,
46696
47639
  LatticeEvalSuite,
47640
+ LeaseAcquisitionTimeoutError,
46697
47641
  LocalSandboxInstance,
46698
47642
  LocalSandboxProvider,
46699
47643
  LoggerLatticeManager,
@@ -46716,7 +47660,10 @@ export {
46716
47660
  MicrosandboxServiceClient,
46717
47661
  ModelLatticeManager,
46718
47662
  MysqlDatabase,
47663
+ OutboxPoller,
46719
47664
  PACK_DIRECTORY_TIMEOUT_SECONDS,
47665
+ PLATFORM_SECRET_ENV_VARS,
47666
+ PLUGIN_CATALOG_CHANGED_TOPIC,
46720
47667
  PersonalAssistantConfig,
46721
47668
  PinoLoggerClient,
46722
47669
  PluginRegistry,
@@ -46737,6 +47684,8 @@ export {
46737
47684
  RepeatedToolCallError,
46738
47685
  RoomAgentMessageService,
46739
47686
  SAFE_PATH_COMPONENT,
47687
+ SANDBOX_OPEN_GATEWAY_URL_ENV,
47688
+ SANDBOX_OPEN_TOKEN_ENV,
46740
47689
  STTModelLattice,
46741
47690
  STTModelLatticeManager,
46742
47691
  SandboxFilesystem,
@@ -46777,11 +47726,13 @@ export {
46777
47726
  appendReconciliationWarning,
46778
47727
  appendUiFence,
46779
47728
  assertSafeUpstreamUrl,
47729
+ buildBundleFromDist,
46780
47730
  buildGrepResultsDict,
46781
47731
  buildInput,
46782
47732
  buildNamedVolumeName,
46783
47733
  buildRemotePackDirectoryCommand,
46784
47734
  buildSandboxMetadataEnv,
47735
+ buildSandboxOpenEnv,
46785
47736
  buildSkillFile,
46786
47737
  buildStateAnnotation,
46787
47738
  buildTableName,
@@ -46790,11 +47741,14 @@ export {
46790
47741
  checkEmptyContent,
46791
47742
  clearEncryptionKeyCache,
46792
47743
  clearEvalRunService,
47744
+ clearSandboxOpenCredentialProviderForTests,
46793
47745
  collectionLatticeManager,
46794
47746
  compileInternal,
46795
47747
  compileSandboxPlugin,
46796
47748
  compileWorkflow,
46797
47749
  computeSandboxName,
47750
+ configureCoordinationLeaseStore,
47751
+ configurePluginCatalogSync,
46798
47752
  configureStores,
46799
47753
  connectAllChannels,
46800
47754
  convertToConnection,
@@ -46826,6 +47780,7 @@ export {
46826
47780
  createSandboxResolver,
46827
47781
  createSchedulerMiddleware,
46828
47782
  createSharePayload,
47783
+ createShellExecTool,
46829
47784
  createTaskExecutionService,
46830
47785
  createTaskLifecycleService,
46831
47786
  createTaskMiddleware,
@@ -46872,6 +47827,7 @@ export {
46872
47827
  getCheckpointSaver,
46873
47828
  getChunkBuffer,
46874
47829
  getCollectionEntryCount,
47830
+ getCoordinationLeaseStore,
46875
47831
  getEmbeddingsClient,
46876
47832
  getEmbeddingsLattice,
46877
47833
  getEncryptionKey,
@@ -46883,15 +47839,18 @@ export {
46883
47839
  getOrCreateCollectionVectorStore,
46884
47840
  getPluginSkillManifestVersion,
46885
47841
  getQueueLattice,
47842
+ getReplicaId,
46886
47843
  getRuntimeCapabilityConfig,
46887
47844
  getRuntimeSkillSelection,
46888
47845
  getSTTClient,
46889
47846
  getSTTClientWithTenant,
46890
47847
  getSTTModelLattice,
46891
47848
  getSandBoxManager,
47849
+ getSandboxOpenCredential,
46892
47850
  getSandboxPluginCatalog,
46893
47851
  getScheduleLattice,
46894
47852
  getStoreLattice,
47853
+ getThreadRunOwner,
46895
47854
  getToolClient,
46896
47855
  getToolClientWithTenant,
46897
47856
  getToolDefinition,
@@ -46945,6 +47904,8 @@ export {
46945
47904
  projectMutationMutex,
46946
47905
  projectRoomPlugin,
46947
47906
  projectTaskAdmissionNotifier,
47907
+ provisionSandboxOpenClient,
47908
+ publishPluginCatalogChanged,
46948
47909
  queueLatticeManager,
46949
47910
  readLoopBreakerConfig,
46950
47911
  reconcileAllTenantTaskRuns,
@@ -46963,6 +47924,7 @@ export {
46963
47924
  registerModelLatticeForTenant,
46964
47925
  registerQueueLattice,
46965
47926
  registerSTTModelLattice,
47927
+ registerSandboxOpenCredentialProvider,
46966
47928
  registerSandboxProviderType,
46967
47929
  registerScheduleLattice,
46968
47930
  registerStoreLattice,
@@ -46973,6 +47935,7 @@ export {
46973
47935
  registerWorkflowRun,
46974
47936
  removeCollectionVectorStore,
46975
47937
  renderTemplate,
47938
+ resetReplicaIdForTests,
46976
47939
  resolveEnabledPluginSkill,
46977
47940
  resolveJudgeModelKey,
46978
47941
  resolvePath,
@@ -46980,6 +47943,7 @@ export {
46980
47943
  resolveSandboxPluginConnections,
46981
47944
  resolveTenantPluginSkill,
46982
47945
  roomCoordinatorConfig,
47946
+ runAsLeader,
46983
47947
  runRemotePackDirectory,
46984
47948
  safeTaskResponseId,
46985
47949
  sandboxLatticeManager,
@@ -46998,11 +47962,15 @@ export {
46998
47962
  storeLatticeManager,
46999
47963
  sttModelLatticeManager,
47000
47964
  taskLifecycleNotifier,
47965
+ taskRunLeaseKey,
47001
47966
  taskRunRegistry,
47967
+ threadRunLeaseKey,
47002
47968
  toJsonSchema,
47003
47969
  toSafeStateExpr,
47004
47970
  toolLatticeManager,
47005
47971
  truncateIfTooLong,
47972
+ tryAcquireTaskRun,
47973
+ tryAcquireThreadRun,
47006
47974
  unregisterTeammateAgent,
47007
47975
  unregisterWorkflowRun,
47008
47976
  updateFileData,
@@ -47018,6 +47986,7 @@ export {
47018
47986
  validateToolInput,
47019
47987
  vectorStoreLatticeManager,
47020
47988
  vectorStoreProviderManager,
47989
+ withLease,
47021
47990
  withTimeout,
47022
47991
  wrapMcpToolWithUi
47023
47992
  };