@axiom-lattice/core 6.2.0 → 6.3.1

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();
@@ -18254,12 +18552,47 @@ var Agent = class {
18254
18552
  id: interruptData.id,
18255
18553
  data: { content: interruptData.value }
18256
18554
  };
18555
+ await this.awaitDurableInterrupt(interruptData.id, signal);
18257
18556
  }
18258
18557
  if (data) {
18259
18558
  this.addChunk(data);
18260
18559
  }
18261
18560
  }
18262
18561
  }
18562
+ /**
18563
+ * Waits until the thread's durable checkpoint reports the given interrupt id.
18564
+ *
18565
+ * Reads {@link getCurrentState} (the checkpoint, i.e. the single source of
18566
+ * truth), never an in-memory status, so a `true` result guarantees a resume
18567
+ * against this thread will find the interrupt. Bounded so a broken
18568
+ * checkpointer cannot strand the stream; on timeout the interrupt is emitted
18569
+ * anyway and a warning is logged.
18570
+ *
18571
+ * @param interruptId - Interrupt id from the streamed `__interrupt__` update.
18572
+ * @param signal - Run abort signal; polling stops when aborted.
18573
+ * @param timeoutMs - Maximum time to wait for durable confirmation.
18574
+ */
18575
+ async awaitDurableInterrupt(interruptId, signal, timeoutMs = 5e3) {
18576
+ if (!interruptId) return;
18577
+ const deadline = Date.now() + timeoutMs;
18578
+ while (!signal?.aborted) {
18579
+ try {
18580
+ const state = await this.getCurrentState();
18581
+ const durable = (state?.tasks ?? []).some(
18582
+ (task) => (task.interrupts ?? []).some((interrupt5) => interrupt5.id === interruptId)
18583
+ );
18584
+ if (durable) return;
18585
+ } catch {
18586
+ }
18587
+ if (Date.now() >= deadline) {
18588
+ console.warn(
18589
+ `[Agent] interrupt ${interruptId} not durable within ${timeoutMs}ms on thread ${this.thread_id}; emitting without durable confirmation`
18590
+ );
18591
+ return;
18592
+ }
18593
+ await new Promise((resolve4) => setTimeout(resolve4, 20));
18594
+ }
18595
+ }
18263
18596
  /**
18264
18597
  * Resume LangGraph execution from the last checkpoint.
18265
18598
  *
@@ -18309,6 +18642,29 @@ var Agent = class {
18309
18642
  }
18310
18643
  return this.queueStore;
18311
18644
  }
18645
+ /**
18646
+ * Resolves the shared lease store used to serialize thread runs across
18647
+ * Gateway replicas. Returns `undefined` when no lease store is registered so
18648
+ * single-process deployments keep running unconditionally.
18649
+ */
18650
+ getLeaseStore() {
18651
+ try {
18652
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
18653
+ } catch {
18654
+ return void 0;
18655
+ }
18656
+ }
18657
+ /**
18658
+ * Releases this replica's thread-run ownership lease, if held.
18659
+ *
18660
+ * @remarks Safe to call from any definitive-stop path; a second call is a
18661
+ * no-op. The handle's unique holder makes a stale release harmless.
18662
+ */
18663
+ async releaseThreadRunLease() {
18664
+ const lease = this.threadRunLease;
18665
+ this.threadRunLease = null;
18666
+ if (lease) await lease.release();
18667
+ }
18312
18668
  getDefaultQueueConfig() {
18313
18669
  return {
18314
18670
  mode: "collect" /* COLLECT */,
@@ -18334,6 +18690,8 @@ var Agent = class {
18334
18690
  */
18335
18691
  stopQueueProcessor() {
18336
18692
  this.cancelQueueProcessorRetry();
18693
+ this.cancelThreadRunLeaseRetry();
18694
+ void this.releaseThreadRunLease();
18337
18695
  this.clearRecoveryBlock();
18338
18696
  this.queueProcessorGeneration += 1;
18339
18697
  if (this.abortController) {
@@ -18481,16 +18839,50 @@ var Agent = class {
18481
18839
  startQueueProcessorIfNeeded() {
18482
18840
  return this.startQueueProcessor(true);
18483
18841
  }
18484
- startQueueProcessor(freshWakeup) {
18842
+ async startQueueProcessor(freshWakeup) {
18485
18843
  this.queueWakeRequested = true;
18486
18844
  if (this.queueProcessorPromise) return this.queueProcessorPromise;
18487
- if (this.queueProcessorRetryTimer) return Promise.resolve();
18845
+ if (this.queueProcessorRetryTimer) return;
18488
18846
  if (freshWakeup && this.queueProcessorRetryAttempt >= QUEUE_PROCESSOR_MAX_RETRIES) {
18489
18847
  this.queueProcessorRetryAttempt = 0;
18490
18848
  }
18849
+ if (freshWakeup && this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) {
18850
+ this.threadRunLeaseRetryAttempt = 0;
18851
+ }
18491
18852
  this.queueWakeRequested = false;
18492
- let processorFailed = false;
18493
18853
  const processorGeneration = this.queueProcessorGeneration;
18854
+ if (this.threadRunLease?.lost) await this.releaseThreadRunLease();
18855
+ let acquiredLeaseHere = false;
18856
+ if (this.threadRunLease === null) {
18857
+ const leaseStore = this.getLeaseStore();
18858
+ if (leaseStore) {
18859
+ const lease = await tryAcquireThreadRun({
18860
+ store: leaseStore,
18861
+ tenantId: this.tenant_id,
18862
+ assistantId: this.assistant_id,
18863
+ threadId: this.thread_id,
18864
+ owner: getReplicaId(),
18865
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
18866
+ onLost: () => {
18867
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
18868
+ this.abortController?.abort();
18869
+ }
18870
+ });
18871
+ if (!lease) {
18872
+ this.scheduleThreadRunLeaseRetry();
18873
+ return;
18874
+ }
18875
+ this.threadRunLease = lease;
18876
+ acquiredLeaseHere = true;
18877
+ this.cancelThreadRunLeaseRetry();
18878
+ }
18879
+ }
18880
+ if (this.queueProcessorPromise) return this.queueProcessorPromise;
18881
+ if (this.queueProcessorGeneration !== processorGeneration) {
18882
+ if (acquiredLeaseHere) await this.releaseThreadRunLease();
18883
+ return;
18884
+ }
18885
+ let processorFailed = false;
18494
18886
  const processor = this.runQueueProcessor().catch(async (error) => {
18495
18887
  processorFailed = true;
18496
18888
  console.error(`Queue processing error for thread ${this.thread_id}:`, error);
@@ -18508,7 +18900,11 @@ var Agent = class {
18508
18900
  if (this.queueWakeRequested) void this.startQueueProcessor(false);
18509
18901
  return;
18510
18902
  }
18511
- if (failed) return;
18903
+ if (failed) {
18904
+ this.cancelThreadRunLeaseRetry();
18905
+ await this.releaseThreadRunLease();
18906
+ return;
18907
+ }
18512
18908
  this.queueProcessorRetryAttempt = 0;
18513
18909
  if (this.queueWakeRequested) {
18514
18910
  void this.startQueueProcessor(false);
@@ -18521,7 +18917,11 @@ var Agent = class {
18521
18917
  void this.startQueueProcessor(false);
18522
18918
  return;
18523
18919
  }
18524
- if (pendingMessages.length === 0) return;
18920
+ if (pendingMessages.length === 0) {
18921
+ this.cancelThreadRunLeaseRetry();
18922
+ await this.releaseThreadRunLease();
18923
+ return;
18924
+ }
18525
18925
  const processingMessages = await this.getQueueStore().getProcessingMessages(
18526
18926
  this.thread_id,
18527
18927
  this.queueScope()
@@ -18533,12 +18933,20 @@ var Agent = class {
18533
18933
  }
18534
18934
  if (processingMessages.length > 0) {
18535
18935
  const runStatus = await this.getRunStatus();
18536
- if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) return;
18936
+ if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) {
18937
+ this.cancelThreadRunLeaseRetry();
18938
+ await this.releaseThreadRunLease();
18939
+ return;
18940
+ }
18537
18941
  }
18538
18942
  void this.startQueueProcessor(false);
18539
18943
  } catch (error) {
18540
18944
  console.error(`Queue processor finalization failed for thread ${this.thread_id}:`, error);
18541
18945
  await this.scheduleQueueProcessorRetry(error, generation);
18946
+ if (!this.queueProcessorPromise) {
18947
+ await this.releaseThreadRunLease();
18948
+ this.cancelThreadRunLeaseRetry();
18949
+ }
18542
18950
  }
18543
18951
  }
18544
18952
  async scheduleQueueProcessorRetry(error, generation) {
@@ -18576,6 +18984,36 @@ var Agent = class {
18576
18984
  this.queueProcessorRetryAttempt = 0;
18577
18985
  this.queueWakeRequested = false;
18578
18986
  }
18987
+ /**
18988
+ * Bounded re-attempt after losing the thread-run lease to a peer. Covers the
18989
+ * narrow window where the peer releases the lease without having observed a
18990
+ * just-enqueued message. Bounded so a genuinely long-held thread does not make
18991
+ * this replica poll forever — the owning replica's loop drains new messages.
18992
+ */
18993
+ scheduleThreadRunLeaseRetry() {
18994
+ if (this.threadRunLeaseRetryTimer || this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) return;
18995
+ const delay = Math.min(
18996
+ THREAD_RUN_LEASE_RETRY_BASE_MS * 2 ** this.threadRunLeaseRetryAttempt,
18997
+ THREAD_RUN_LEASE_RETRY_CAP_MS
18998
+ );
18999
+ this.threadRunLeaseRetryAttempt += 1;
19000
+ this.threadRunLeaseRetryTimer = setTimeout(() => {
19001
+ this.threadRunLeaseRetryTimer = void 0;
19002
+ void this.startQueueProcessor(false);
19003
+ }, delay);
19004
+ }
19005
+ /**
19006
+ * Clears any pending thread-run lease retry and resets its backoff budget.
19007
+ *
19008
+ * @remarks Called on lease acquisition and on every definitive processor
19009
+ * stop so the next enqueue starts from the base delay. Never called on
19010
+ * continue/re-arm paths, which must preserve the bounded budget.
19011
+ */
19012
+ cancelThreadRunLeaseRetry() {
19013
+ if (this.threadRunLeaseRetryTimer) clearTimeout(this.threadRunLeaseRetryTimer);
19014
+ this.threadRunLeaseRetryTimer = void 0;
19015
+ this.threadRunLeaseRetryAttempt = 0;
19016
+ }
18579
19017
  async runQueueProcessor() {
18580
19018
  const store = this.getQueueStore();
18581
19019
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
@@ -18720,6 +19158,38 @@ var Agent = class {
18720
19158
  console.error(`[Agent] Failed to initialize agent lattice for ${this.assistant_id} (tenant: ${this.tenant_id}):`, error);
18721
19159
  throw error;
18722
19160
  }
19161
+ let acquiredLeaseHere = false;
19162
+ if (this.threadRunLease === null) {
19163
+ const leaseStore = this.getLeaseStore();
19164
+ if (leaseStore) {
19165
+ const lease = await tryAcquireThreadRun({
19166
+ store: leaseStore,
19167
+ tenantId: this.tenant_id,
19168
+ assistantId: this.assistant_id,
19169
+ threadId: this.thread_id,
19170
+ owner: getReplicaId(),
19171
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
19172
+ onLost: () => {
19173
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
19174
+ this.abortController?.abort();
19175
+ }
19176
+ });
19177
+ if (!lease) {
19178
+ return { status: await this.getRunStatus(), settled: false, leaseContended: true };
19179
+ }
19180
+ this.threadRunLease = lease;
19181
+ acquiredLeaseHere = true;
19182
+ this.cancelThreadRunLeaseRetry();
19183
+ }
19184
+ }
19185
+ try {
19186
+ return await this.resumeTaskLocked();
19187
+ } finally {
19188
+ if (acquiredLeaseHere && !this.queueProcessorPromise) await this.releaseThreadRunLease();
19189
+ }
19190
+ }
19191
+ /** Recovery body of {@link resumeTask}, executed while thread ownership is settled. */
19192
+ async resumeTaskLocked() {
18723
19193
  const store = this.getQueueStore();
18724
19194
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
18725
19195
  const preRunState = await this.getCurrentState();
@@ -18835,6 +19305,8 @@ var Agent = class {
18835
19305
  */
18836
19306
  async abort() {
18837
19307
  this.cancelQueueProcessorRetry();
19308
+ this.cancelThreadRunLeaseRetry();
19309
+ await this.releaseThreadRunLease();
18838
19310
  this.clearRecoveryBlock();
18839
19311
  this.queueProcessorGeneration += 1;
18840
19312
  if (this.abortController) {
@@ -19494,27 +19966,27 @@ function createCheckAsyncTaskTool() {
19494
19966
  if (!mainAgent) {
19495
19967
  return "Cannot access main agent state.";
19496
19968
  }
19497
- const cached = mainAgent.getAsyncTask(task_id);
19498
- if (!cached) {
19969
+ const cached2 = mainAgent.getAsyncTask(task_id);
19970
+ if (!cached2) {
19499
19971
  return `Task ${task_id} not found. Use list_async_tasks to see all tasks.`;
19500
19972
  }
19501
19973
  try {
19502
19974
  if (!agentInstanceManager.hasAgent({
19503
- assistant_id: cached.assistantId,
19975
+ assistant_id: cached2.assistantId,
19504
19976
  thread_id: task_id,
19505
19977
  tenant_id: mainAgent.tenant_id
19506
19978
  })) {
19507
- return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached.status}`;
19979
+ return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached2.status}`;
19508
19980
  }
19509
19981
  const subAgent = agentInstanceManager.getAgent({
19510
- assistant_id: cached.assistantId,
19982
+ assistant_id: cached2.assistantId,
19511
19983
  thread_id: task_id,
19512
19984
  tenant_id: mainAgent.tenant_id
19513
19985
  });
19514
19986
  const status = await subAgent.getRunStatus();
19515
19987
  if (status === "busy") {
19516
19988
  return `Task ${task_id}: still running.
19517
- Description: ${cached.description}`;
19989
+ Description: ${cached2.description}`;
19518
19990
  }
19519
19991
  if (status === "idle") {
19520
19992
  const state = await subAgent.getCurrentState();
@@ -19525,21 +19997,21 @@ Description: ${cached.description}`;
19525
19997
  mainAgent.updateAsyncTaskStatus(task_id, "completed");
19526
19998
  const result = lastAI?.content ?? "(completed, no output)";
19527
19999
  return `Task ${task_id}: COMPLETED.
19528
- Description: ${cached.description}
20000
+ Description: ${cached2.description}
19529
20001
  Result:
19530
20002
  ${result}`;
19531
20003
  }
19532
20004
  if (status === "error") {
19533
20005
  mainAgent.updateAsyncTaskStatus(task_id, "failed");
19534
20006
  return `Task ${task_id}: failed.
19535
- Description: ${cached.description}`;
20007
+ Description: ${cached2.description}`;
19536
20008
  }
19537
20009
  if (status === "interrupted") {
19538
20010
  return `Task ${task_id}: interrupted (waiting for input).
19539
- Description: ${cached.description}`;
20011
+ Description: ${cached2.description}`;
19540
20012
  }
19541
20013
  return `Task ${task_id}: ${status}.
19542
- Description: ${cached.description}`;
20014
+ Description: ${cached2.description}`;
19543
20015
  } catch (err) {
19544
20016
  return `Task ${task_id}: error checking status: ${err instanceof Error ? err.message : String(err)}`;
19545
20017
  }
@@ -19612,25 +20084,25 @@ function createCancelAsyncTaskTool() {
19612
20084
  if (!mainAgent) {
19613
20085
  return "Cannot access main agent state.";
19614
20086
  }
19615
- const cached = mainAgent.getAsyncTask(task_id);
19616
- if (!cached) {
20087
+ const cached2 = mainAgent.getAsyncTask(task_id);
20088
+ if (!cached2) {
19617
20089
  return `Task ${task_id} not found.`;
19618
20090
  }
19619
20091
  try {
19620
20092
  const subAgentExists = agentInstanceManager.hasAgent({
19621
- assistant_id: cached.assistantId,
20093
+ assistant_id: cached2.assistantId,
19622
20094
  thread_id: task_id,
19623
20095
  tenant_id: mainAgent.tenant_id
19624
20096
  });
19625
20097
  if (subAgentExists) {
19626
20098
  const subAgent = agentInstanceManager.getAgent({
19627
- assistant_id: cached.assistantId,
20099
+ assistant_id: cached2.assistantId,
19628
20100
  thread_id: task_id,
19629
20101
  tenant_id: mainAgent.tenant_id
19630
20102
  });
19631
20103
  await subAgent.abort();
19632
20104
  agentInstanceManager.removeAgent({
19633
- assistant_id: cached.assistantId,
20105
+ assistant_id: cached2.assistantId,
19634
20106
  thread_id: task_id,
19635
20107
  tenant_id: mainAgent.tenant_id
19636
20108
  });
@@ -19879,6 +20351,48 @@ var MemoryScheduleStorage = class {
19879
20351
  });
19880
20352
  }
19881
20353
  }
20354
+ /**
20355
+ * Atomically claims one due fire by transitioning the task `pending -> running`.
20356
+ *
20357
+ * @param taskId Task to claim.
20358
+ * @returns True when this caller won the claim (task was `pending`); false if
20359
+ * another replica already claimed it or it is not pending.
20360
+ */
20361
+ async claimScheduledRun(taskId) {
20362
+ const task = this.tasks.get(taskId);
20363
+ if (!task || task.status !== ScheduledTaskStatus.PENDING) {
20364
+ return false;
20365
+ }
20366
+ this.tasks.set(taskId, {
20367
+ ...task,
20368
+ status: ScheduledTaskStatus.RUNNING,
20369
+ updatedAt: Date.now()
20370
+ });
20371
+ return true;
20372
+ }
20373
+ /**
20374
+ * Requeues `running` tasks whose `updatedAt` is older than `olderThanMs` back
20375
+ * to `pending` (recovery after a crash that left a claim unsettled).
20376
+ *
20377
+ * @param olderThanMs Age threshold in milliseconds.
20378
+ * @returns The number of tasks requeued.
20379
+ */
20380
+ async requeueStaleRunning(olderThanMs) {
20381
+ const now = Date.now();
20382
+ const cutoff = now - olderThanMs;
20383
+ let requeued = 0;
20384
+ for (const [taskId, task] of this.tasks.entries()) {
20385
+ if (task.status === ScheduledTaskStatus.RUNNING && task.updatedAt < cutoff) {
20386
+ this.tasks.set(taskId, {
20387
+ ...task,
20388
+ status: ScheduledTaskStatus.PENDING,
20389
+ updatedAt: now
20390
+ });
20391
+ requeued++;
20392
+ }
20393
+ }
20394
+ return requeued;
20395
+ }
19882
20396
  /**
19883
20397
  * Delete task
19884
20398
  */
@@ -20243,6 +20757,7 @@ function describeCronExpression(expression) {
20243
20757
  }
20244
20758
 
20245
20759
  // src/schedule_lattice/DefaultScheduleClient.ts
20760
+ var STALE_RUNNING_MS = 30 * 6e4;
20246
20761
  var _DefaultScheduleClient = class _DefaultScheduleClient {
20247
20762
  constructor(storage) {
20248
20763
  this.handlers = /* @__PURE__ */ new Map();
@@ -20483,6 +20998,19 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20483
20998
  }
20484
20999
  // ===== Recovery =====
20485
21000
  async restore() {
21001
+ try {
21002
+ const requeued = await this.storage.requeueStaleRunning(STALE_RUNNING_MS);
21003
+ if (requeued > 0) {
21004
+ console.log(
21005
+ `[Scheduler] Requeued ${requeued} stale running task(s)`
21006
+ );
21007
+ }
21008
+ } catch (error) {
21009
+ console.error(
21010
+ "[Scheduler] Failed to requeue stale running tasks:",
21011
+ error
21012
+ );
21013
+ }
20486
21014
  const activeTasks = await this.storage.getActiveTasks();
20487
21015
  let restored = 0;
20488
21016
  for (const task of activeTasks) {
@@ -20543,6 +21071,16 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20543
21071
  });
20544
21072
  }
20545
21073
  async executeTask(task) {
21074
+ let claimed = false;
21075
+ try {
21076
+ claimed = await this.storage.claimScheduledRun(task.taskId);
21077
+ } catch (error) {
21078
+ console.error(`[Scheduler] Failed to claim task ${task.taskId}:`, error);
21079
+ return;
21080
+ }
21081
+ if (!claimed) {
21082
+ return;
21083
+ }
20546
21084
  const handler = this.handlers.get(task.taskType);
20547
21085
  if (!handler) {
20548
21086
  console.error(
@@ -20554,9 +21092,6 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20554
21092
  });
20555
21093
  return;
20556
21094
  }
20557
- await this.storage.update(task.taskId, {
20558
- status: ScheduledTaskStatus2.RUNNING
20559
- });
20560
21095
  console.log(`[Scheduler] Executing task: ${task.taskId}`);
20561
21096
  try {
20562
21097
  const latestTask = await this.storage.get(task.taskId);
@@ -25270,8 +25805,8 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
25270
25805
  * @param key Provider key name
25271
25806
  * @param provider VectorStoreProvider implementation
25272
25807
  */
25273
- registerProvider(key5, provider) {
25274
- const lattice = { key: key5, provider };
25808
+ registerProvider(key5, provider2) {
25809
+ const lattice = { key: key5, provider: provider2 };
25275
25810
  this.registerWithTenant("default", key5, lattice);
25276
25811
  }
25277
25812
  /**
@@ -25307,7 +25842,7 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
25307
25842
  };
25308
25843
  var vectorStoreProviderManager = VectorStoreProviderManager.getInstance();
25309
25844
  vectorStoreProviderManager.registerProvider("default", new MemoryVectorStoreProvider());
25310
- var registerVectorStoreProvider = (key5, provider) => vectorStoreProviderManager.registerProvider(key5, provider);
25845
+ var registerVectorStoreProvider = (key5, provider2) => vectorStoreProviderManager.registerProvider(key5, provider2);
25311
25846
  var getVectorStoreProvider = (key5) => vectorStoreProviderManager.getProvider(key5);
25312
25847
 
25313
25848
  // src/store_lattice/configureStores.ts
@@ -28009,7 +28544,110 @@ independently before the workflow's integration eval.
28009
28544
 
28010
28545
  An unsupported architecture conversion interrupts the flow and requests a new
28011
28546
  user decision. Do not route backward to the Charter or to [[agent-build]], and do
28012
- not create a replacement.`
28547
+ not create a replacement.`,
28548
+ "ptc": `---
28549
+ name: ptc
28550
+ 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.
28551
+ metadata:
28552
+ role: workflow
28553
+ domain: orchestration
28554
+ verified: unverified
28555
+ ---
28556
+
28557
+ # Programmatic Tool Calling (PTC)
28558
+
28559
+ PTC means the model writes and runs code that calls platform tools itself, instead
28560
+ of calling tools one at a time. Reach for it when orchestration is multi-step,
28561
+ data-heavy, or needs a loop/branch that would be wasteful as a chain of model tool
28562
+ calls.
28563
+
28564
+ ## Where the code runs
28565
+
28566
+ Run the code with \`code_eval\` (backed by \`shell_exec\`). Each execution process is
28567
+ injected with two environment variables:
28568
+
28569
+ - \`API_GATEWAY_URL\` \u2014 the gateway base URL.
28570
+ - \`OPEN_API_TOKEN\` \u2014 a short-lived bearer token **self-bound to this agent**. It is
28571
+ not reusable elsewhere.
28572
+
28573
+ The token already encodes this agent's authority: the platform reflects the agent's
28574
+ own configured middleware into Open grants, plus a small always-on baseline
28575
+ (\`kb\` read-only and \`sandbox:self\`). So the callable set = that baseline \u222A the
28576
+ domains your configured middleware exposes \u2014 and nothing beyond it.
28577
+
28578
+ ## Client
28579
+
28580
+ The sandbox must have the zero-dependency \`@axiom-lattice/open-mcp-client\` package.
28581
+ Get it one of two ways:
28582
+
28583
+ - **Preinstalled in the sandbox image** (recommended): \`open-mcp\` is on \`PATH\`; for
28584
+ \`require("@axiom-lattice/open-mcp-client")\` to resolve, install it into the working
28585
+ directory or point \`NODE_PATH\` at the global \`node_modules\`.
28586
+ - **Install it in this run** when the sandbox has network + npm:
28587
+
28588
+ \`\`\`bash
28589
+ npm install @axiom-lattice/open-mcp-client
28590
+ # or use without installing:
28591
+ npx @axiom-lattice/open-mcp-client tools list --json
28592
+ \`\`\`
28593
+
28594
+ \`\`\`js
28595
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28596
+ const client = OpenMcpClient.fromEnv(); // reads API_GATEWAY_URL + OPEN_API_TOKEN
28597
+ \`\`\`
28598
+
28599
+ CLI equivalents (config comes from the same env vars):
28600
+
28601
+ \`\`\`bash
28602
+ open-mcp tools list --json
28603
+ open-mcp tools call kb_search --json '{"query":"refund policy"}'
28604
+ \`\`\`
28605
+
28606
+ ## Discover, then call
28607
+
28608
+ 1. **Discover first.** Call \`await client.listTools()\`. This returns exactly the
28609
+ tools this agent is authorized to call (the server filters by the token's grants
28610
+ on every request, so it is the source of truth).
28611
+ 2. **Only call discovered tools.** Pick a tool from that list and call
28612
+ \`await client.callTool(name, args)\` with args matching the tool's \`inputSchema\`.
28613
+ 3. **Print the final result as JSON to stdout** so the calling agent can consume it.
28614
+ Keep intermediate logs minimal.
28615
+
28616
+ An unconfigured domain isn't registered, so calling it returns an \`isError\`
28617
+ tool-not-found result; a registered tool refused by the token's grants surfaces
28618
+ \`GRANT_DENIED\`. Either way the capability is **not configured for this agent**.
28619
+ Do not retry or work around it \u2014 drop that step or tell the user which
28620
+ middleware/domain must be enabled. A denied call is expected behavior, not a
28621
+ transient failure.
28622
+
28623
+ ## Tips
28624
+
28625
+ - Fan out with \`Promise.all\` over many items instead of many sequential model turns;
28626
+ that is the main win of PTC.
28627
+ - Inspect \`tool.inputSchema\` before calling to avoid schema-validation errors.
28628
+ - Reduce large results in code and return only the fields the agent needs.
28629
+ - The token is short-lived; for very long jobs re-read the env per \`code_eval\` run
28630
+ rather than caching the token in a file.
28631
+
28632
+ ## Worked example
28633
+
28634
+ \`\`\`js
28635
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28636
+
28637
+ async function main() {
28638
+ const client = OpenMcpClient.fromEnv(); // API_GATEWAY_URL + OPEN_API_TOKEN
28639
+ const tools = await client.listTools(); // exactly what this agent may call
28640
+ const pick = tools.find((t) => t.name === "kb_search") ?? tools[0];
28641
+ const out = await client.callTool(pick.name, { query: "refund policy" });
28642
+ console.log(JSON.stringify({ called: pick.name, result: out.json() }));
28643
+ }
28644
+
28645
+ main().catch((err) => { console.error(String(err)); process.exit(1); });
28646
+ \`\`\`
28647
+
28648
+ Run this in \`code_eval\`. The first run proves env + client wiring; then extend it
28649
+ into the real fan-out/aggregation the task needs.
28650
+ `
28013
28651
  };
28014
28652
  function getBuiltInSkillMeta(name) {
28015
28653
  const content = BUILTIN_SKILLS[name];
@@ -28606,7 +29244,7 @@ ${body}` : `${frontmatter}
28606
29244
  };
28607
29245
 
28608
29246
  // src/store_lattice/InMemoryMenuStore.ts
28609
- import { randomUUID as randomUUID6 } from "crypto";
29247
+ import { randomUUID as randomUUID7 } from "crypto";
28610
29248
  var InMemoryMenuStore = class {
28611
29249
  constructor() {
28612
29250
  this.items = /* @__PURE__ */ new Map();
@@ -28627,7 +29265,7 @@ var InMemoryMenuStore = class {
28627
29265
  async create(input) {
28628
29266
  const now = /* @__PURE__ */ new Date();
28629
29267
  const item = {
28630
- id: randomUUID6(),
29268
+ id: randomUUID7(),
28631
29269
  tenantId: input.tenantId,
28632
29270
  menuTarget: input.menuTarget,
28633
29271
  group: input.group,
@@ -31139,9 +31777,9 @@ async function getOrCreateCollectionVectorStore(name, embeddingKey, tenantId2, p
31139
31777
  const pendingInitialization = pendingVectorStoreInitializations.get(storeKey);
31140
31778
  if (pendingInitialization) return pendingInitialization;
31141
31779
  const initialization = (async () => {
31142
- const provider = vectorStoreProviderManager.getProvider(providerKey);
31780
+ const provider2 = vectorStoreProviderManager.getProvider(providerKey);
31143
31781
  const tableName = buildTableName(tenantId2, name);
31144
- const vectorStore = await provider.create({ tableName, embeddingKey });
31782
+ const vectorStore = await provider2.create({ tableName, embeddingKey });
31145
31783
  vectorStoreLatticeManager.registerLattice(storeKey, vectorStore);
31146
31784
  return vectorStore;
31147
31785
  })();
@@ -31159,9 +31797,9 @@ async function getCollectionEntryCount(name, embeddingKey, tenantId2) {
31159
31797
  return vs.getDocumentCount();
31160
31798
  }
31161
31799
  try {
31162
- const provider = vectorStoreProviderManager.getProvider("default");
31163
- if (provider.getEntryCount) {
31164
- return provider.getEntryCount(buildTableName(tenantId2, name));
31800
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31801
+ if (provider2.getEntryCount) {
31802
+ return provider2.getEntryCount(buildTableName(tenantId2, name));
31165
31803
  }
31166
31804
  } catch {
31167
31805
  }
@@ -31173,9 +31811,9 @@ async function listCollectionEntries(name, embeddingKey, tenantId2) {
31173
31811
  return vs.getAllDocuments();
31174
31812
  }
31175
31813
  try {
31176
- const provider = vectorStoreProviderManager.getProvider("default");
31177
- if (provider.listEntries) {
31178
- return provider.listEntries(buildTableName(tenantId2, name));
31814
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31815
+ if (provider2.listEntries) {
31816
+ return provider2.listEntries(buildTableName(tenantId2, name));
31179
31817
  }
31180
31818
  } catch {
31181
31819
  }
@@ -31185,9 +31823,9 @@ async function removeCollectionVectorStore(name, tenantId2) {
31185
31823
  const storeKey = buildKey(tenantId2, name);
31186
31824
  const tableName = buildTableName(tenantId2, name);
31187
31825
  try {
31188
- const provider = vectorStoreProviderManager.getProvider("default");
31189
- if (provider.dropTable) {
31190
- await provider.dropTable(tableName);
31826
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31827
+ if (provider2.dropTable) {
31828
+ await provider2.dropTable(tableName);
31191
31829
  }
31192
31830
  } catch {
31193
31831
  }
@@ -33502,7 +34140,7 @@ var DaytonaProvider = class {
33502
34140
 
33503
34141
  // src/sandbox_lattice/providers/LocalSandboxProvider.ts
33504
34142
  import * as path6 from "path";
33505
- import * as os from "os";
34143
+ import * as os2 from "os";
33506
34144
  import * as fs4 from "fs/promises";
33507
34145
 
33508
34146
  // src/sandbox_lattice/LocalSandboxInstance.ts
@@ -33529,6 +34167,32 @@ function assertRegularDeleteTarget(file, stat4) {
33529
34167
  function quoteShellArgument(value) {
33530
34168
  return `'${value.replace(/'/g, "'\\''")}'`;
33531
34169
  }
34170
+ var PLATFORM_SECRET_ENV_VARS = [
34171
+ "OPEN_TOKEN_SECRET",
34172
+ "AUTH_TOKEN_SECRET",
34173
+ "JWT_SECRET",
34174
+ "WEB_APP_ATTACHMENT_SECRET",
34175
+ "LATTICE_ENCRYPTION_KEY",
34176
+ "DATABASE_URL",
34177
+ "REDIS_PASSWORD",
34178
+ "MICROSANDBOX_API_KEY",
34179
+ "DAYTONA_API_KEY",
34180
+ "E2B_API_KEY",
34181
+ "OPENAI_API_KEY",
34182
+ "AZURE_OPENAI_API_KEY",
34183
+ "ANTHROPIC_API_KEY",
34184
+ "TAVILY_API_KEY",
34185
+ "VAPID_PRIVATE_KEY",
34186
+ "SUPABASE_KEY",
34187
+ "A2A_API_KEYS"
34188
+ ];
34189
+ function stripPlatformSecrets(env) {
34190
+ const rest = { ...env };
34191
+ for (const name of PLATFORM_SECRET_ENV_VARS) {
34192
+ delete rest[name];
34193
+ }
34194
+ return rest;
34195
+ }
33532
34196
  function validatePackEntryName(name) {
33533
34197
  if (!name || name === "." || name === ".." || name.includes("\\") || name.includes("/")) {
33534
34198
  throw new Error(`Cannot pack: unsafe entry name '${name}'`);
@@ -33701,7 +34365,7 @@ var LocalSandboxInstance = class {
33701
34365
  timeout: timeoutMs,
33702
34366
  maxBuffer: 10 * 1024 * 1024,
33703
34367
  // 10MB
33704
- env: { ...process.env, HOME: this.rootDir }
34368
+ env: stripPlatformSecrets({ ...process.env, HOME: this.rootDir })
33705
34369
  }
33706
34370
  );
33707
34371
  const output = stderr ? `${stdout}
@@ -33966,7 +34630,7 @@ var LocalSandboxProvider = class {
33966
34630
  this.instances = /* @__PURE__ */ new Map();
33967
34631
  this.creating = /* @__PURE__ */ new Map();
33968
34632
  this.createResourceAddress = createResourceAddress;
33969
- this.basePath = config.basePath ?? path6.join(os.homedir(), ".axiom-local-sandbox");
34633
+ this.basePath = config.basePath ?? path6.join(os2.homedir(), ".axiom-local-sandbox");
33970
34634
  }
33971
34635
  async createSandbox(name, _config) {
33972
34636
  const existing = this.instances.get(name);
@@ -35342,6 +36006,10 @@ var TaskLifecycleNotifier = class {
35342
36006
  this.listeners = /* @__PURE__ */ new Set();
35343
36007
  this.tails = /* @__PURE__ */ new Map();
35344
36008
  }
36009
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
36010
+ setOutbox(outbox2) {
36011
+ this.outbox = outbox2;
36012
+ }
35345
36013
  /**
35346
36014
  * Registers an async lifecycle listener.
35347
36015
  *
@@ -35362,6 +36030,25 @@ var TaskLifecycleNotifier = class {
35362
36030
  * @throws Error when the event is malformed or one or more listeners reject.
35363
36031
  */
35364
36032
  async publish(event) {
36033
+ const snapshot = snapshotNotification(event);
36034
+ const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
36035
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36036
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot));
36037
+ this.tails.set(key5, delivery);
36038
+ if (this.outbox) {
36039
+ try {
36040
+ await this.outbox.append({ topic: "task-lifecycle", payload: snapshot, originReplicaId: getReplicaId() });
36041
+ } catch {
36042
+ }
36043
+ }
36044
+ try {
36045
+ await delivery;
36046
+ } finally {
36047
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36048
+ }
36049
+ }
36050
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36051
+ async deliverFromOutbox(event) {
35365
36052
  const snapshot = snapshotNotification(event);
35366
36053
  const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
35367
36054
  const previous = this.tails.get(key5) ?? Promise.resolve();
@@ -35426,6 +36113,10 @@ var ProjectTaskAdmissionNotifier = class {
35426
36113
  if (!Number.isSafeInteger(timeout) || timeout <= 0) throw new Error("Invalid admission listener timeout");
35427
36114
  this.listenerTimeoutMs = timeout;
35428
36115
  }
36116
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
36117
+ setOutbox(outbox2) {
36118
+ this.outbox = outbox2;
36119
+ }
35429
36120
  /**
35430
36121
  * Registers a Project task admission listener.
35431
36122
  *
@@ -35445,6 +36136,30 @@ var ProjectTaskAdmissionNotifier = class {
35445
36136
  * @returns A promise settled after every snapshotted listener settles.
35446
36137
  */
35447
36138
  async publish(event) {
36139
+ const snapshot = snapshotNotification2(event);
36140
+ const listeners = [...this.listeners];
36141
+ const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
36142
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36143
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot, listeners));
36144
+ this.tails.set(key5, delivery);
36145
+ if (this.outbox) {
36146
+ try {
36147
+ await this.outbox.append({
36148
+ topic: "project-task-admission",
36149
+ payload: snapshot,
36150
+ originReplicaId: getReplicaId()
36151
+ });
36152
+ } catch {
36153
+ }
36154
+ }
36155
+ try {
36156
+ await delivery;
36157
+ } finally {
36158
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36159
+ }
36160
+ }
36161
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36162
+ async deliverFromOutbox(event) {
35448
36163
  const snapshot = snapshotNotification2(event);
35449
36164
  const listeners = [...this.listeners];
35450
36165
  const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
@@ -35514,8 +36229,137 @@ function canonicalTimestamp(value) {
35514
36229
  return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
35515
36230
  }
35516
36231
 
36232
+ // src/services/OutboxPoller.ts
36233
+ var OutboxPoller = class {
36234
+ /**
36235
+ * Creates a poller bound to one durable outbox.
36236
+ *
36237
+ * @param deps Store, owner replica id, topic handlers, and optional tuning.
36238
+ */
36239
+ constructor(deps) {
36240
+ this.deps = deps;
36241
+ this.cursor = null;
36242
+ this.polling = false;
36243
+ }
36244
+ /** Captures the starting cursor and begins polling. */
36245
+ async start() {
36246
+ this.cursor = await this.deps.store.latestId();
36247
+ this.timer = setInterval(() => {
36248
+ void this.pollOnce();
36249
+ }, this.deps.intervalMs ?? 1e3);
36250
+ this.timer.unref?.();
36251
+ }
36252
+ /** Stops polling. */
36253
+ stop() {
36254
+ if (this.timer) clearInterval(this.timer);
36255
+ this.timer = void 0;
36256
+ }
36257
+ /** Runs one drain pass (exported for tests). */
36258
+ async pollOnce() {
36259
+ if (this.polling) return;
36260
+ this.polling = true;
36261
+ try {
36262
+ const batchSize = this.deps.batchSize ?? 100;
36263
+ for (; ; ) {
36264
+ const events = await this.deps.store.listAfter(this.cursor, batchSize);
36265
+ if (events.length === 0) break;
36266
+ for (const event of events) {
36267
+ this.cursor = event.id;
36268
+ if (event.originReplicaId === this.deps.owner) continue;
36269
+ const handler = this.deps.handlers[event.topic];
36270
+ if (!handler) continue;
36271
+ try {
36272
+ await this.deliverWithTimeout(handler, event.payload);
36273
+ } catch {
36274
+ this.deps.log?.warn("Outbox delivery failed", { topic: event.topic, id: event.id });
36275
+ }
36276
+ }
36277
+ if (events.length < batchSize) break;
36278
+ }
36279
+ } catch (error) {
36280
+ this.deps.log?.warn("Outbox poll failed", { error: error instanceof Error ? error.message : String(error) });
36281
+ } finally {
36282
+ this.polling = false;
36283
+ }
36284
+ }
36285
+ /**
36286
+ * Runs one handler bounded by `handlerTimeoutMs` so a hung listener cannot
36287
+ * stall cross-replica delivery for this replica.
36288
+ */
36289
+ async deliverWithTimeout(handler, payload) {
36290
+ const timeoutMs = this.deps.handlerTimeoutMs ?? 5e3;
36291
+ let timer;
36292
+ const timeout = new Promise((_resolve, reject) => {
36293
+ timer = setTimeout(() => reject(new Error("Outbox handler timed out")), timeoutMs);
36294
+ timer.unref?.();
36295
+ });
36296
+ try {
36297
+ await Promise.race([Promise.resolve().then(() => handler(payload)), timeout]);
36298
+ } finally {
36299
+ if (timer) clearTimeout(timer);
36300
+ }
36301
+ }
36302
+ };
36303
+
35517
36304
  // src/services/ProjectMutationMutex.ts
35518
36305
  import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
36306
+
36307
+ // src/services/coordinationLease.ts
36308
+ var configured;
36309
+ function configureCoordinationLeaseStore(store) {
36310
+ configured = store;
36311
+ }
36312
+ function getCoordinationLeaseStore() {
36313
+ return configured;
36314
+ }
36315
+
36316
+ // src/services/withLease.ts
36317
+ import { randomUUID as randomUUID8 } from "crypto";
36318
+ var LeaseAcquisitionTimeoutError = class extends Error {
36319
+ /**
36320
+ * @param key Lease key that could not be acquired.
36321
+ * @param holder Unique holder that attempted acquisition.
36322
+ * @param timeoutMs How long the caller waited before giving up.
36323
+ */
36324
+ constructor(key5, holder, timeoutMs) {
36325
+ super(`Could not acquire lease '${key5}' for '${holder}' within ${timeoutMs}ms`);
36326
+ this.key = key5;
36327
+ this.holder = holder;
36328
+ this.timeoutMs = timeoutMs;
36329
+ /** Stable machine-readable code. */
36330
+ this.code = "LEASE_ACQUISITION_TIMEOUT";
36331
+ this.name = "LeaseAcquisitionTimeoutError";
36332
+ }
36333
+ };
36334
+ function sleep(ms) {
36335
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
36336
+ }
36337
+ async function withLease(store, key5, ownerLabel, fn, options = {}) {
36338
+ const ttlMs = options.ttlMs ?? 3e4;
36339
+ const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
36340
+ const retryDelayMs = options.retryDelayMs ?? 50;
36341
+ const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
36342
+ const holder = `${ownerLabel}#${randomUUID8()}`;
36343
+ const deadline = Date.now() + acquireTimeoutMs;
36344
+ for (; ; ) {
36345
+ if (await store.tryAcquire(key5, holder, ttlMs)) break;
36346
+ if (Date.now() >= deadline) throw new LeaseAcquisitionTimeoutError(key5, holder, acquireTimeoutMs);
36347
+ await sleep(retryDelayMs);
36348
+ }
36349
+ const heartbeat = setInterval(() => {
36350
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
36351
+ }, heartbeatMs);
36352
+ heartbeat.unref?.();
36353
+ try {
36354
+ return await fn();
36355
+ } finally {
36356
+ clearInterval(heartbeat);
36357
+ await store.release(key5, holder).catch(() => void 0);
36358
+ }
36359
+ }
36360
+
36361
+ // src/services/ProjectMutationMutex.ts
36362
+ var MUTATION_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
35519
36363
  function projectKey(scope) {
35520
36364
  const row = snapshotExactRecord4(scope, ["tenantId", "projectId"]);
35521
36365
  if (!row || typeof row.tenantId !== "string" || !row.tenantId || row.tenantId.includes("\0") || typeof row.projectId !== "string" || !row.projectId || row.projectId.includes("\0")) {
@@ -35531,9 +36375,12 @@ var ProjectMutationMutex = class {
35531
36375
  /** Runs one mutation after all earlier mutations for the same Tenant and Project. */
35532
36376
  async runExclusive(scope, operation, signal) {
35533
36377
  const key5 = projectKey(scope);
36378
+ const leaseKey = `project-mutation:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.projectId)}`;
35534
36379
  await this.acquire(key5, signal);
35535
36380
  try {
35536
- return await operation();
36381
+ const store = getCoordinationLeaseStore();
36382
+ if (!store) return await operation();
36383
+ return await withLease(store, leaseKey, getReplicaId(), operation, MUTATION_LEASE_OPTIONS);
35537
36384
  } finally {
35538
36385
  this.release(key5);
35539
36386
  }
@@ -35591,7 +36438,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
35591
36438
  }
35592
36439
 
35593
36440
  // src/services/RoomAgentMessageService.ts
35594
- import { randomUUID as randomUUID7 } from "crypto";
36441
+ import { randomUUID as randomUUID9 } from "crypto";
35595
36442
  import { snapshotExactArray, snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
35596
36443
  var INPUT_KEYS = [
35597
36444
  "tenantId",
@@ -35679,7 +36526,7 @@ var RoomAgentMessageService = class {
35679
36526
  throw new Error("Invalid project room membership");
35680
36527
  }
35681
36528
  const candidate = {
35682
- id: (this.deps.idFactory ?? randomUUID7)(),
36529
+ id: (this.deps.idFactory ?? randomUUID9)(),
35683
36530
  tenantId: input.tenantId,
35684
36531
  workspaceId: input.workspaceId,
35685
36532
  projectId: input.projectId,
@@ -36040,9 +36887,9 @@ var BELIEF_OWNER_WRITE_ATTEMPTS = 3;
36040
36887
  function isWellFormedImpact(impact) {
36041
36888
  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
36889
  }
36043
- async function applyBeliefImpacts(taskStore, owner, impacts) {
36044
- if (impacts.length === 0) return { description: owner.description ?? "" };
36045
- let current = owner;
36890
+ async function applyBeliefImpacts(taskStore, owner2, impacts) {
36891
+ if (impacts.length === 0) return { description: owner2.description ?? "" };
36892
+ let current = owner2;
36046
36893
  for (let attempt = 0; attempt < BELIEF_OWNER_WRITE_ATTEMPTS; attempt += 1) {
36047
36894
  const parsed = parseTaskBeliefState(current.description ?? "");
36048
36895
  const existingTable = parsed.success;
@@ -36171,14 +37018,14 @@ function sameBeliefOwner(left, right) {
36171
37018
  function sameBeliefOwnerIdentity(left, right) {
36172
37019
  return canonicalJson({ ...left, beliefState: void 0 }) === canonicalJson({ ...right, beliefState: void 0 });
36173
37020
  }
36174
- function canonicalBeliefOwner(owner) {
37021
+ function canonicalBeliefOwner(owner2) {
36175
37022
  const beliefState = {
36176
- entries: owner.beliefState.entries.map((entry) => ({
37023
+ entries: owner2.beliefState.entries.map((entry) => ({
36177
37024
  ...entry,
36178
37025
  basis: entry.basis.trim().replace(/\s+/g, " ")
36179
37026
  })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0)
36180
37027
  };
36181
- return { ...owner, beliefState };
37028
+ return { ...owner2, beliefState };
36182
37029
  }
36183
37030
  function eventKeyFor(taskId, result, impacts, beliefOwner) {
36184
37031
  const base = `belief-impact:${taskId}:${buildBeliefCompletionEventKey(taskId, result, impacts)}`;
@@ -37545,8 +38392,8 @@ var TaskLifecycleService = class {
37545
38392
  return { task: existing, parent: parentResult, eventKey, evidence, result, impacts };
37546
38393
  }
37547
38394
  beliefOwnerSnapshot(task, parent) {
37548
- const owner = parent?.ownerType === "agent" ? parent : task;
37549
- const parsed = parseTaskBeliefState(owner.description ?? "");
38395
+ const owner2 = parent?.ownerType === "agent" ? parent : task;
38396
+ const parsed = parseTaskBeliefState(owner2.description ?? "");
37550
38397
  const beliefState = parsed.success ? parsed.state : { entries: [] };
37551
38398
  if (parent?.ownerType === "agent") {
37552
38399
  return {
@@ -38266,7 +39113,7 @@ function createTaskLifecycleService(deps) {
38266
39113
  }
38267
39114
 
38268
39115
  // src/services/TaskExecutionService.ts
38269
- import { randomUUID as randomUUID8 } from "crypto";
39116
+ import { randomUUID as randomUUID11 } from "crypto";
38270
39117
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
38271
39118
  import { isExecutionResultEventKey as isExecutionResultEventKey2 } from "@axiom-lattice/protocols";
38272
39119
 
@@ -38525,12 +39372,20 @@ var TaskRunRegistry = class {
38525
39372
  }
38526
39373
  };
38527
39374
  var taskRunRegistry = new TaskRunRegistry();
39375
+ async function safelyLive(probe, task) {
39376
+ try {
39377
+ return await probe(task);
39378
+ } catch {
39379
+ return true;
39380
+ }
39381
+ }
38528
39382
  async function reconcileStaleTaskRuns(input) {
38529
39383
  const registry4 = input.registry ?? taskRunRegistry;
38530
39384
  const converged = [];
38531
39385
  const convergedTasks = [];
38532
39386
  for (const task of input.tasks) {
38533
39387
  if (task.status !== "in_progress" || registry4.isLive(task.id)) continue;
39388
+ if (input.isTaskLive && await safelyLive(input.isTaskLive, task)) continue;
38534
39389
  const result = await input.lifecycle.failTask({
38535
39390
  tenantId: task.tenantId,
38536
39391
  taskId: task.id,
@@ -38551,7 +39406,8 @@ async function reconcileTenantTaskRuns(input) {
38551
39406
  tasks: candidates,
38552
39407
  actor: input.actor,
38553
39408
  lifecycle: input.lifecycle,
38554
- ...input.registry ? { registry: input.registry } : {}
39409
+ ...input.registry ? { registry: input.registry } : {},
39410
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
38555
39411
  });
38556
39412
  }
38557
39413
  async function reconcileAllTenantTaskRuns(input) {
@@ -38567,7 +39423,8 @@ async function reconcileAllTenantTaskRuns(input) {
38567
39423
  taskStore: input.taskStore,
38568
39424
  lifecycle: input.lifecycle,
38569
39425
  ...input.registry ? { registry: input.registry } : {},
38570
- ...input.include ? { include: input.include } : {}
39426
+ ...input.include ? { include: input.include } : {},
39427
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
38571
39428
  });
38572
39429
  } catch {
38573
39430
  continue;
@@ -38578,6 +39435,42 @@ async function reconcileAllTenantTaskRuns(input) {
38578
39435
  return { converged, convergedTasks };
38579
39436
  }
38580
39437
 
39438
+ // src/services/taskRunLease.ts
39439
+ import { randomUUID as randomUUID10 } from "crypto";
39440
+ function taskRunLeaseKey(taskId) {
39441
+ return `task-run:${taskId}`;
39442
+ }
39443
+ async function tryAcquireTaskRun(input) {
39444
+ const key5 = taskRunLeaseKey(input.taskId);
39445
+ const holder = `${input.owner}#${randomUUID10()}`;
39446
+ if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
39447
+ const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
39448
+ let released = false;
39449
+ let lost = false;
39450
+ const heartbeat = setInterval(() => {
39451
+ void input.store.renew(key5, holder, input.ttlMs).then((renewed) => {
39452
+ if (released || renewed || lost) return;
39453
+ lost = true;
39454
+ try {
39455
+ input.onLost?.();
39456
+ } catch {
39457
+ }
39458
+ }).catch(() => void 0);
39459
+ }, heartbeatMs);
39460
+ heartbeat.unref?.();
39461
+ return {
39462
+ get lost() {
39463
+ return lost;
39464
+ },
39465
+ release: async () => {
39466
+ if (released) return;
39467
+ released = true;
39468
+ clearInterval(heartbeat);
39469
+ await input.store.release(key5, holder).catch(() => void 0);
39470
+ }
39471
+ };
39472
+ }
39473
+
38581
39474
  // src/services/ExecutionAuthorityRegistry.ts
38582
39475
  var ManagedTaskStartError = class extends Error {
38583
39476
  /**
@@ -38848,6 +39741,14 @@ async function prepareTaskDispatch(deps, input) {
38848
39741
 
38849
39742
  // src/services/TaskExecutionService.ts
38850
39743
  import { GraphInterrupt as GraphInterrupt2 } from "@langchain/langgraph";
39744
+ var TASK_RUN_LEASE_TTL_MS = 12e4;
39745
+ function getLeaseStore() {
39746
+ try {
39747
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
39748
+ } catch {
39749
+ return void 0;
39750
+ }
39751
+ }
38851
39752
  function persistedExecutionObservation(item, attempted) {
38852
39753
  const detail = item.detail;
38853
39754
  const persistedObservation = toJsonSafeValue(detail?.observation);
@@ -38908,11 +39809,11 @@ function createTaskExecutionService(deps) {
38908
39809
  let threadId;
38909
39810
  let runId;
38910
39811
  try {
38911
- threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID8)();
39812
+ threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID11)();
38912
39813
  if (task.status === "interrupted" && reuseCurrentRun) {
38913
39814
  runId = storedRunId;
38914
39815
  } else if (task.status === "interrupted" || task.status === "failed") {
38915
- runId = (deps.createRunId ?? randomUUID8)();
39816
+ runId = (deps.createRunId ?? randomUUID11)();
38916
39817
  }
38917
39818
  } catch (error) {
38918
39819
  return {
@@ -38975,7 +39876,21 @@ function createTaskExecutionService(deps) {
38975
39876
  warnings.push(...begun.warnings);
38976
39877
  }
38977
39878
  taskRunRegistry.register(task.id);
39879
+ let taskRunLease = null;
38978
39880
  try {
39881
+ const leaseStore = getLeaseStore();
39882
+ if (leaseStore) {
39883
+ try {
39884
+ taskRunLease = await tryAcquireTaskRun({
39885
+ store: leaseStore,
39886
+ taskId: task.id,
39887
+ owner: getReplicaId(),
39888
+ ttlMs: TASK_RUN_LEASE_TTL_MS
39889
+ });
39890
+ } catch {
39891
+ taskRunLease = null;
39892
+ }
39893
+ }
38979
39894
  const invocation = {
38980
39895
  tenantId: tenantId2,
38981
39896
  workspaceId: task.workspaceId,
@@ -39143,6 +40058,7 @@ function createTaskExecutionService(deps) {
39143
40058
  }
39144
40059
  } finally {
39145
40060
  taskRunRegistry.unregister(task.id);
40061
+ if (taskRunLease) await taskRunLease.release();
39146
40062
  }
39147
40063
  };
39148
40064
  return {
@@ -39155,6 +40071,43 @@ function createTaskExecutionService(deps) {
39155
40071
  };
39156
40072
  }
39157
40073
 
40074
+ // src/services/leadership.ts
40075
+ import { randomUUID as randomUUID12 } from "crypto";
40076
+ async function runAsLeader(store, name, fn, options = {}) {
40077
+ const key5 = `leader:${name}`;
40078
+ const owner2 = options.owner ?? getReplicaId();
40079
+ const holder = `${owner2}#${randomUUID12()}`;
40080
+ const ttlMs = options.ttlMs ?? 6e4;
40081
+ if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
40082
+ const heartbeat = setInterval(() => {
40083
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
40084
+ }, Math.max(1e3, Math.floor(ttlMs / 3)));
40085
+ heartbeat.unref?.();
40086
+ try {
40087
+ const result = await fn();
40088
+ return { ran: true, result };
40089
+ } finally {
40090
+ clearInterval(heartbeat);
40091
+ if (!options.hold) await store.release(key5, holder).catch(() => void 0);
40092
+ }
40093
+ }
40094
+
40095
+ // src/services/pluginCatalogSync.ts
40096
+ var PLUGIN_CATALOG_CHANGED_TOPIC = "plugin-catalog-changed";
40097
+ var outbox;
40098
+ var owner = "unknown";
40099
+ function configurePluginCatalogSync(next) {
40100
+ outbox = next?.outbox;
40101
+ owner = next?.owner ?? "unknown";
40102
+ }
40103
+ async function publishPluginCatalogChanged(tenantId2) {
40104
+ if (!outbox || !tenantId2) return;
40105
+ try {
40106
+ await outbox.append({ topic: PLUGIN_CATALOG_CHANGED_TOPIC, payload: { tenantId: tenantId2 }, originReplicaId: owner });
40107
+ } catch {
40108
+ }
40109
+ }
40110
+
39158
40111
  // src/index.ts
39159
40112
  import * as Protocols from "@axiom-lattice/protocols";
39160
40113
 
@@ -39297,9 +40250,9 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39297
40250
  if (!skillName) continue;
39298
40251
  if (await isPluginSkillManagedInSandbox(sandbox, skillName)) {
39299
40252
  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) {
40253
+ const owner2 = pluginSkillOwners?.[skillName];
40254
+ const registered = owner2 && enabledDefinition ? PluginRegistry.getSkillForPlugin(owner2, skillName) : void 0;
40255
+ if (!registered || registered.definition !== enabledDefinition || !owner2 || !await isPluginSkillAuthorizedInSandbox(sandbox, skillName, owner2) || await getPluginSkillManifestVersion(sandbox, skillName) !== enabledDefinition.version) {
39303
40256
  continue;
39304
40257
  }
39305
40258
  }
@@ -39325,8 +40278,8 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39325
40278
  const seenNames = new Set(filteredSkills.map((skill) => skill.name));
39326
40279
  try {
39327
40280
  for (const [name, definition] of Object.entries(pluginSkills ?? {})) {
39328
- const owner = pluginSkillOwners?.[name];
39329
- const registered = owner ? PluginRegistry.getSkillForPlugin(owner, name) : void 0;
40281
+ const owner2 = pluginSkillOwners?.[name];
40282
+ const registered = owner2 ? PluginRegistry.getSkillForPlugin(owner2, name) : void 0;
39330
40283
  if (!registered || registered.definition !== definition) continue;
39331
40284
  const { meta } = parseSkillFrontmatter(definition.content);
39332
40285
  if (!meta.name || !meta.description || seenNames.has(meta.name)) continue;
@@ -39363,24 +40316,24 @@ import z54 from "zod";
39363
40316
  import { tool as tool48 } from "langchain";
39364
40317
 
39365
40318
  // src/sandbox_plugin/pluginSkillResolution.ts
39366
- async function resolveTenantPluginSkill(tenantId2, owner, skillName) {
40319
+ async function resolveTenantPluginSkill(tenantId2, owner2, skillName) {
39367
40320
  try {
39368
40321
  const { plugins } = await getSandboxPluginCatalog().ensureLoaded(tenantId2);
39369
- const plugin = plugins.find((candidate) => candidate.meta.type === owner);
40322
+ const plugin = plugins.find((candidate) => candidate.meta.type === owner2);
39370
40323
  return plugin?.skills?.[skillName];
39371
40324
  } catch {
39372
40325
  return void 0;
39373
40326
  }
39374
40327
  }
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 };
40328
+ async function resolveEnabledPluginSkill(owner2, skillName, enabledDefinition, tenantId2) {
40329
+ if (!owner2 || !enabledDefinition) return void 0;
40330
+ const registered = PluginRegistry.getSkillForPlugin(owner2, skillName);
40331
+ if (registered && registered.pluginType === owner2 && registered.definition === enabledDefinition) {
40332
+ return { pluginType: owner2, definition: enabledDefinition };
39380
40333
  }
39381
40334
  if (!tenantId2) return void 0;
39382
- const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner, skillName);
39383
- return tenantDefinition ? { pluginType: owner, definition: tenantDefinition } : void 0;
40335
+ const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner2, skillName);
40336
+ return tenantDefinition ? { pluginType: owner2, definition: tenantDefinition } : void 0;
39384
40337
  }
39385
40338
 
39386
40339
  // src/tool_lattice/skill/load_skill_content.ts
@@ -39442,10 +40395,10 @@ var createLoadSkillContentTool = (pluginSkills, pluginSkillOwnersOrStaticSelecti
39442
40395
  return result2;
39443
40396
  }
39444
40397
  const enabledDefinition = pluginSkills?.[input.skill_name];
39445
- const owner = pluginSkillOwners?.[input.skill_name];
40398
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39446
40399
  const runConfig = _exe_config?.configurable?.runConfig || {};
39447
40400
  const pluginSkill = await resolveEnabledPluginSkill(
39448
- owner,
40401
+ owner2,
39449
40402
  input.skill_name,
39450
40403
  enabledDefinition,
39451
40404
  runConfig.tenantId
@@ -39552,9 +40505,9 @@ var createLoadSkillResourceTool = (pluginSkills, pluginSkillOwners) => {
39552
40505
  validateRelativePath(input.resource_path, "resource");
39553
40506
  const sandbox = await getSandboxFromExeConfig3(_exe_config);
39554
40507
  const definition = pluginSkills?.[input.skill_name];
39555
- const owner = pluginSkillOwners?.[input.skill_name];
40508
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39556
40509
  const tenantId2 = _exe_config?.configurable?.runConfig?.tenantId;
39557
- const resolved = await resolveEnabledPluginSkill(owner, input.skill_name, definition, tenantId2);
40510
+ const resolved = await resolveEnabledPluginSkill(owner2, input.skill_name, definition, tenantId2);
39558
40511
  if (definition) {
39559
40512
  if (!resolved || !resolved.definition.resources?.[input.resource_path]) {
39560
40513
  return `Resource "${input.resource_path}" not found for skill "${input.skill_name}".`;
@@ -41786,9 +42739,9 @@ async function renderAvailableExecutors(rc, tenantId2) {
41786
42739
  const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
41787
42740
  const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
41788
42741
  const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
41789
- const cached = executorCache.get(cacheKey);
42742
+ const cached2 = executorCache.get(cacheKey);
41790
42743
  const now = Date.now();
41791
- if (cached && now - cached.at < EXECUTOR_CACHE_TTL_MS) return cached.text;
42744
+ if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
41792
42745
  const lines = [];
41793
42746
  try {
41794
42747
  if (governed && projectId) {
@@ -46074,6 +47027,7 @@ var sandboxPluginsPlugin = {
46074
47027
  }
46075
47028
  case "reload": {
46076
47029
  const loaded = await catalog2.reload(tenantId2);
47030
+ void publishPluginCatalogChanged(tenantId2);
46077
47031
  return JSON.stringify({ reloaded: true, plugins: loaded.plugins.length, diagnostics: loaded.diagnostics });
46078
47032
  }
46079
47033
  case "create":
@@ -46093,12 +47047,14 @@ var sandboxPluginsPlugin = {
46093
47047
  if (!check.manifest) return JSON.stringify({ written: false, diagnostics: check.diagnostics });
46094
47048
  await catalog2.writePlugin(tenantId2, input.type, input.manifest, handlers);
46095
47049
  const loaded = await catalog2.reload(tenantId2);
47050
+ void publishPluginCatalogChanged(tenantId2);
46096
47051
  return JSON.stringify({ written: true, diagnostics: loaded.diagnostics, hash: loaded.hash });
46097
47052
  }
46098
47053
  case "delete": {
46099
47054
  if (!input.type) return "type is required";
46100
47055
  await catalog2.deletePlugin(tenantId2, input.type);
46101
47056
  await catalog2.reload(tenantId2);
47057
+ void publishPluginCatalogChanged(tenantId2);
46102
47058
  return JSON.stringify({ deleted: true });
46103
47059
  }
46104
47060
  case "validate": {
@@ -46210,6 +47166,26 @@ function registerBuiltinPlugins() {
46210
47166
  }
46211
47167
  }
46212
47168
 
47169
+ // src/sandbox_plugin/provisionOpenClient.ts
47170
+ import { readFileSync } from "fs";
47171
+ var SDK_ROOT = "/root/.agents/sdk";
47172
+ var CLIENT_DIR = `${SDK_ROOT}/open-mcp-client`;
47173
+ var MANIFEST_PATH2 = `${SDK_ROOT}/.open-client-manifest.json`;
47174
+ async function provisionSandboxOpenClient(sandbox, bundle) {
47175
+ await sandbox.file.createDirectory(SDK_ROOT);
47176
+ await sandbox.file.createDirectory(CLIENT_DIR);
47177
+ await sandbox.file.writeFile(`${CLIENT_DIR}/index.js`, bundle.indexJs);
47178
+ await sandbox.file.writeFile(`${CLIENT_DIR}/cli.js`, bundle.cliJs);
47179
+ await sandbox.file.writeFile(MANIFEST_PATH2, JSON.stringify({ version: bundle.version }));
47180
+ }
47181
+ function buildBundleFromDist(distDir, version) {
47182
+ return {
47183
+ indexJs: readFileSync(`${distDir}/index.js`, "utf8"),
47184
+ cliJs: readFileSync(`${distDir}/cli.js`, "utf8"),
47185
+ version
47186
+ };
47187
+ }
47188
+
46213
47189
  // src/personal_assistant/PersonalAssistantConfig.ts
46214
47190
  function deepClone(obj) {
46215
47191
  return JSON.parse(JSON.stringify(obj));
@@ -46671,10 +47647,12 @@ export {
46671
47647
  InMemoryChunkBuffer,
46672
47648
  InMemoryCollectionStore,
46673
47649
  InMemoryDatabaseConfigStore,
47650
+ InMemoryLeaseStore,
46674
47651
  InMemoryMailboxStore,
46675
47652
  InMemoryMenuStore,
46676
47653
  InMemoryModelProviderStore,
46677
47654
  InMemoryOpenAuditStore,
47655
+ InMemoryOutboxStore,
46678
47656
  InMemoryProjectBotMembershipStore,
46679
47657
  InMemoryProjectMembershipStore,
46680
47658
  InMemoryProjectRoomCoordinator,
@@ -46694,6 +47672,7 @@ export {
46694
47672
  LatticeEval,
46695
47673
  LatticeEvalProject,
46696
47674
  LatticeEvalSuite,
47675
+ LeaseAcquisitionTimeoutError,
46697
47676
  LocalSandboxInstance,
46698
47677
  LocalSandboxProvider,
46699
47678
  LoggerLatticeManager,
@@ -46716,7 +47695,10 @@ export {
46716
47695
  MicrosandboxServiceClient,
46717
47696
  ModelLatticeManager,
46718
47697
  MysqlDatabase,
47698
+ OutboxPoller,
46719
47699
  PACK_DIRECTORY_TIMEOUT_SECONDS,
47700
+ PLATFORM_SECRET_ENV_VARS,
47701
+ PLUGIN_CATALOG_CHANGED_TOPIC,
46720
47702
  PersonalAssistantConfig,
46721
47703
  PinoLoggerClient,
46722
47704
  PluginRegistry,
@@ -46737,6 +47719,8 @@ export {
46737
47719
  RepeatedToolCallError,
46738
47720
  RoomAgentMessageService,
46739
47721
  SAFE_PATH_COMPONENT,
47722
+ SANDBOX_OPEN_GATEWAY_URL_ENV,
47723
+ SANDBOX_OPEN_TOKEN_ENV,
46740
47724
  STTModelLattice,
46741
47725
  STTModelLatticeManager,
46742
47726
  SandboxFilesystem,
@@ -46777,11 +47761,13 @@ export {
46777
47761
  appendReconciliationWarning,
46778
47762
  appendUiFence,
46779
47763
  assertSafeUpstreamUrl,
47764
+ buildBundleFromDist,
46780
47765
  buildGrepResultsDict,
46781
47766
  buildInput,
46782
47767
  buildNamedVolumeName,
46783
47768
  buildRemotePackDirectoryCommand,
46784
47769
  buildSandboxMetadataEnv,
47770
+ buildSandboxOpenEnv,
46785
47771
  buildSkillFile,
46786
47772
  buildStateAnnotation,
46787
47773
  buildTableName,
@@ -46790,11 +47776,14 @@ export {
46790
47776
  checkEmptyContent,
46791
47777
  clearEncryptionKeyCache,
46792
47778
  clearEvalRunService,
47779
+ clearSandboxOpenCredentialProviderForTests,
46793
47780
  collectionLatticeManager,
46794
47781
  compileInternal,
46795
47782
  compileSandboxPlugin,
46796
47783
  compileWorkflow,
46797
47784
  computeSandboxName,
47785
+ configureCoordinationLeaseStore,
47786
+ configurePluginCatalogSync,
46798
47787
  configureStores,
46799
47788
  connectAllChannels,
46800
47789
  convertToConnection,
@@ -46826,6 +47815,7 @@ export {
46826
47815
  createSandboxResolver,
46827
47816
  createSchedulerMiddleware,
46828
47817
  createSharePayload,
47818
+ createShellExecTool,
46829
47819
  createTaskExecutionService,
46830
47820
  createTaskLifecycleService,
46831
47821
  createTaskMiddleware,
@@ -46872,6 +47862,7 @@ export {
46872
47862
  getCheckpointSaver,
46873
47863
  getChunkBuffer,
46874
47864
  getCollectionEntryCount,
47865
+ getCoordinationLeaseStore,
46875
47866
  getEmbeddingsClient,
46876
47867
  getEmbeddingsLattice,
46877
47868
  getEncryptionKey,
@@ -46883,15 +47874,18 @@ export {
46883
47874
  getOrCreateCollectionVectorStore,
46884
47875
  getPluginSkillManifestVersion,
46885
47876
  getQueueLattice,
47877
+ getReplicaId,
46886
47878
  getRuntimeCapabilityConfig,
46887
47879
  getRuntimeSkillSelection,
46888
47880
  getSTTClient,
46889
47881
  getSTTClientWithTenant,
46890
47882
  getSTTModelLattice,
46891
47883
  getSandBoxManager,
47884
+ getSandboxOpenCredential,
46892
47885
  getSandboxPluginCatalog,
46893
47886
  getScheduleLattice,
46894
47887
  getStoreLattice,
47888
+ getThreadRunOwner,
46895
47889
  getToolClient,
46896
47890
  getToolClientWithTenant,
46897
47891
  getToolDefinition,
@@ -46945,6 +47939,8 @@ export {
46945
47939
  projectMutationMutex,
46946
47940
  projectRoomPlugin,
46947
47941
  projectTaskAdmissionNotifier,
47942
+ provisionSandboxOpenClient,
47943
+ publishPluginCatalogChanged,
46948
47944
  queueLatticeManager,
46949
47945
  readLoopBreakerConfig,
46950
47946
  reconcileAllTenantTaskRuns,
@@ -46963,6 +47959,7 @@ export {
46963
47959
  registerModelLatticeForTenant,
46964
47960
  registerQueueLattice,
46965
47961
  registerSTTModelLattice,
47962
+ registerSandboxOpenCredentialProvider,
46966
47963
  registerSandboxProviderType,
46967
47964
  registerScheduleLattice,
46968
47965
  registerStoreLattice,
@@ -46973,6 +47970,7 @@ export {
46973
47970
  registerWorkflowRun,
46974
47971
  removeCollectionVectorStore,
46975
47972
  renderTemplate,
47973
+ resetReplicaIdForTests,
46976
47974
  resolveEnabledPluginSkill,
46977
47975
  resolveJudgeModelKey,
46978
47976
  resolvePath,
@@ -46980,6 +47978,7 @@ export {
46980
47978
  resolveSandboxPluginConnections,
46981
47979
  resolveTenantPluginSkill,
46982
47980
  roomCoordinatorConfig,
47981
+ runAsLeader,
46983
47982
  runRemotePackDirectory,
46984
47983
  safeTaskResponseId,
46985
47984
  sandboxLatticeManager,
@@ -46998,11 +47997,15 @@ export {
46998
47997
  storeLatticeManager,
46999
47998
  sttModelLatticeManager,
47000
47999
  taskLifecycleNotifier,
48000
+ taskRunLeaseKey,
47001
48001
  taskRunRegistry,
48002
+ threadRunLeaseKey,
47002
48003
  toJsonSchema,
47003
48004
  toSafeStateExpr,
47004
48005
  toolLatticeManager,
47005
48006
  truncateIfTooLong,
48007
+ tryAcquireTaskRun,
48008
+ tryAcquireThreadRun,
47006
48009
  unregisterTeammateAgent,
47007
48010
  unregisterWorkflowRun,
47008
48011
  updateFileData,
@@ -47018,6 +48021,7 @@ export {
47018
48021
  validateToolInput,
47019
48022
  vectorStoreLatticeManager,
47020
48023
  vectorStoreProviderManager,
48024
+ withLease,
47021
48025
  withTimeout,
47022
48026
  wrapMcpToolWithUi
47023
48027
  };