@axiom-lattice/core 6.1.1 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2115,7 +2115,7 @@ var InMemoryModelProviderStore = class {
2115
2115
  */
2116
2116
  async listProviders(tenantId2) {
2117
2117
  return Array.from(this.providers.values()).filter(
2118
- (provider) => provider.tenantId === tenantId2
2118
+ (provider2) => provider2.tenantId === tenantId2
2119
2119
  );
2120
2120
  }
2121
2121
  /**
@@ -2129,7 +2129,7 @@ var InMemoryModelProviderStore = class {
2129
2129
  */
2130
2130
  async getProviderByName(tenantId2, name) {
2131
2131
  const providers = await this.listProviders(tenantId2);
2132
- return providers.find((provider) => provider.name === name) || null;
2132
+ return providers.find((provider2) => provider2.name === name) || null;
2133
2133
  }
2134
2134
  /**
2135
2135
  * Create a new provider configuration
@@ -2313,8 +2313,8 @@ var InMemoryModelProviderStore = class {
2313
2313
  * Clear providers and models for a specific tenant
2314
2314
  */
2315
2315
  clearByTenant(tenantId2) {
2316
- for (const [key5, provider] of Array.from(this.providers.entries())) {
2317
- if (provider.tenantId === tenantId2) {
2316
+ for (const [key5, provider2] of Array.from(this.providers.entries())) {
2317
+ if (provider2.tenantId === tenantId2) {
2318
2318
  this.providers.delete(key5);
2319
2319
  }
2320
2320
  }
@@ -3190,6 +3190,123 @@ function entryKey(tenantId2, userId, endpoint) {
3190
3190
  return JSON.stringify([tenantId2, userId, endpoint]);
3191
3191
  }
3192
3192
 
3193
+ // src/store_lattice/InMemoryLeaseStore.ts
3194
+ var InMemoryLeaseStore = class {
3195
+ /**
3196
+ * @param now Injectable clock in epoch milliseconds (tests use a fake clock).
3197
+ */
3198
+ constructor(now = Date.now) {
3199
+ this.leases = /* @__PURE__ */ new Map();
3200
+ this.now = now;
3201
+ }
3202
+ /** Atomically acquires a free/expired key (or the caller's own key). */
3203
+ async tryAcquire(key5, holder, ttlMs) {
3204
+ const existing = this.leases.get(key5);
3205
+ if (existing && Date.parse(existing.expiresAt) > this.now()) {
3206
+ if (existing.holder !== holder) return false;
3207
+ this.leases.set(key5, { ...existing, expiresAt: new Date(this.now() + ttlMs).toISOString() });
3208
+ return true;
3209
+ }
3210
+ const acquiredAt = existing && existing.holder === holder ? existing.acquiredAt : new Date(this.now()).toISOString();
3211
+ this.leases.set(key5, {
3212
+ key: key5,
3213
+ holder,
3214
+ acquiredAt,
3215
+ expiresAt: new Date(this.now() + ttlMs).toISOString()
3216
+ });
3217
+ return true;
3218
+ }
3219
+ /** Extends a lease only while `holder` still owns it. */
3220
+ async renew(key5, holder, ttlMs) {
3221
+ const existing = this.leases.get(key5);
3222
+ if (!existing || existing.holder !== holder) return false;
3223
+ this.leases.set(key5, { ...existing, expiresAt: new Date(this.now() + ttlMs).toISOString() });
3224
+ return true;
3225
+ }
3226
+ /** Deletes a lease only when `holder` owns it. */
3227
+ async release(key5, holder) {
3228
+ const existing = this.leases.get(key5);
3229
+ if (!existing || existing.holder !== holder) return false;
3230
+ this.leases.delete(key5);
3231
+ return true;
3232
+ }
3233
+ /** Returns the live lease, pruning an expired one. */
3234
+ async get(key5) {
3235
+ const existing = this.leases.get(key5);
3236
+ if (!existing) return null;
3237
+ if (Date.parse(existing.expiresAt) <= this.now()) {
3238
+ this.leases.delete(key5);
3239
+ return null;
3240
+ }
3241
+ return { ...existing };
3242
+ }
3243
+ };
3244
+
3245
+ // src/store_lattice/InMemoryOutboxStore.ts
3246
+ var InMemoryOutboxStore = class {
3247
+ /**
3248
+ * Creates an in-memory outbox store.
3249
+ *
3250
+ * @param now Optional clock override (defaults to the system clock); it stamps
3251
+ * `createdAt` and is used to evaluate age-based retention.
3252
+ */
3253
+ constructor(now = () => /* @__PURE__ */ new Date()) {
3254
+ this.events = [];
3255
+ this.counter = 0;
3256
+ this.now = now;
3257
+ }
3258
+ /**
3259
+ * Appends one event, assigning the next monotonic id.
3260
+ * @param input Topic, JSON-safe payload, and authoring replica id.
3261
+ * @returns A defensive copy of the stored event.
3262
+ */
3263
+ async append(input) {
3264
+ const event = {
3265
+ id: String(++this.counter),
3266
+ topic: input.topic,
3267
+ payload: input.payload,
3268
+ originReplicaId: input.originReplicaId,
3269
+ createdAt: this.now().toISOString()
3270
+ };
3271
+ this.events.push(event);
3272
+ return { ...event };
3273
+ }
3274
+ /**
3275
+ * Lists events strictly newer than `cursor`, ascending, bounded by `limit`.
3276
+ * @param cursor Last-seen id, or `null` to read from the start.
3277
+ * @param limit Maximum number of events to return.
3278
+ * @returns Defensive copies of the matching events.
3279
+ */
3280
+ async listAfter(cursor, limit) {
3281
+ const floor = cursor === null ? 0 : Number(cursor);
3282
+ return this.events.filter((event) => Number(event.id) > floor).slice(0, limit).map((event) => ({ ...event }));
3283
+ }
3284
+ /**
3285
+ * Returns the newest event id.
3286
+ * @returns The last id, or `null` when the log is empty.
3287
+ */
3288
+ async latestId() {
3289
+ return this.events.length === 0 ? null : this.events[this.events.length - 1].id;
3290
+ }
3291
+ /**
3292
+ * Deletes events older than `olderThanMs`, evaluated against this store's clock.
3293
+ *
3294
+ * @param olderThanMs Age threshold in milliseconds.
3295
+ * @returns The number of events removed.
3296
+ */
3297
+ async pruneOlderThan(olderThanMs) {
3298
+ const cutoff = this.now().getTime() - olderThanMs;
3299
+ let removed = 0;
3300
+ for (let i = this.events.length - 1; i >= 0; i -= 1) {
3301
+ if (new Date(this.events[i].createdAt).getTime() < cutoff) {
3302
+ this.events.splice(i, 1);
3303
+ removed += 1;
3304
+ }
3305
+ }
3306
+ return removed;
3307
+ }
3308
+ };
3309
+
3193
3310
  // src/store_lattice/InMemoryUserStore.ts
3194
3311
  var InMemoryUserStore = class {
3195
3312
  constructor() {
@@ -3396,7 +3513,8 @@ function isMessageContentValue(value) {
3396
3513
 
3397
3514
  // src/store_lattice/InMemoryThreadMessageQueueStore.ts
3398
3515
  var InMemoryThreadMessageQueueStore = class {
3399
- constructor() {
3516
+ constructor(now = Date.now) {
3517
+ this.now = now;
3400
3518
  this.messages = /* @__PURE__ */ new Map();
3401
3519
  this.messageIdCounter = 0;
3402
3520
  this.capacityTail = Promise.resolve();
@@ -3503,6 +3621,36 @@ var InMemoryThreadMessageQueueStore = class {
3503
3621
  const threadMessages = this.getMessagesForThread(threadId);
3504
3622
  return threadMessages.filter((msg) => msg.status === "processing" && this.inScope(msg, scope)).sort((a, b) => (b.priority || 0) - (a.priority || 0) || a.sequence - b.sequence).map(clonePendingMessage);
3505
3623
  }
3624
+ async claimPendingMessages(threadId, scope, options) {
3625
+ const threadMessages = this.getMessagesForThread(threadId);
3626
+ const candidates = threadMessages.filter((msg) => (msg.status === "pending" || !msg.status) && this.inScope(msg, scope)).sort((a, b) => (b.priority || 0) - (a.priority || 0) || a.sequence - b.sequence);
3627
+ const limit = options.limit === void 0 ? void 0 : Math.max(0, Math.floor(options.limit));
3628
+ const selected = limit === void 0 ? candidates : candidates.slice(0, limit);
3629
+ const now = this.now();
3630
+ for (const message of selected) {
3631
+ message.status = "processing";
3632
+ message.lockedBy = options.owner;
3633
+ message.lockedAt = new Date(now);
3634
+ message.leaseExpiresAt = new Date(now + options.ttlMs);
3635
+ }
3636
+ return selected.map(clonePendingMessage);
3637
+ }
3638
+ async reclaimExpiredProcessing(threadId, scope) {
3639
+ const messages = this.messages.get(threadId);
3640
+ if (!messages) return 0;
3641
+ const now = this.now();
3642
+ let count = 0;
3643
+ for (const msg of messages) {
3644
+ if (msg.status === "processing" && this.inScope(msg, scope) && msg.leaseExpiresAt !== void 0 && msg.leaseExpiresAt.getTime() <= now) {
3645
+ msg.status = "pending";
3646
+ msg.lockedBy = void 0;
3647
+ msg.lockedAt = void 0;
3648
+ msg.leaseExpiresAt = void 0;
3649
+ count += 1;
3650
+ }
3651
+ }
3652
+ return count;
3653
+ }
3506
3654
  async getQueueSize(threadId, scope) {
3507
3655
  const pending = await this.getPendingMessages(threadId, scope);
3508
3656
  return pending.length;
@@ -3566,6 +3714,9 @@ var InMemoryThreadMessageQueueStore = class {
3566
3714
  for (const msg of messages) {
3567
3715
  if (msg.status === "processing" && this.inScope(msg, scope)) {
3568
3716
  msg.status = "pending";
3717
+ msg.lockedBy = void 0;
3718
+ msg.lockedAt = void 0;
3719
+ msg.leaseExpiresAt = void 0;
3569
3720
  count++;
3570
3721
  }
3571
3722
  }
@@ -4824,6 +4975,8 @@ var defaultProjectBotMembershipStore = new InMemoryProjectBotMembershipStore(def
4824
4975
  var defaultProjectRoomMessageStore = new InMemoryProjectRoomMessageStore(defaultProjectRoomCoordinator);
4825
4976
  var defaultProjectRoomReadStateStore = new InMemoryProjectRoomReadStateStore();
4826
4977
  var defaultUserPushSubscriptionStore = new InMemoryUserPushSubscriptionStore();
4978
+ var defaultLeaseStore = new InMemoryLeaseStore();
4979
+ var defaultOutboxStore = new InMemoryOutboxStore();
4827
4980
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
4828
4981
  storeLatticeManager.registerLattice(
4829
4982
  "default",
@@ -4845,6 +4998,8 @@ storeLatticeManager.registerLattice("default", "projectBotMembership", defaultPr
4845
4998
  storeLatticeManager.registerLattice("default", "projectRoomMessage", defaultProjectRoomMessageStore);
4846
4999
  storeLatticeManager.registerLattice("default", "projectRoomReadState", defaultProjectRoomReadStateStore);
4847
5000
  storeLatticeManager.registerLattice("default", "userPushSubscription", defaultUserPushSubscriptionStore);
5001
+ storeLatticeManager.registerLattice("default", "lease", defaultLeaseStore);
5002
+ storeLatticeManager.registerLattice("default", "outbox", defaultOutboxStore);
4848
5003
  var defaultUserStore = new InMemoryUserStore();
4849
5004
  var defaultTenantStore = new InMemoryTenantStore();
4850
5005
  var defaultUserTenantLinkStore = new InMemoryUserTenantLinkStore();
@@ -6713,8 +6868,8 @@ var MetricsServerManager = class _MetricsServerManager {
6713
6868
  } catch {
6714
6869
  }
6715
6870
  const tenantConfigs = this.configs.get(tenantId2);
6716
- const cached = tenantConfigs?.get(key5);
6717
- if (cached) return cached;
6871
+ const cached2 = tenantConfigs?.get(key5);
6872
+ if (cached2) return cached2;
6718
6873
  throw new Error(`Metrics server '${key5}' not found for tenant '${tenantId2}'`);
6719
6874
  }
6720
6875
  /**
@@ -8024,8 +8179,8 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8024
8179
  getLatticeType() {
8025
8180
  return "sandbox_manager";
8026
8181
  }
8027
- registerLattice(key5, provider) {
8028
- this.register(key5, provider);
8182
+ registerLattice(key5, provider2) {
8183
+ this.register(key5, provider2);
8029
8184
  }
8030
8185
  getSandboxLattice(key5) {
8031
8186
  const lattice = this.get(key5);
@@ -8076,8 +8231,8 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8076
8231
  return this.getVolumeBackendForPath(config, "/project");
8077
8232
  }
8078
8233
  async getVolumeBackendForPath(config, filePath) {
8079
- const provider = this._requireProvider();
8080
- if (!provider.createVolumeFsClient) {
8234
+ const provider2 = this._requireProvider();
8235
+ if (!provider2.createVolumeFsClient) {
8081
8236
  console.log(`[getVolumeBackendForPath] provider has no createVolumeFsClient, returning null`);
8082
8237
  return null;
8083
8238
  }
@@ -8088,7 +8243,7 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8088
8243
  return null;
8089
8244
  }
8090
8245
  console.log(`[getVolumeBackendForPath] path=${filePath} volume=${mapping.volumeName} prefix=${mapping.pathPrefix}`);
8091
- const client = provider.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
8246
+ const client = provider2.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
8092
8247
  return new VolumeFilesystem(stripPrefixClient(client, mapping.pathPrefix), mapping.pathPrefix);
8093
8248
  }
8094
8249
  _resolveVolumeForPath(config, tenantId2, filePath) {
@@ -8123,35 +8278,35 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
8123
8278
  };
8124
8279
  }
8125
8280
  async createSandbox(name, config) {
8126
- const provider = this._requireProvider();
8127
- return provider.createSandbox(name, config);
8281
+ const provider2 = this._requireProvider();
8282
+ return provider2.createSandbox(name, config);
8128
8283
  }
8129
8284
  async getSandbox(name) {
8130
- const provider = this._requireProvider();
8131
- return provider.getSandbox(name);
8285
+ const provider2 = this._requireProvider();
8286
+ return provider2.getSandbox(name);
8132
8287
  }
8133
8288
  async stopSandbox(name) {
8134
- const provider = this._requireProvider();
8135
- return provider.stopSandbox(name);
8289
+ const provider2 = this._requireProvider();
8290
+ return provider2.stopSandbox(name);
8136
8291
  }
8137
8292
  async deleteSandbox(name) {
8138
- const provider = this._requireProvider();
8139
- return provider.deleteSandbox(name);
8293
+ const provider2 = this._requireProvider();
8294
+ return provider2.deleteSandbox(name);
8140
8295
  }
8141
8296
  async listSandboxes() {
8142
- const provider = this._requireProvider();
8143
- return provider.listSandboxes();
8297
+ const provider2 = this._requireProvider();
8298
+ return provider2.listSandboxes();
8144
8299
  }
8145
8300
  _requireProvider() {
8146
8301
  const keys = this.keysByTenant("default");
8147
8302
  if (keys.length === 0) {
8148
8303
  throw new Error("Sandbox provider not configured.");
8149
8304
  }
8150
- const provider = this.get(keys[0]);
8151
- if (!provider) {
8305
+ const provider2 = this.get(keys[0]);
8306
+ if (!provider2) {
8152
8307
  throw new Error("Sandbox provider not configured.");
8153
8308
  }
8154
- return provider;
8309
+ return provider2;
8155
8310
  }
8156
8311
  /**
8157
8312
  * Returns the first registered (default) sandbox provider.
@@ -8181,6 +8336,46 @@ import { tool as tool14 } from "langchain";
8181
8336
  // src/tool_lattice/shell_exec/index.ts
8182
8337
  import z17 from "zod";
8183
8338
  import { tool as tool15 } from "langchain";
8339
+
8340
+ // src/sandbox_lattice/sandboxOpenCredential.ts
8341
+ var SANDBOX_OPEN_GATEWAY_URL_ENV = "API_GATEWAY_URL";
8342
+ var SANDBOX_OPEN_TOKEN_ENV = "OPEN_API_TOKEN";
8343
+ var provider;
8344
+ var providerErrorHandler;
8345
+ function registerSandboxOpenCredentialProvider(next, options = {}) {
8346
+ provider = next;
8347
+ providerErrorHandler = options.onError;
8348
+ return () => {
8349
+ if (provider === next) {
8350
+ provider = void 0;
8351
+ providerErrorHandler = void 0;
8352
+ }
8353
+ };
8354
+ }
8355
+ async function getSandboxOpenCredential(context) {
8356
+ if (!provider) return void 0;
8357
+ try {
8358
+ return await provider(context);
8359
+ } catch (error) {
8360
+ try {
8361
+ providerErrorHandler?.(error);
8362
+ } catch {
8363
+ }
8364
+ return void 0;
8365
+ }
8366
+ }
8367
+ function buildSandboxOpenEnv(credential) {
8368
+ return `export ${SANDBOX_OPEN_GATEWAY_URL_ENV}=${shellSingleQuote(credential.gatewayUrl)} && export ${SANDBOX_OPEN_TOKEN_ENV}=${shellSingleQuote(credential.token)}`;
8369
+ }
8370
+ function shellSingleQuote(value) {
8371
+ return `'${value.replace(/'/g, "'\\''")}'`;
8372
+ }
8373
+ function clearSandboxOpenCredentialProviderForTests() {
8374
+ provider = void 0;
8375
+ providerErrorHandler = void 0;
8376
+ }
8377
+
8378
+ // src/tool_lattice/shell_exec/index.ts
8184
8379
  var SHELL_EXEC_DESCRIPTION = `Execute a shell command in the sandbox environment.
8185
8380
 
8186
8381
  Use this to run CLI tools, install packages, inspect files, or perform any operation that requires a shell.
@@ -8213,8 +8408,19 @@ var createShellExecTool = ({
8213
8408
  projectId: runConfig.projectId,
8214
8409
  vmIsolation
8215
8410
  });
8411
+ const tenantId2 = runConfig.tenantId;
8412
+ const credential = tenantId2 ? await getSandboxOpenCredential({
8413
+ tenantId: tenantId2,
8414
+ workspaceId: runConfig.workspaceId,
8415
+ projectId: runConfig.projectId,
8416
+ sandboxName: sandbox.name,
8417
+ assistantId: runConfig.assistant_id || void 0,
8418
+ threadId: runConfig.thread_id || void 0,
8419
+ depth: runConfig.depth
8420
+ }) : void 0;
8421
+ const command = credential ? `${buildSandboxOpenEnv(credential)} && ${input.command}` : input.command;
8216
8422
  const executeResult = await sandbox.shell.execCommand({
8217
- command: input.command,
8423
+ command,
8218
8424
  exec_dir: input.exec_dir,
8219
8425
  timeout: input.timeout ?? 300
8220
8426
  });
@@ -12952,13 +13158,23 @@ var SandboxPluginExecutor = class {
12952
13158
  validateRelativePath(args.handler, "handler");
12953
13159
  const pluginDir = `${PLUGINS_ROOT}/${args.manifest.type}`;
12954
13160
  const handlerAbs = `${pluginDir}/${args.handler}`;
13161
+ const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
12955
13162
  const sandbox = await this.deps.resolveSandbox({
12956
- tenantId: args.runConfig?.tenantId ?? args.context.tenantId,
13163
+ tenantId: tenantId2,
12957
13164
  assistantId: args.runConfig?.assistantId,
12958
13165
  threadId: args.runConfig?.threadId,
12959
13166
  workspaceId: args.runConfig?.workspaceId,
12960
13167
  projectId: args.runConfig?.projectId
12961
13168
  });
13169
+ const openCredential = tenantId2 ? await getSandboxOpenCredential({
13170
+ tenantId: tenantId2,
13171
+ workspaceId: args.runConfig?.workspaceId,
13172
+ projectId: args.runConfig?.projectId,
13173
+ sandboxName: sandbox.name,
13174
+ assistantId: args.runConfig?.assistantId,
13175
+ threadId: args.runConfig?.threadId,
13176
+ depth: args.runConfig?.depth
13177
+ }) : void 0;
12962
13178
  const timeoutMs = clamp(args.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
12963
13179
  const cap = clamp(
12964
13180
  args.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES,
@@ -12989,6 +13205,12 @@ var SandboxPluginExecutor = class {
12989
13205
  envParts.push(`PLUGIN_CONNECTION_FILE=${quotePosixShellArg(connectionFile)}`);
12990
13206
  }
12991
13207
  envParts.push(`PLUGIN_CONTEXT=${quotePosixShellArg(encode(args.context))}`);
13208
+ if (openCredential) {
13209
+ envParts.push(
13210
+ `${SANDBOX_OPEN_GATEWAY_URL_ENV}=${quotePosixShellArg(openCredential.gatewayUrl)}`
13211
+ );
13212
+ envParts.push(`${SANDBOX_OPEN_TOKEN_ENV}=${quotePosixShellArg(openCredential.token)}`);
13213
+ }
12992
13214
  const command = `${envParts.join(" ")} node ${quotePosixShellArg(handlerAbs)}`;
12993
13215
  const execResult = await sandbox.shell.execCommand({
12994
13216
  command,
@@ -13940,10 +14162,10 @@ var TenantPluginCatalog = class {
13940
14162
  candidates.push({ type, dirPath: dir.path, raw, manifestJson, files: dirFiles });
13941
14163
  }
13942
14164
  const hash = createHash3("sha256").update(hashParts.sort().join("\n")).digest("hex").slice(0, 16);
13943
- const cached = this.cache.get(tenantId2)?.value;
13944
- if (cached && cached.hash === hash) {
13945
- this.cache.set(tenantId2, { value: cached, checkedAt: this.now() });
13946
- return cached;
14165
+ const cached2 = this.cache.get(tenantId2)?.value;
14166
+ if (cached2 && cached2.hash === hash) {
14167
+ this.cache.set(tenantId2, { value: cached2, checkedAt: this.now() });
14168
+ return cached2;
13947
14169
  }
13948
14170
  const plugins = [];
13949
14171
  const manifests = {};
@@ -15304,7 +15526,7 @@ var AgentWorkerState = Annotation.Root({
15304
15526
  default: () => ({})
15305
15527
  }),
15306
15528
  command2: Annotation({
15307
- reducer: (x, y) => y ?? x,
15529
+ reducer: (_x, y) => y,
15308
15530
  default: () => void 0
15309
15531
  }),
15310
15532
  // RunConfig for passing configuration through the graph
@@ -15321,12 +15543,12 @@ var AgentWorkerState = Annotation.Root({
15321
15543
  }),
15322
15544
  // Execution result
15323
15545
  result: Annotation({
15324
- reducer: (x, y) => y ?? x,
15546
+ reducer: (_x, y) => y,
15325
15547
  default: () => void 0
15326
15548
  }),
15327
15549
  // Interrupt data
15328
15550
  interruptValue: Annotation({
15329
- reducer: (x, y) => y ?? x,
15551
+ reducer: (_x, y) => y,
15330
15552
  default: () => void 0
15331
15553
  }),
15332
15554
  // Final state to return
@@ -15404,6 +15626,324 @@ function createAgentWorkerGraph() {
15404
15626
  }
15405
15627
  var agentWorkerGraph = createAgentWorkerGraph();
15406
15628
 
15629
+ // src/util/toJsonSafeValue.ts
15630
+ var CIRCULAR_VALUE = "[Circular]";
15631
+ var REPEATED_VALUE = "[Repeated]";
15632
+ var arrayFrom = Array.from.bind(Array);
15633
+ var arrayIsArray = Array.isArray.bind(Array);
15634
+ var hasOwnProperty = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
15635
+ var isPrototypeOf = Object.prototype.isPrototypeOf.call.bind(Object.prototype.isPrototypeOf);
15636
+ var objectCreate = Object.create.bind(Object);
15637
+ var objectDefineProperty = Object.defineProperty.bind(Object);
15638
+ var objectIs = Object.is.bind(Object);
15639
+ var reflectGet = Reflect.get.bind(Reflect);
15640
+ var reflectGetOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor.bind(Reflect);
15641
+ var reflectOwnKeys = Reflect.ownKeys.bind(Reflect);
15642
+ var stringifyJson = JSON.stringify.bind(JSON);
15643
+ var numberIsFinite = Number.isFinite.bind(Number);
15644
+ var numberIsNaN = Number.isNaN.bind(Number);
15645
+ var numberIsSafeInteger = Number.isSafeInteger.bind(Number);
15646
+ var mathMax = Math.max.bind(Math);
15647
+ var dateConstructor = Date;
15648
+ var datePrototype = dateConstructor.prototype;
15649
+ var dateGetTime = datePrototype.getTime.call.bind(datePrototype.getTime);
15650
+ var dateToISOString = datePrototype.toISOString.call.bind(datePrototype.toISOString);
15651
+ var bigintToString = BigInt.prototype.toString.call.bind(BigInt.prototype.toString);
15652
+ var stringSlice = String.prototype.slice.call.bind(String.prototype.slice);
15653
+ var weakMapGet = WeakMap.prototype.get.call.bind(WeakMap.prototype.get);
15654
+ var weakMapSet = WeakMap.prototype.set.call.bind(WeakMap.prototype.set);
15655
+ var WeakMapConstructor = WeakMap;
15656
+ var utf8Encoder = new TextEncoder();
15657
+ var encodeUtf8 = TextEncoder.prototype.encode.bind(utf8Encoder);
15658
+ var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
15659
+ var typedArrayByteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")?.get;
15660
+ if (!typedArrayByteLengthGetter) throw new Error("TypedArray byteLength getter is unavailable");
15661
+ var typedArrayByteLength = typedArrayByteLengthGetter.call.bind(typedArrayByteLengthGetter);
15662
+ var JSON_SAFE_MAX_DEPTH = 32;
15663
+ var JSON_SAFE_MAX_NODES = 1e4;
15664
+ var JSON_SAFE_MAX_COLLECTION_ENTRIES = 1e4;
15665
+ var JSON_SAFE_MAX_STRING_LENGTH = 64 * 1024;
15666
+ var JSON_SAFE_MAX_PROPERTY_KEY_LENGTH = JSON_SAFE_MAX_STRING_LENGTH;
15667
+ var JSON_SAFE_MAX_PROPERTY_KEY_BYTES = 256 * 1024;
15668
+ var JSON_SAFE_MAX_SERIALIZED_BYTES = 256 * 1024;
15669
+ var JsonSafeValueError = class extends Error {
15670
+ /**
15671
+ * Create a bounded normalization error.
15672
+ *
15673
+ * @param code Stable normalization failure code.
15674
+ * @param message Human-readable bounded failure description.
15675
+ */
15676
+ constructor(code, message) {
15677
+ super(message);
15678
+ objectDefineProperty(this, "name", {
15679
+ value: "JsonSafeValueError",
15680
+ writable: true,
15681
+ configurable: true
15682
+ });
15683
+ objectDefineProperty(this, "code", {
15684
+ value: code,
15685
+ writable: true,
15686
+ configurable: true
15687
+ });
15688
+ }
15689
+ };
15690
+ function fail(code, message) {
15691
+ throw new JsonSafeValueError(code, message);
15692
+ }
15693
+ function propertyAccess(operation, path8) {
15694
+ try {
15695
+ return operation();
15696
+ } catch {
15697
+ fail("PROPERTY_ACCESS_FAILED", `Unable to inspect JSON-safe value at ${path8}`);
15698
+ }
15699
+ }
15700
+ function serializedStringBytes(value) {
15701
+ if (value.length > JSON_SAFE_MAX_STRING_LENGTH) {
15702
+ fail("MAX_STRING_LENGTH", `JSON-safe string exceeds ${JSON_SAFE_MAX_STRING_LENGTH} characters`);
15703
+ }
15704
+ return typedArrayByteLength(encodeUtf8(stringifyJson(value)));
15705
+ }
15706
+ function validatePropertyKey(key5) {
15707
+ if (key5.length > JSON_SAFE_MAX_PROPERTY_KEY_LENGTH) {
15708
+ fail(
15709
+ "MAX_STRING_LENGTH",
15710
+ `JSON-safe property key exceeds ${JSON_SAFE_MAX_PROPERTY_KEY_LENGTH} characters`
15711
+ );
15712
+ }
15713
+ }
15714
+ function chargePropertyKey(state, key5) {
15715
+ state.propertyKeyBytes += typedArrayByteLength(encodeUtf8(key5));
15716
+ if (state.propertyKeyBytes > JSON_SAFE_MAX_PROPERTY_KEY_BYTES) {
15717
+ fail(
15718
+ "MAX_PROPERTY_KEY_BYTES",
15719
+ `JSON-safe property keys exceed ${JSON_SAFE_MAX_PROPERTY_KEY_BYTES} traversed UTF-8 bytes`
15720
+ );
15721
+ }
15722
+ }
15723
+ function chargeBytes(state, bytes) {
15724
+ state.serializedBytes += bytes;
15725
+ if (state.serializedBytes > JSON_SAFE_MAX_SERIALIZED_BYTES) {
15726
+ fail(
15727
+ "MAX_SERIALIZED_BYTES",
15728
+ `JSON-safe value exceeds ${JSON_SAFE_MAX_SERIALIZED_BYTES} serialized bytes`
15729
+ );
15730
+ }
15731
+ }
15732
+ function chargeString(state, value) {
15733
+ chargeBytes(state, serializedStringBytes(value));
15734
+ }
15735
+ function objectPropertyPath(path8, key5) {
15736
+ const boundedKey = key5.length > 128 ? `${stringSlice(key5, 0, 128)}...` : key5;
15737
+ return `${path8}[${stringifyJson(boundedKey)}]`;
15738
+ }
15739
+ function convertJsonValue(value, state, depth, inArray, path8) {
15740
+ state.nodes += 1;
15741
+ if (state.nodes > JSON_SAFE_MAX_NODES) {
15742
+ fail("MAX_NODES", `JSON-safe value exceeds ${JSON_SAFE_MAX_NODES} total nodes`);
15743
+ }
15744
+ if (typeof value === "string") {
15745
+ chargeString(state, value);
15746
+ return value;
15747
+ }
15748
+ if (value === null) {
15749
+ chargeBytes(state, 4);
15750
+ return value;
15751
+ }
15752
+ if (typeof value === "boolean") {
15753
+ chargeBytes(state, value ? 4 : 5);
15754
+ return value;
15755
+ }
15756
+ if (typeof value === "number") {
15757
+ const normalized = numberIsFinite(value) ? objectIs(value, -0) ? 0 : value : null;
15758
+ chargeBytes(state, normalized === null ? 4 : stringifyJson(normalized).length);
15759
+ return normalized;
15760
+ }
15761
+ if (typeof value === "bigint") {
15762
+ const normalized = bigintToString(value);
15763
+ chargeString(state, normalized);
15764
+ return normalized;
15765
+ }
15766
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
15767
+ if (inArray) chargeBytes(state, 4);
15768
+ return inArray ? null : void 0;
15769
+ }
15770
+ const isDate = propertyAccess(() => isPrototypeOf(datePrototype, value), path8);
15771
+ if (isDate) {
15772
+ const time = propertyAccess(() => dateGetTime(value), path8);
15773
+ if (numberIsNaN(time)) {
15774
+ chargeBytes(state, 4);
15775
+ return null;
15776
+ }
15777
+ const normalized = propertyAccess(() => dateToISOString(value), path8);
15778
+ chargeString(state, normalized);
15779
+ return normalized;
15780
+ }
15781
+ if (depth > JSON_SAFE_MAX_DEPTH) {
15782
+ fail("MAX_DEPTH", `JSON-safe value exceeds maximum depth ${JSON_SAFE_MAX_DEPTH}`);
15783
+ }
15784
+ const referenceState = weakMapGet(state.references, value);
15785
+ if (referenceState === "visiting") {
15786
+ chargeString(state, CIRCULAR_VALUE);
15787
+ return CIRCULAR_VALUE;
15788
+ }
15789
+ if (referenceState === "visited") {
15790
+ chargeString(state, REPEATED_VALUE);
15791
+ return REPEATED_VALUE;
15792
+ }
15793
+ weakMapSet(state.references, value, "visiting");
15794
+ if (propertyAccess(() => arrayIsArray(value), path8)) {
15795
+ const arrayValue = value;
15796
+ const length = propertyAccess(
15797
+ () => arrayValue.length,
15798
+ path8
15799
+ );
15800
+ if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 0) {
15801
+ fail("PROPERTY_ACCESS_FAILED", `Unable to inspect JSON-safe array length at ${path8}`);
15802
+ }
15803
+ if (length > JSON_SAFE_MAX_COLLECTION_ENTRIES) {
15804
+ fail(
15805
+ "COLLECTION_TOO_LARGE",
15806
+ `JSON-safe array exceeds ${JSON_SAFE_MAX_COLLECTION_ENTRIES} entries`
15807
+ );
15808
+ }
15809
+ chargeBytes(state, 2 + mathMax(0, length - 1));
15810
+ const result2 = arrayFrom({ length }, (_, index) => {
15811
+ const entryPath = `${path8}[${index}]`;
15812
+ const hasEntry = propertyAccess(
15813
+ () => hasOwnProperty(arrayValue, index),
15814
+ entryPath
15815
+ );
15816
+ if (!hasEntry) {
15817
+ chargeBytes(state, 4);
15818
+ return null;
15819
+ }
15820
+ const entry = propertyAccess(() => arrayValue[index], entryPath);
15821
+ return convertJsonValue(entry, state, depth + 1, true, `${path8}[${index}]`);
15822
+ });
15823
+ objectDefineProperty(result2, "toJSON", {
15824
+ value: void 0,
15825
+ configurable: true,
15826
+ writable: true
15827
+ });
15828
+ weakMapSet(state.references, value, "visited");
15829
+ return result2;
15830
+ }
15831
+ const keys = propertyAccess(() => reflectOwnKeys(value), path8);
15832
+ if (keys.length > JSON_SAFE_MAX_COLLECTION_ENTRIES) {
15833
+ fail(
15834
+ "COLLECTION_TOO_LARGE",
15835
+ `JSON-safe object exceeds ${JSON_SAFE_MAX_COLLECTION_ENTRIES} own keys`
15836
+ );
15837
+ }
15838
+ for (let index = 0; index < keys.length; index += 1) {
15839
+ const key5 = keys[index];
15840
+ if (typeof key5 !== "string") continue;
15841
+ validatePropertyKey(key5);
15842
+ chargePropertyKey(state, key5);
15843
+ }
15844
+ chargeBytes(state, 2);
15845
+ const result = objectCreate(null);
15846
+ let emittedProperties = 0;
15847
+ for (let index = 0; index < keys.length; index += 1) {
15848
+ const key5 = keys[index];
15849
+ const entryPath = typeof key5 === "string" ? objectPropertyPath(path8, key5) : path8;
15850
+ const descriptor = propertyAccess(
15851
+ () => reflectGetOwnPropertyDescriptor(value, key5),
15852
+ entryPath
15853
+ );
15854
+ if (typeof key5 !== "string" || !descriptor?.enumerable) continue;
15855
+ const entry = propertyAccess(() => reflectGet(value, key5), entryPath);
15856
+ const converted = convertJsonValue(entry, state, depth + 1, false, entryPath);
15857
+ if (converted !== void 0) {
15858
+ chargeBytes(state, serializedStringBytes(key5) + 1 + (emittedProperties === 0 ? 0 : 1));
15859
+ propertyAccess(() => objectDefineProperty(result, key5, {
15860
+ value: converted,
15861
+ enumerable: true,
15862
+ configurable: true,
15863
+ writable: true
15864
+ }), entryPath);
15865
+ emittedProperties += 1;
15866
+ }
15867
+ }
15868
+ weakMapSet(state.references, value, "visited");
15869
+ return result;
15870
+ }
15871
+ function toJsonSafeValue(value) {
15872
+ const state = {
15873
+ nodes: 0,
15874
+ propertyKeyBytes: 0,
15875
+ serializedBytes: 0,
15876
+ references: new WeakMapConstructor()
15877
+ };
15878
+ const converted = convertJsonValue(value, state, 0, false, "$");
15879
+ if (converted === void 0) chargeBytes(state, 4);
15880
+ const result = converted ?? null;
15881
+ const serialized = propertyAccess(() => stringifyJson(result), "$");
15882
+ if (typedArrayByteLength(encodeUtf8(serialized)) > JSON_SAFE_MAX_SERIALIZED_BYTES) {
15883
+ fail(
15884
+ "MAX_SERIALIZED_BYTES",
15885
+ `JSON-safe value exceeds ${JSON_SAFE_MAX_SERIALIZED_BYTES} serialized bytes`
15886
+ );
15887
+ }
15888
+ return result;
15889
+ }
15890
+
15891
+ // src/util/truncateForJson.ts
15892
+ var TRUNCATION_MARKER = "...[truncated]";
15893
+ function truncateString(value, cap) {
15894
+ if (value.length <= cap) return value;
15895
+ const budget = Math.max(0, cap - TRUNCATION_MARKER.length);
15896
+ return value.slice(0, budget) + TRUNCATION_MARKER;
15897
+ }
15898
+ function truncateValue(value, depth, maxDepth, maxEntries, maxStringLength, seen) {
15899
+ if (typeof value === "string") return truncateString(value, maxStringLength);
15900
+ if (value === null || typeof value !== "object") return value;
15901
+ if (depth >= maxDepth) return "[truncated:max-depth]";
15902
+ if (seen.has(value)) return "[Circular]";
15903
+ seen.add(value);
15904
+ try {
15905
+ if (value instanceof Date) return value.toISOString();
15906
+ if (Array.isArray(value)) {
15907
+ const limit2 = Math.min(value.length, maxEntries);
15908
+ const output2 = [];
15909
+ for (let index = 0; index < limit2; index += 1) {
15910
+ output2.push(truncateValue(value[index], depth + 1, maxDepth, maxEntries, maxStringLength, seen));
15911
+ }
15912
+ if (value.length > limit2) output2.push("[truncated:entries]");
15913
+ return output2;
15914
+ }
15915
+ const record = value;
15916
+ const output = {};
15917
+ const keys = Object.keys(record);
15918
+ const limit = Math.min(keys.length, maxEntries);
15919
+ for (let index = 0; index < limit; index += 1) {
15920
+ const key5 = keys[index];
15921
+ output[truncateString(key5, maxStringLength)] = truncateValue(
15922
+ record[key5],
15923
+ depth + 1,
15924
+ maxDepth,
15925
+ maxEntries,
15926
+ maxStringLength,
15927
+ seen
15928
+ );
15929
+ }
15930
+ if (keys.length > limit) output["[truncated]"] = true;
15931
+ return output;
15932
+ } finally {
15933
+ seen.delete(value);
15934
+ }
15935
+ }
15936
+ function truncateForJson(value, options = {}) {
15937
+ return truncateValue(
15938
+ value,
15939
+ 0,
15940
+ options.maxDepth ?? JSON_SAFE_MAX_DEPTH,
15941
+ options.maxEntries ?? JSON_SAFE_MAX_COLLECTION_ENTRIES,
15942
+ options.maxStringLength ?? JSON_SAFE_MAX_STRING_LENGTH,
15943
+ /* @__PURE__ */ new WeakSet()
15944
+ );
15945
+ }
15946
+
15407
15947
  // src/services/Agent.ts
15408
15948
  import { descriptorDataValue } from "@axiom-lattice/protocols";
15409
15949
  import { Command as Command2 } from "@langchain/langgraph";
@@ -16698,6 +17238,71 @@ var CapabilityRuntimeResolver = class {
16698
17238
  // src/services/TrustedAgentDispatchCapability.ts
16699
17239
  var TRUSTED_AGENT_DISPATCH = Symbol("axiom.trustedAgentDispatch");
16700
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
+
16701
17306
  // src/services/Agent.ts
16702
17307
  var ThreadStatus2 = /* @__PURE__ */ ((ThreadStatus3) => {
16703
17308
  ThreadStatus3["IDLE"] = "idle";
@@ -16945,6 +17550,27 @@ function statusFromSnapshot(state) {
16945
17550
  if (Array.isArray(state.next) && state.next.length > 0) return "busy" /* BUSY */;
16946
17551
  return "idle" /* IDLE */;
16947
17552
  }
17553
+ var MAX_EXECUTION_ERROR_LENGTH = 2e3;
17554
+ function executionErrorFromSnapshot(state) {
17555
+ if (!isPlainRecord(state) || !Array.isArray(state.tasks)) return void 0;
17556
+ for (const task of state.tasks) {
17557
+ if (!isPlainRecord(task) || task.error === void 0 || task.error === null) continue;
17558
+ const raw = task.error;
17559
+ const message = typeof raw === "string" ? raw : raw instanceof Error ? raw.message : isPlainRecord(raw) && typeof raw.message === "string" ? raw.message : (() => {
17560
+ try {
17561
+ return JSON.stringify(raw);
17562
+ } catch {
17563
+ return String(raw);
17564
+ }
17565
+ })();
17566
+ if (typeof message !== "string") continue;
17567
+ const trimmed = message.trim();
17568
+ if (trimmed.length === 0) continue;
17569
+ const bounded = truncateForJson(trimmed, { maxStringLength: MAX_EXECUTION_ERROR_LENGTH });
17570
+ if (typeof bounded === "string" && bounded.length > 0) return bounded;
17571
+ }
17572
+ return void 0;
17573
+ }
16948
17574
  function freshReply(state, preRunMarker, inputMessageId, consumedInputMessageIds) {
16949
17575
  const output = finalAiMessageMarker(state);
16950
17576
  if (output?.text === void 0 || output.index <= (preRunMarker?.index ?? -1) || output.totalMessageCount <= (preRunMarker?.totalMessageCount ?? 0)) return void 0;
@@ -16994,6 +17620,10 @@ function checkpointCapabilityRevision(state) {
16994
17620
  var QUEUE_PROCESSOR_RETRY_BASE_MS = 100;
16995
17621
  var QUEUE_PROCESSOR_MAX_RETRIES = 3;
16996
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;
16997
17627
  function hasExactKeys(record, required, optional = []) {
16998
17628
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
16999
17629
  return required.every((key5) => Object.prototype.hasOwnProperty.call(record, key5)) && Reflect.ownKeys(record).every((key5) => typeof key5 === "string" && allowed.has(key5));
@@ -17023,6 +17653,13 @@ var Agent = class {
17023
17653
  this.queueProcessorRetryAttempt = 0;
17024
17654
  this.queueProcessorGeneration = 0;
17025
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;
17026
17663
  this.asyncTasks = [];
17027
17664
  this.agentExecutor = async ({ input, command, custom_run_config, expected_capability_revision, trusted_run_context }, signal) => {
17028
17665
  const { runnable_agent, runConfig } = await this.getLatticeClientAndRuntimeConfig(
@@ -17139,7 +17776,7 @@ var Agent = class {
17139
17776
  }
17140
17777
  };
17141
17778
  this.waitingForQueueEnd = async (signal) => {
17142
- while (!signal?.aborted) {
17779
+ while (!signal?.aborted && !this.threadRunLease?.lost) {
17143
17780
  const pendings = await this.getPendingMessages();
17144
17781
  if (pendings.length === 0) {
17145
17782
  const state = await this.getCurrentState();
@@ -17224,7 +17861,7 @@ var Agent = class {
17224
17861
  state
17225
17862
  });
17226
17863
  } else if (runStatus === "error" /* ERROR */) {
17227
- const failureReason = "Agent execution ended in an error state";
17864
+ const failureReason = executionErrorFromSnapshot(state) ?? "Agent execution ended in an error state";
17228
17865
  this.addChunk({
17229
17866
  type: "message_failed",
17230
17867
  data: { id: humanContent.id, content: failureReason }
@@ -17337,7 +17974,7 @@ var Agent = class {
17337
17974
  }
17338
17975
  if (runStatus === "busy" /* BUSY */) return;
17339
17976
  if (runStatus === "error" /* ERROR */) {
17340
- const failureReason = "Agent execution ended in an error state";
17977
+ const failureReason = executionErrorFromSnapshot(state) ?? "Agent execution ended in an error state";
17341
17978
  for (const p of collectPendings) {
17342
17979
  const messageId = p.content.id;
17343
17980
  this.addChunk({
@@ -17438,7 +18075,7 @@ var Agent = class {
17438
18075
  });
17439
18076
  if (p.execution_mode === "followup") return;
17440
18077
  } else if (runStatus === "error" /* ERROR */) {
17441
- const failureReason = "Agent execution ended in an error state";
18078
+ const failureReason = executionErrorFromSnapshot(state) ?? "Agent execution ended in an error state";
17442
18079
  this.addChunk({
17443
18080
  type: "message_failed",
17444
18081
  data: { id: humanContent.id, content: failureReason }
@@ -17745,12 +18382,17 @@ var Agent = class {
17745
18382
  capabilityRuntimeRevision: capabilityRuntime.revision,
17746
18383
  tenantId: this.tenant_id,
17747
18384
  workspaceId: this.workspace_id,
17748
- projectId: this.project_id,
18385
+ // The propagated project scope must not fabricate `"default"`:
18386
+ // `queue_project_id` is `null` when the run had no project, so an unscoped
18387
+ // run stays unscoped instead of later querying a project that may not
18388
+ // exist. An explicitly passed `"default"` is a real project for some
18389
+ // tenants and is preserved.
18390
+ projectId: this.queue_project_id ?? void 0,
17749
18391
  assistant_id: this.assistant_id,
17750
18392
  thread_id: this.thread_id,
17751
18393
  "x-tenant-id": this.tenant_id,
17752
18394
  "x-workspace-id": this.workspace_id,
17753
- "x-project-id": this.project_id,
18395
+ "x-project-id": this.queue_project_id ?? void 0,
17754
18396
  "x-thread-id": this.thread_id,
17755
18397
  "x-assistant-id": this.assistant_id
17756
18398
  };
@@ -17965,6 +18607,29 @@ var Agent = class {
17965
18607
  }
17966
18608
  return this.queueStore;
17967
18609
  }
18610
+ /**
18611
+ * Resolves the shared lease store used to serialize thread runs across
18612
+ * Gateway replicas. Returns `undefined` when no lease store is registered so
18613
+ * single-process deployments keep running unconditionally.
18614
+ */
18615
+ getLeaseStore() {
18616
+ try {
18617
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
18618
+ } catch {
18619
+ return void 0;
18620
+ }
18621
+ }
18622
+ /**
18623
+ * Releases this replica's thread-run ownership lease, if held.
18624
+ *
18625
+ * @remarks Safe to call from any definitive-stop path; a second call is a
18626
+ * no-op. The handle's unique holder makes a stale release harmless.
18627
+ */
18628
+ async releaseThreadRunLease() {
18629
+ const lease = this.threadRunLease;
18630
+ this.threadRunLease = null;
18631
+ if (lease) await lease.release();
18632
+ }
17968
18633
  getDefaultQueueConfig() {
17969
18634
  return {
17970
18635
  mode: "collect" /* COLLECT */,
@@ -17990,6 +18655,8 @@ var Agent = class {
17990
18655
  */
17991
18656
  stopQueueProcessor() {
17992
18657
  this.cancelQueueProcessorRetry();
18658
+ this.cancelThreadRunLeaseRetry();
18659
+ void this.releaseThreadRunLease();
17993
18660
  this.clearRecoveryBlock();
17994
18661
  this.queueProcessorGeneration += 1;
17995
18662
  if (this.abortController) {
@@ -18095,7 +18762,15 @@ var Agent = class {
18095
18762
  if (!accepted) throw new Error(`Queue is full (max ${maxSize} messages)`);
18096
18763
  if (useMode === "steer" /* STEER */ && !queueMessage.command) {
18097
18764
  this.stopQueueProcessor();
18098
- await store.resetProcessingToPending(this.thread_id, this.queueScope());
18765
+ let runStatus;
18766
+ try {
18767
+ runStatus = await this.getRunStatus();
18768
+ } catch {
18769
+ runStatus = void 0;
18770
+ }
18771
+ if (runStatus !== "interrupted" /* INTERRUPTED */) {
18772
+ await store.resetProcessingToPending(this.thread_id, this.queueScope());
18773
+ }
18099
18774
  console.log(`[Agent] STEER mode: stopped current processing, reset queue, message ${messageId} will execute next`);
18100
18775
  }
18101
18776
  } else {
@@ -18129,16 +18804,50 @@ var Agent = class {
18129
18804
  startQueueProcessorIfNeeded() {
18130
18805
  return this.startQueueProcessor(true);
18131
18806
  }
18132
- startQueueProcessor(freshWakeup) {
18807
+ async startQueueProcessor(freshWakeup) {
18133
18808
  this.queueWakeRequested = true;
18134
18809
  if (this.queueProcessorPromise) return this.queueProcessorPromise;
18135
- if (this.queueProcessorRetryTimer) return Promise.resolve();
18810
+ if (this.queueProcessorRetryTimer) return;
18136
18811
  if (freshWakeup && this.queueProcessorRetryAttempt >= QUEUE_PROCESSOR_MAX_RETRIES) {
18137
18812
  this.queueProcessorRetryAttempt = 0;
18138
18813
  }
18814
+ if (freshWakeup && this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) {
18815
+ this.threadRunLeaseRetryAttempt = 0;
18816
+ }
18139
18817
  this.queueWakeRequested = false;
18140
- let processorFailed = false;
18141
18818
  const processorGeneration = this.queueProcessorGeneration;
18819
+ if (this.threadRunLease?.lost) await this.releaseThreadRunLease();
18820
+ let acquiredLeaseHere = false;
18821
+ if (this.threadRunLease === null) {
18822
+ const leaseStore = this.getLeaseStore();
18823
+ if (leaseStore) {
18824
+ const lease = await tryAcquireThreadRun({
18825
+ store: leaseStore,
18826
+ tenantId: this.tenant_id,
18827
+ assistantId: this.assistant_id,
18828
+ threadId: this.thread_id,
18829
+ owner: getReplicaId(),
18830
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
18831
+ onLost: () => {
18832
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
18833
+ this.abortController?.abort();
18834
+ }
18835
+ });
18836
+ if (!lease) {
18837
+ this.scheduleThreadRunLeaseRetry();
18838
+ return;
18839
+ }
18840
+ this.threadRunLease = lease;
18841
+ acquiredLeaseHere = true;
18842
+ this.cancelThreadRunLeaseRetry();
18843
+ }
18844
+ }
18845
+ if (this.queueProcessorPromise) return this.queueProcessorPromise;
18846
+ if (this.queueProcessorGeneration !== processorGeneration) {
18847
+ if (acquiredLeaseHere) await this.releaseThreadRunLease();
18848
+ return;
18849
+ }
18850
+ let processorFailed = false;
18142
18851
  const processor = this.runQueueProcessor().catch(async (error) => {
18143
18852
  processorFailed = true;
18144
18853
  console.error(`Queue processing error for thread ${this.thread_id}:`, error);
@@ -18156,7 +18865,11 @@ var Agent = class {
18156
18865
  if (this.queueWakeRequested) void this.startQueueProcessor(false);
18157
18866
  return;
18158
18867
  }
18159
- if (failed) return;
18868
+ if (failed) {
18869
+ this.cancelThreadRunLeaseRetry();
18870
+ await this.releaseThreadRunLease();
18871
+ return;
18872
+ }
18160
18873
  this.queueProcessorRetryAttempt = 0;
18161
18874
  if (this.queueWakeRequested) {
18162
18875
  void this.startQueueProcessor(false);
@@ -18169,7 +18882,11 @@ var Agent = class {
18169
18882
  void this.startQueueProcessor(false);
18170
18883
  return;
18171
18884
  }
18172
- if (pendingMessages.length === 0) return;
18885
+ if (pendingMessages.length === 0) {
18886
+ this.cancelThreadRunLeaseRetry();
18887
+ await this.releaseThreadRunLease();
18888
+ return;
18889
+ }
18173
18890
  const processingMessages = await this.getQueueStore().getProcessingMessages(
18174
18891
  this.thread_id,
18175
18892
  this.queueScope()
@@ -18181,12 +18898,20 @@ var Agent = class {
18181
18898
  }
18182
18899
  if (processingMessages.length > 0) {
18183
18900
  const runStatus = await this.getRunStatus();
18184
- if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) return;
18901
+ if (runStatus === "busy" /* BUSY */ || runStatus === "interrupted" /* INTERRUPTED */) {
18902
+ this.cancelThreadRunLeaseRetry();
18903
+ await this.releaseThreadRunLease();
18904
+ return;
18905
+ }
18185
18906
  }
18186
18907
  void this.startQueueProcessor(false);
18187
18908
  } catch (error) {
18188
18909
  console.error(`Queue processor finalization failed for thread ${this.thread_id}:`, error);
18189
18910
  await this.scheduleQueueProcessorRetry(error, generation);
18911
+ if (!this.queueProcessorPromise) {
18912
+ await this.releaseThreadRunLease();
18913
+ this.cancelThreadRunLeaseRetry();
18914
+ }
18190
18915
  }
18191
18916
  }
18192
18917
  async scheduleQueueProcessorRetry(error, generation) {
@@ -18224,6 +18949,36 @@ var Agent = class {
18224
18949
  this.queueProcessorRetryAttempt = 0;
18225
18950
  this.queueWakeRequested = false;
18226
18951
  }
18952
+ /**
18953
+ * Bounded re-attempt after losing the thread-run lease to a peer. Covers the
18954
+ * narrow window where the peer releases the lease without having observed a
18955
+ * just-enqueued message. Bounded so a genuinely long-held thread does not make
18956
+ * this replica poll forever — the owning replica's loop drains new messages.
18957
+ */
18958
+ scheduleThreadRunLeaseRetry() {
18959
+ if (this.threadRunLeaseRetryTimer || this.threadRunLeaseRetryAttempt >= THREAD_RUN_LEASE_MAX_RETRIES) return;
18960
+ const delay = Math.min(
18961
+ THREAD_RUN_LEASE_RETRY_BASE_MS * 2 ** this.threadRunLeaseRetryAttempt,
18962
+ THREAD_RUN_LEASE_RETRY_CAP_MS
18963
+ );
18964
+ this.threadRunLeaseRetryAttempt += 1;
18965
+ this.threadRunLeaseRetryTimer = setTimeout(() => {
18966
+ this.threadRunLeaseRetryTimer = void 0;
18967
+ void this.startQueueProcessor(false);
18968
+ }, delay);
18969
+ }
18970
+ /**
18971
+ * Clears any pending thread-run lease retry and resets its backoff budget.
18972
+ *
18973
+ * @remarks Called on lease acquisition and on every definitive processor
18974
+ * stop so the next enqueue starts from the base delay. Never called on
18975
+ * continue/re-arm paths, which must preserve the bounded budget.
18976
+ */
18977
+ cancelThreadRunLeaseRetry() {
18978
+ if (this.threadRunLeaseRetryTimer) clearTimeout(this.threadRunLeaseRetryTimer);
18979
+ this.threadRunLeaseRetryTimer = void 0;
18980
+ this.threadRunLeaseRetryAttempt = 0;
18981
+ }
18227
18982
  async runQueueProcessor() {
18228
18983
  const store = this.getQueueStore();
18229
18984
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
@@ -18368,6 +19123,38 @@ var Agent = class {
18368
19123
  console.error(`[Agent] Failed to initialize agent lattice for ${this.assistant_id} (tenant: ${this.tenant_id}):`, error);
18369
19124
  throw error;
18370
19125
  }
19126
+ let acquiredLeaseHere = false;
19127
+ if (this.threadRunLease === null) {
19128
+ const leaseStore = this.getLeaseStore();
19129
+ if (leaseStore) {
19130
+ const lease = await tryAcquireThreadRun({
19131
+ store: leaseStore,
19132
+ tenantId: this.tenant_id,
19133
+ assistantId: this.assistant_id,
19134
+ threadId: this.thread_id,
19135
+ owner: getReplicaId(),
19136
+ ttlMs: THREAD_RUN_LEASE_TTL_MS,
19137
+ onLost: () => {
19138
+ console.warn(`[Agent] Thread-run lease lost for thread ${this.thread_id}; aborting the held run`);
19139
+ this.abortController?.abort();
19140
+ }
19141
+ });
19142
+ if (!lease) {
19143
+ return { status: await this.getRunStatus(), settled: false, leaseContended: true };
19144
+ }
19145
+ this.threadRunLease = lease;
19146
+ acquiredLeaseHere = true;
19147
+ this.cancelThreadRunLeaseRetry();
19148
+ }
19149
+ }
19150
+ try {
19151
+ return await this.resumeTaskLocked();
19152
+ } finally {
19153
+ if (acquiredLeaseHere && !this.queueProcessorPromise) await this.releaseThreadRunLease();
19154
+ }
19155
+ }
19156
+ /** Recovery body of {@link resumeTask}, executed while thread ownership is settled. */
19157
+ async resumeTaskLocked() {
18371
19158
  const store = this.getQueueStore();
18372
19159
  const processingMessages = await store.getProcessingMessages(this.thread_id, this.queueScope());
18373
19160
  const preRunState = await this.getCurrentState();
@@ -18411,7 +19198,7 @@ var Agent = class {
18411
19198
  }
18412
19199
  } else if (resumedStatus === "error" /* ERROR */) {
18413
19200
  this.clearRecoveryBlock();
18414
- const failureReason = "Agent execution ended in an error state";
19201
+ const failureReason = executionErrorFromSnapshot(state) ?? "Agent execution ended in an error state";
18415
19202
  for (const msg of processingMessages) {
18416
19203
  this.addChunk({ type: "message_failed", data: { id: msg.id, content: failureReason } });
18417
19204
  this.publish("message:failed", {
@@ -18447,7 +19234,7 @@ var Agent = class {
18447
19234
  } else if (runStatus === "error" /* ERROR */) {
18448
19235
  this.processingExecutionContext(processingMessages);
18449
19236
  this.clearRecoveryBlock();
18450
- const failureReason = "Agent execution ended in an error state";
19237
+ const failureReason = executionErrorFromSnapshot(preRunState) ?? "Agent execution ended in an error state";
18451
19238
  for (const msg of processingMessages) {
18452
19239
  this.addChunk({ type: "message_failed", data: { id: msg.id, content: failureReason } });
18453
19240
  this.publish("message:failed", {
@@ -18483,6 +19270,8 @@ var Agent = class {
18483
19270
  */
18484
19271
  async abort() {
18485
19272
  this.cancelQueueProcessorRetry();
19273
+ this.cancelThreadRunLeaseRetry();
19274
+ await this.releaseThreadRunLease();
18486
19275
  this.clearRecoveryBlock();
18487
19276
  this.queueProcessorGeneration += 1;
18488
19277
  if (this.abortController) {
@@ -18593,8 +19382,19 @@ var AgentInstanceManager = class _AgentInstanceManager {
18593
19382
  }
18594
19383
  return _AgentInstanceManager._instance;
18595
19384
  }
19385
+ /**
19386
+ * A thread's identity is `(tenant_id, assistant_id, thread_id)`.
19387
+ *
19388
+ * Workspace/project are execution scope chosen when the instance is created,
19389
+ * not part of identity: including them let the same thread exist as several
19390
+ * Agents when callers defaulted optional workspace/project headers
19391
+ * differently (e.g. `createRun` passing `undefined` while `abort` defaulted
19392
+ * to `"default"`), so control-plane calls could address a different instance
19393
+ * than the one running. `undefined`/`null` normalize to `""`; ids are assumed
19394
+ * not to contain `:` (UUID-shaped in practice).
19395
+ */
18596
19396
  getKey(params) {
18597
- return `${params.tenant_id}:${params.assistant_id}:${params.thread_id}:${params.workspace_id}:${params.project_id}`;
19397
+ return [params.tenant_id, params.assistant_id, params.thread_id].map((value) => value ?? "").join(":");
18598
19398
  }
18599
19399
  /**
18600
19400
  * Get or create Agent instance for a thread
@@ -18608,6 +19408,24 @@ var AgentInstanceManager = class _AgentInstanceManager {
18608
19408
  }
18609
19409
  return this.agents.get(key5);
18610
19410
  }
19411
+ /**
19412
+ * Get the managed Agent for a thread WITHOUT creating or registering one.
19413
+ *
19414
+ * Read-only control-plane callers (state, memory, inbox) must not be the first
19415
+ * to establish a thread's execution scope: since identity is
19416
+ * `(tenant_id, assistant_id, thread_id)`, whichever caller reaches
19417
+ * {@link getAgent} first fixes the instance's workspace/project for its whole
19418
+ * lifetime, and a later trusted dispatch on that thread would then fail
19419
+ * trusted-scope validation. This returns the existing managed instance when
19420
+ * present, otherwise a transient unregistered Agent that is sufficient for
19421
+ * state reads and disappears with the request.
19422
+ *
19423
+ * @param params - Thread identity; scope is only used for the transient Agent.
19424
+ * @returns The managed instance if any, else an unregistered read-only Agent.
19425
+ */
19426
+ getAgentForRead(params) {
19427
+ return this.agents.get(this.getKey(params)) ?? new Agent(params);
19428
+ }
18611
19429
  /**
18612
19430
  * Check if an agent instance exists for the thread
18613
19431
  */
@@ -18643,55 +19461,6 @@ var AgentInstanceManager = class _AgentInstanceManager {
18643
19461
  });
18644
19462
  this.agents.clear();
18645
19463
  }
18646
- /**
18647
- * Restore agent instances for threads with pending messages after server restart
18648
- * Queries the message queue store for threads with pending/processing messages
18649
- * and recreates agent instances to resume processing
18650
- */
18651
- async restore() {
18652
- const stats = { restored: 0, errors: 0 };
18653
- try {
18654
- const queueStoreLattice = storeLatticeManager.getStoreLattice("default", "threadMessageQueue");
18655
- const queueStore = queueStoreLattice.store;
18656
- const threadsWithPending = await queueStore.getThreadsWithPendingMessages();
18657
- if (threadsWithPending.length === 0) {
18658
- console.log("[AgentInstanceManager] No threads with pending messages found");
18659
- return stats;
18660
- }
18661
- console.log(`[AgentInstanceManager] Found ${threadsWithPending.length} threads with pending messages, restoring...`);
18662
- for (const threadInfo of threadsWithPending) {
18663
- try {
18664
- await this.restoreThread(threadInfo);
18665
- stats.restored++;
18666
- } catch (error) {
18667
- console.error(`[AgentInstanceManager] Failed to restore thread ${threadInfo.threadId}:`, error);
18668
- stats.errors++;
18669
- }
18670
- }
18671
- console.log(`[AgentInstanceManager] Restore complete: ${stats.restored} restored, ${stats.errors} errors`);
18672
- return stats;
18673
- } catch (error) {
18674
- console.error("[AgentInstanceManager] Restore failed:", error);
18675
- throw error;
18676
- }
18677
- }
18678
- /**
18679
- * Restore a single thread
18680
- * Delegates actual recovery logic to Agent.resumeTask()
18681
- */
18682
- async restoreThread(threadInfo) {
18683
- const { tenantId: tenantId2, assistantId, threadId, workspaceId, projectId } = threadInfo;
18684
- const threadParams = {
18685
- tenant_id: tenantId2,
18686
- assistant_id: assistantId,
18687
- thread_id: threadId,
18688
- workspace_id: workspaceId,
18689
- project_id: projectId
18690
- };
18691
- const agent = this.getAgent(threadParams);
18692
- await agent.resumeTask();
18693
- console.log(`[AgentInstanceManager] Restored thread ${threadId}`);
18694
- }
18695
19464
  };
18696
19465
  var agentInstanceManager = AgentInstanceManager.getInstance();
18697
19466
 
@@ -19162,27 +19931,27 @@ function createCheckAsyncTaskTool() {
19162
19931
  if (!mainAgent) {
19163
19932
  return "Cannot access main agent state.";
19164
19933
  }
19165
- const cached = mainAgent.getAsyncTask(task_id);
19166
- if (!cached) {
19934
+ const cached2 = mainAgent.getAsyncTask(task_id);
19935
+ if (!cached2) {
19167
19936
  return `Task ${task_id} not found. Use list_async_tasks to see all tasks.`;
19168
19937
  }
19169
19938
  try {
19170
19939
  if (!agentInstanceManager.hasAgent({
19171
- assistant_id: cached.assistantId,
19940
+ assistant_id: cached2.assistantId,
19172
19941
  thread_id: task_id,
19173
19942
  tenant_id: mainAgent.tenant_id
19174
19943
  })) {
19175
- return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached.status}`;
19944
+ return `Task ${task_id}: not found (may have been cleaned up). Status was: ${cached2.status}`;
19176
19945
  }
19177
19946
  const subAgent = agentInstanceManager.getAgent({
19178
- assistant_id: cached.assistantId,
19947
+ assistant_id: cached2.assistantId,
19179
19948
  thread_id: task_id,
19180
19949
  tenant_id: mainAgent.tenant_id
19181
19950
  });
19182
19951
  const status = await subAgent.getRunStatus();
19183
19952
  if (status === "busy") {
19184
19953
  return `Task ${task_id}: still running.
19185
- Description: ${cached.description}`;
19954
+ Description: ${cached2.description}`;
19186
19955
  }
19187
19956
  if (status === "idle") {
19188
19957
  const state = await subAgent.getCurrentState();
@@ -19193,21 +19962,21 @@ Description: ${cached.description}`;
19193
19962
  mainAgent.updateAsyncTaskStatus(task_id, "completed");
19194
19963
  const result = lastAI?.content ?? "(completed, no output)";
19195
19964
  return `Task ${task_id}: COMPLETED.
19196
- Description: ${cached.description}
19965
+ Description: ${cached2.description}
19197
19966
  Result:
19198
19967
  ${result}`;
19199
19968
  }
19200
19969
  if (status === "error") {
19201
19970
  mainAgent.updateAsyncTaskStatus(task_id, "failed");
19202
19971
  return `Task ${task_id}: failed.
19203
- Description: ${cached.description}`;
19972
+ Description: ${cached2.description}`;
19204
19973
  }
19205
19974
  if (status === "interrupted") {
19206
19975
  return `Task ${task_id}: interrupted (waiting for input).
19207
- Description: ${cached.description}`;
19976
+ Description: ${cached2.description}`;
19208
19977
  }
19209
19978
  return `Task ${task_id}: ${status}.
19210
- Description: ${cached.description}`;
19979
+ Description: ${cached2.description}`;
19211
19980
  } catch (err) {
19212
19981
  return `Task ${task_id}: error checking status: ${err instanceof Error ? err.message : String(err)}`;
19213
19982
  }
@@ -19280,25 +20049,25 @@ function createCancelAsyncTaskTool() {
19280
20049
  if (!mainAgent) {
19281
20050
  return "Cannot access main agent state.";
19282
20051
  }
19283
- const cached = mainAgent.getAsyncTask(task_id);
19284
- if (!cached) {
20052
+ const cached2 = mainAgent.getAsyncTask(task_id);
20053
+ if (!cached2) {
19285
20054
  return `Task ${task_id} not found.`;
19286
20055
  }
19287
20056
  try {
19288
20057
  const subAgentExists = agentInstanceManager.hasAgent({
19289
- assistant_id: cached.assistantId,
20058
+ assistant_id: cached2.assistantId,
19290
20059
  thread_id: task_id,
19291
20060
  tenant_id: mainAgent.tenant_id
19292
20061
  });
19293
20062
  if (subAgentExists) {
19294
20063
  const subAgent = agentInstanceManager.getAgent({
19295
- assistant_id: cached.assistantId,
20064
+ assistant_id: cached2.assistantId,
19296
20065
  thread_id: task_id,
19297
20066
  tenant_id: mainAgent.tenant_id
19298
20067
  });
19299
20068
  await subAgent.abort();
19300
20069
  agentInstanceManager.removeAgent({
19301
- assistant_id: cached.assistantId,
20070
+ assistant_id: cached2.assistantId,
19302
20071
  thread_id: task_id,
19303
20072
  tenant_id: mainAgent.tenant_id
19304
20073
  });
@@ -19547,6 +20316,48 @@ var MemoryScheduleStorage = class {
19547
20316
  });
19548
20317
  }
19549
20318
  }
20319
+ /**
20320
+ * Atomically claims one due fire by transitioning the task `pending -> running`.
20321
+ *
20322
+ * @param taskId Task to claim.
20323
+ * @returns True when this caller won the claim (task was `pending`); false if
20324
+ * another replica already claimed it or it is not pending.
20325
+ */
20326
+ async claimScheduledRun(taskId) {
20327
+ const task = this.tasks.get(taskId);
20328
+ if (!task || task.status !== ScheduledTaskStatus.PENDING) {
20329
+ return false;
20330
+ }
20331
+ this.tasks.set(taskId, {
20332
+ ...task,
20333
+ status: ScheduledTaskStatus.RUNNING,
20334
+ updatedAt: Date.now()
20335
+ });
20336
+ return true;
20337
+ }
20338
+ /**
20339
+ * Requeues `running` tasks whose `updatedAt` is older than `olderThanMs` back
20340
+ * to `pending` (recovery after a crash that left a claim unsettled).
20341
+ *
20342
+ * @param olderThanMs Age threshold in milliseconds.
20343
+ * @returns The number of tasks requeued.
20344
+ */
20345
+ async requeueStaleRunning(olderThanMs) {
20346
+ const now = Date.now();
20347
+ const cutoff = now - olderThanMs;
20348
+ let requeued = 0;
20349
+ for (const [taskId, task] of this.tasks.entries()) {
20350
+ if (task.status === ScheduledTaskStatus.RUNNING && task.updatedAt < cutoff) {
20351
+ this.tasks.set(taskId, {
20352
+ ...task,
20353
+ status: ScheduledTaskStatus.PENDING,
20354
+ updatedAt: now
20355
+ });
20356
+ requeued++;
20357
+ }
20358
+ }
20359
+ return requeued;
20360
+ }
19550
20361
  /**
19551
20362
  * Delete task
19552
20363
  */
@@ -19911,6 +20722,7 @@ function describeCronExpression(expression) {
19911
20722
  }
19912
20723
 
19913
20724
  // src/schedule_lattice/DefaultScheduleClient.ts
20725
+ var STALE_RUNNING_MS = 30 * 6e4;
19914
20726
  var _DefaultScheduleClient = class _DefaultScheduleClient {
19915
20727
  constructor(storage) {
19916
20728
  this.handlers = /* @__PURE__ */ new Map();
@@ -20151,6 +20963,19 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20151
20963
  }
20152
20964
  // ===== Recovery =====
20153
20965
  async restore() {
20966
+ try {
20967
+ const requeued = await this.storage.requeueStaleRunning(STALE_RUNNING_MS);
20968
+ if (requeued > 0) {
20969
+ console.log(
20970
+ `[Scheduler] Requeued ${requeued} stale running task(s)`
20971
+ );
20972
+ }
20973
+ } catch (error) {
20974
+ console.error(
20975
+ "[Scheduler] Failed to requeue stale running tasks:",
20976
+ error
20977
+ );
20978
+ }
20154
20979
  const activeTasks = await this.storage.getActiveTasks();
20155
20980
  let restored = 0;
20156
20981
  for (const task of activeTasks) {
@@ -20211,6 +21036,16 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20211
21036
  });
20212
21037
  }
20213
21038
  async executeTask(task) {
21039
+ let claimed = false;
21040
+ try {
21041
+ claimed = await this.storage.claimScheduledRun(task.taskId);
21042
+ } catch (error) {
21043
+ console.error(`[Scheduler] Failed to claim task ${task.taskId}:`, error);
21044
+ return;
21045
+ }
21046
+ if (!claimed) {
21047
+ return;
21048
+ }
20214
21049
  const handler = this.handlers.get(task.taskType);
20215
21050
  if (!handler) {
20216
21051
  console.error(
@@ -20222,9 +21057,6 @@ var _DefaultScheduleClient = class _DefaultScheduleClient {
20222
21057
  });
20223
21058
  return;
20224
21059
  }
20225
- await this.storage.update(task.taskId, {
20226
- status: ScheduledTaskStatus2.RUNNING
20227
- });
20228
21060
  console.log(`[Scheduler] Executing task: ${task.taskId}`);
20229
21061
  try {
20230
21062
  const latestTask = await this.storage.get(task.taskId);
@@ -24938,8 +25770,8 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
24938
25770
  * @param key Provider key name
24939
25771
  * @param provider VectorStoreProvider implementation
24940
25772
  */
24941
- registerProvider(key5, provider) {
24942
- const lattice = { key: key5, provider };
25773
+ registerProvider(key5, provider2) {
25774
+ const lattice = { key: key5, provider: provider2 };
24943
25775
  this.registerWithTenant("default", key5, lattice);
24944
25776
  }
24945
25777
  /**
@@ -24975,7 +25807,7 @@ var VectorStoreProviderManager = class _VectorStoreProviderManager extends BaseL
24975
25807
  };
24976
25808
  var vectorStoreProviderManager = VectorStoreProviderManager.getInstance();
24977
25809
  vectorStoreProviderManager.registerProvider("default", new MemoryVectorStoreProvider());
24978
- var registerVectorStoreProvider = (key5, provider) => vectorStoreProviderManager.registerProvider(key5, provider);
25810
+ var registerVectorStoreProvider = (key5, provider2) => vectorStoreProviderManager.registerProvider(key5, provider2);
24979
25811
  var getVectorStoreProvider = (key5) => vectorStoreProviderManager.getProvider(key5);
24980
25812
 
24981
25813
  // src/store_lattice/configureStores.ts
@@ -27677,7 +28509,110 @@ independently before the workflow's integration eval.
27677
28509
 
27678
28510
  An unsupported architecture conversion interrupts the flow and requests a new
27679
28511
  user decision. Do not route backward to the Charter or to [[agent-build]], and do
27680
- not create a replacement.`
28512
+ not create a replacement.`,
28513
+ "ptc": `---
28514
+ name: ptc
28515
+ description: Programmatic Tool Calling (PTC) \u2014 write sandbox code that discovers and calls platform Open capabilities (MCP tools) through the gateway. Use when orchestration is multi-step, data-heavy, or needs loops/branches across platform tools instead of one-at-a-time model tool calls.
28516
+ metadata:
28517
+ role: workflow
28518
+ domain: orchestration
28519
+ verified: unverified
28520
+ ---
28521
+
28522
+ # Programmatic Tool Calling (PTC)
28523
+
28524
+ PTC means the model writes and runs code that calls platform tools itself, instead
28525
+ of calling tools one at a time. Reach for it when orchestration is multi-step,
28526
+ data-heavy, or needs a loop/branch that would be wasteful as a chain of model tool
28527
+ calls.
28528
+
28529
+ ## Where the code runs
28530
+
28531
+ Run the code with \`code_eval\` (backed by \`shell_exec\`). Each execution process is
28532
+ injected with two environment variables:
28533
+
28534
+ - \`API_GATEWAY_URL\` \u2014 the gateway base URL.
28535
+ - \`OPEN_API_TOKEN\` \u2014 a short-lived bearer token **self-bound to this agent**. It is
28536
+ not reusable elsewhere.
28537
+
28538
+ The token already encodes this agent's authority: the platform reflects the agent's
28539
+ own configured middleware into Open grants, plus a small always-on baseline
28540
+ (\`kb\` read-only and \`sandbox:self\`). So the callable set = that baseline \u222A the
28541
+ domains your configured middleware exposes \u2014 and nothing beyond it.
28542
+
28543
+ ## Client
28544
+
28545
+ The sandbox must have the zero-dependency \`@axiom-lattice/open-mcp-client\` package.
28546
+ Get it one of two ways:
28547
+
28548
+ - **Preinstalled in the sandbox image** (recommended): \`open-mcp\` is on \`PATH\`; for
28549
+ \`require("@axiom-lattice/open-mcp-client")\` to resolve, install it into the working
28550
+ directory or point \`NODE_PATH\` at the global \`node_modules\`.
28551
+ - **Install it in this run** when the sandbox has network + npm:
28552
+
28553
+ \`\`\`bash
28554
+ npm install @axiom-lattice/open-mcp-client
28555
+ # or use without installing:
28556
+ npx @axiom-lattice/open-mcp-client tools list --json
28557
+ \`\`\`
28558
+
28559
+ \`\`\`js
28560
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28561
+ const client = OpenMcpClient.fromEnv(); // reads API_GATEWAY_URL + OPEN_API_TOKEN
28562
+ \`\`\`
28563
+
28564
+ CLI equivalents (config comes from the same env vars):
28565
+
28566
+ \`\`\`bash
28567
+ open-mcp tools list --json
28568
+ open-mcp tools call kb_search --json '{"query":"refund policy"}'
28569
+ \`\`\`
28570
+
28571
+ ## Discover, then call
28572
+
28573
+ 1. **Discover first.** Call \`await client.listTools()\`. This returns exactly the
28574
+ tools this agent is authorized to call (the server filters by the token's grants
28575
+ on every request, so it is the source of truth).
28576
+ 2. **Only call discovered tools.** Pick a tool from that list and call
28577
+ \`await client.callTool(name, args)\` with args matching the tool's \`inputSchema\`.
28578
+ 3. **Print the final result as JSON to stdout** so the calling agent can consume it.
28579
+ Keep intermediate logs minimal.
28580
+
28581
+ An unconfigured domain isn't registered, so calling it returns an \`isError\`
28582
+ tool-not-found result; a registered tool refused by the token's grants surfaces
28583
+ \`GRANT_DENIED\`. Either way the capability is **not configured for this agent**.
28584
+ Do not retry or work around it \u2014 drop that step or tell the user which
28585
+ middleware/domain must be enabled. A denied call is expected behavior, not a
28586
+ transient failure.
28587
+
28588
+ ## Tips
28589
+
28590
+ - Fan out with \`Promise.all\` over many items instead of many sequential model turns;
28591
+ that is the main win of PTC.
28592
+ - Inspect \`tool.inputSchema\` before calling to avoid schema-validation errors.
28593
+ - Reduce large results in code and return only the fields the agent needs.
28594
+ - The token is short-lived; for very long jobs re-read the env per \`code_eval\` run
28595
+ rather than caching the token in a file.
28596
+
28597
+ ## Worked example
28598
+
28599
+ \`\`\`js
28600
+ const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
28601
+
28602
+ async function main() {
28603
+ const client = OpenMcpClient.fromEnv(); // API_GATEWAY_URL + OPEN_API_TOKEN
28604
+ const tools = await client.listTools(); // exactly what this agent may call
28605
+ const pick = tools.find((t) => t.name === "kb_search") ?? tools[0];
28606
+ const out = await client.callTool(pick.name, { query: "refund policy" });
28607
+ console.log(JSON.stringify({ called: pick.name, result: out.json() }));
28608
+ }
28609
+
28610
+ main().catch((err) => { console.error(String(err)); process.exit(1); });
28611
+ \`\`\`
28612
+
28613
+ Run this in \`code_eval\`. The first run proves env + client wiring; then extend it
28614
+ into the real fan-out/aggregation the task needs.
28615
+ `
27681
28616
  };
27682
28617
  function getBuiltInSkillMeta(name) {
27683
28618
  const content = BUILTIN_SKILLS[name];
@@ -28274,7 +29209,7 @@ ${body}` : `${frontmatter}
28274
29209
  };
28275
29210
 
28276
29211
  // src/store_lattice/InMemoryMenuStore.ts
28277
- import { randomUUID as randomUUID6 } from "crypto";
29212
+ import { randomUUID as randomUUID7 } from "crypto";
28278
29213
  var InMemoryMenuStore = class {
28279
29214
  constructor() {
28280
29215
  this.items = /* @__PURE__ */ new Map();
@@ -28295,7 +29230,7 @@ var InMemoryMenuStore = class {
28295
29230
  async create(input) {
28296
29231
  const now = /* @__PURE__ */ new Date();
28297
29232
  const item = {
28298
- id: randomUUID6(),
29233
+ id: randomUUID7(),
28299
29234
  tenantId: input.tenantId,
28300
29235
  menuTarget: input.menuTarget,
28301
29236
  group: input.group,
@@ -30807,9 +31742,9 @@ async function getOrCreateCollectionVectorStore(name, embeddingKey, tenantId2, p
30807
31742
  const pendingInitialization = pendingVectorStoreInitializations.get(storeKey);
30808
31743
  if (pendingInitialization) return pendingInitialization;
30809
31744
  const initialization = (async () => {
30810
- const provider = vectorStoreProviderManager.getProvider(providerKey);
31745
+ const provider2 = vectorStoreProviderManager.getProvider(providerKey);
30811
31746
  const tableName = buildTableName(tenantId2, name);
30812
- const vectorStore = await provider.create({ tableName, embeddingKey });
31747
+ const vectorStore = await provider2.create({ tableName, embeddingKey });
30813
31748
  vectorStoreLatticeManager.registerLattice(storeKey, vectorStore);
30814
31749
  return vectorStore;
30815
31750
  })();
@@ -30827,9 +31762,9 @@ async function getCollectionEntryCount(name, embeddingKey, tenantId2) {
30827
31762
  return vs.getDocumentCount();
30828
31763
  }
30829
31764
  try {
30830
- const provider = vectorStoreProviderManager.getProvider("default");
30831
- if (provider.getEntryCount) {
30832
- return provider.getEntryCount(buildTableName(tenantId2, name));
31765
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31766
+ if (provider2.getEntryCount) {
31767
+ return provider2.getEntryCount(buildTableName(tenantId2, name));
30833
31768
  }
30834
31769
  } catch {
30835
31770
  }
@@ -30841,9 +31776,9 @@ async function listCollectionEntries(name, embeddingKey, tenantId2) {
30841
31776
  return vs.getAllDocuments();
30842
31777
  }
30843
31778
  try {
30844
- const provider = vectorStoreProviderManager.getProvider("default");
30845
- if (provider.listEntries) {
30846
- return provider.listEntries(buildTableName(tenantId2, name));
31779
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31780
+ if (provider2.listEntries) {
31781
+ return provider2.listEntries(buildTableName(tenantId2, name));
30847
31782
  }
30848
31783
  } catch {
30849
31784
  }
@@ -30853,9 +31788,9 @@ async function removeCollectionVectorStore(name, tenantId2) {
30853
31788
  const storeKey = buildKey(tenantId2, name);
30854
31789
  const tableName = buildTableName(tenantId2, name);
30855
31790
  try {
30856
- const provider = vectorStoreProviderManager.getProvider("default");
30857
- if (provider.dropTable) {
30858
- await provider.dropTable(tableName);
31791
+ const provider2 = vectorStoreProviderManager.getProvider("default");
31792
+ if (provider2.dropTable) {
31793
+ await provider2.dropTable(tableName);
30859
31794
  }
30860
31795
  } catch {
30861
31796
  }
@@ -33170,7 +34105,7 @@ var DaytonaProvider = class {
33170
34105
 
33171
34106
  // src/sandbox_lattice/providers/LocalSandboxProvider.ts
33172
34107
  import * as path6 from "path";
33173
- import * as os from "os";
34108
+ import * as os2 from "os";
33174
34109
  import * as fs4 from "fs/promises";
33175
34110
 
33176
34111
  // src/sandbox_lattice/LocalSandboxInstance.ts
@@ -33197,6 +34132,32 @@ function assertRegularDeleteTarget(file, stat4) {
33197
34132
  function quoteShellArgument(value) {
33198
34133
  return `'${value.replace(/'/g, "'\\''")}'`;
33199
34134
  }
34135
+ var PLATFORM_SECRET_ENV_VARS = [
34136
+ "OPEN_TOKEN_SECRET",
34137
+ "AUTH_TOKEN_SECRET",
34138
+ "JWT_SECRET",
34139
+ "WEB_APP_ATTACHMENT_SECRET",
34140
+ "LATTICE_ENCRYPTION_KEY",
34141
+ "DATABASE_URL",
34142
+ "REDIS_PASSWORD",
34143
+ "MICROSANDBOX_API_KEY",
34144
+ "DAYTONA_API_KEY",
34145
+ "E2B_API_KEY",
34146
+ "OPENAI_API_KEY",
34147
+ "AZURE_OPENAI_API_KEY",
34148
+ "ANTHROPIC_API_KEY",
34149
+ "TAVILY_API_KEY",
34150
+ "VAPID_PRIVATE_KEY",
34151
+ "SUPABASE_KEY",
34152
+ "A2A_API_KEYS"
34153
+ ];
34154
+ function stripPlatformSecrets(env) {
34155
+ const rest = { ...env };
34156
+ for (const name of PLATFORM_SECRET_ENV_VARS) {
34157
+ delete rest[name];
34158
+ }
34159
+ return rest;
34160
+ }
33200
34161
  function validatePackEntryName(name) {
33201
34162
  if (!name || name === "." || name === ".." || name.includes("\\") || name.includes("/")) {
33202
34163
  throw new Error(`Cannot pack: unsafe entry name '${name}'`);
@@ -33369,7 +34330,7 @@ var LocalSandboxInstance = class {
33369
34330
  timeout: timeoutMs,
33370
34331
  maxBuffer: 10 * 1024 * 1024,
33371
34332
  // 10MB
33372
- env: { ...process.env, HOME: this.rootDir }
34333
+ env: stripPlatformSecrets({ ...process.env, HOME: this.rootDir })
33373
34334
  }
33374
34335
  );
33375
34336
  const output = stderr ? `${stdout}
@@ -33634,7 +34595,7 @@ var LocalSandboxProvider = class {
33634
34595
  this.instances = /* @__PURE__ */ new Map();
33635
34596
  this.creating = /* @__PURE__ */ new Map();
33636
34597
  this.createResourceAddress = createResourceAddress;
33637
- this.basePath = config.basePath ?? path6.join(os.homedir(), ".axiom-local-sandbox");
34598
+ this.basePath = config.basePath ?? path6.join(os2.homedir(), ".axiom-local-sandbox");
33638
34599
  }
33639
34600
  async createSandbox(name, _config) {
33640
34601
  const existing = this.instances.get(name);
@@ -35010,6 +35971,10 @@ var TaskLifecycleNotifier = class {
35010
35971
  this.listeners = /* @__PURE__ */ new Set();
35011
35972
  this.tails = /* @__PURE__ */ new Map();
35012
35973
  }
35974
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
35975
+ setOutbox(outbox2) {
35976
+ this.outbox = outbox2;
35977
+ }
35013
35978
  /**
35014
35979
  * Registers an async lifecycle listener.
35015
35980
  *
@@ -35030,6 +35995,25 @@ var TaskLifecycleNotifier = class {
35030
35995
  * @throws Error when the event is malformed or one or more listeners reject.
35031
35996
  */
35032
35997
  async publish(event) {
35998
+ const snapshot = snapshotNotification(event);
35999
+ const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
36000
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36001
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot));
36002
+ this.tails.set(key5, delivery);
36003
+ if (this.outbox) {
36004
+ try {
36005
+ await this.outbox.append({ topic: "task-lifecycle", payload: snapshot, originReplicaId: getReplicaId() });
36006
+ } catch {
36007
+ }
36008
+ }
36009
+ try {
36010
+ await delivery;
36011
+ } finally {
36012
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36013
+ }
36014
+ }
36015
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36016
+ async deliverFromOutbox(event) {
35033
36017
  const snapshot = snapshotNotification(event);
35034
36018
  const key5 = `${snapshot.tenantId}:${snapshot.taskId}`;
35035
36019
  const previous = this.tails.get(key5) ?? Promise.resolve();
@@ -35094,6 +36078,10 @@ var ProjectTaskAdmissionNotifier = class {
35094
36078
  if (!Number.isSafeInteger(timeout) || timeout <= 0) throw new Error("Invalid admission listener timeout");
35095
36079
  this.listenerTimeoutMs = timeout;
35096
36080
  }
36081
+ /** Installs (or clears) the durable outbox used to fan notifications to other replicas. */
36082
+ setOutbox(outbox2) {
36083
+ this.outbox = outbox2;
36084
+ }
35097
36085
  /**
35098
36086
  * Registers a Project task admission listener.
35099
36087
  *
@@ -35113,6 +36101,30 @@ var ProjectTaskAdmissionNotifier = class {
35113
36101
  * @returns A promise settled after every snapshotted listener settles.
35114
36102
  */
35115
36103
  async publish(event) {
36104
+ const snapshot = snapshotNotification2(event);
36105
+ const listeners = [...this.listeners];
36106
+ const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
36107
+ const previous = this.tails.get(key5) ?? Promise.resolve();
36108
+ const delivery = previous.catch(() => void 0).then(() => this.deliver(snapshot, listeners));
36109
+ this.tails.set(key5, delivery);
36110
+ if (this.outbox) {
36111
+ try {
36112
+ await this.outbox.append({
36113
+ topic: "project-task-admission",
36114
+ payload: snapshot,
36115
+ originReplicaId: getReplicaId()
36116
+ });
36117
+ } catch {
36118
+ }
36119
+ }
36120
+ try {
36121
+ await delivery;
36122
+ } finally {
36123
+ if (this.tails.get(key5) === delivery) this.tails.delete(key5);
36124
+ }
36125
+ }
36126
+ /** Delivers one outbox payload to local listeners without re-appending (poller path). */
36127
+ async deliverFromOutbox(event) {
35116
36128
  const snapshot = snapshotNotification2(event);
35117
36129
  const listeners = [...this.listeners];
35118
36130
  const key5 = `${snapshot.tenantId}\0${snapshot.taskId}`;
@@ -35182,8 +36194,137 @@ function canonicalTimestamp(value) {
35182
36194
  return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
35183
36195
  }
35184
36196
 
36197
+ // src/services/OutboxPoller.ts
36198
+ var OutboxPoller = class {
36199
+ /**
36200
+ * Creates a poller bound to one durable outbox.
36201
+ *
36202
+ * @param deps Store, owner replica id, topic handlers, and optional tuning.
36203
+ */
36204
+ constructor(deps) {
36205
+ this.deps = deps;
36206
+ this.cursor = null;
36207
+ this.polling = false;
36208
+ }
36209
+ /** Captures the starting cursor and begins polling. */
36210
+ async start() {
36211
+ this.cursor = await this.deps.store.latestId();
36212
+ this.timer = setInterval(() => {
36213
+ void this.pollOnce();
36214
+ }, this.deps.intervalMs ?? 1e3);
36215
+ this.timer.unref?.();
36216
+ }
36217
+ /** Stops polling. */
36218
+ stop() {
36219
+ if (this.timer) clearInterval(this.timer);
36220
+ this.timer = void 0;
36221
+ }
36222
+ /** Runs one drain pass (exported for tests). */
36223
+ async pollOnce() {
36224
+ if (this.polling) return;
36225
+ this.polling = true;
36226
+ try {
36227
+ const batchSize = this.deps.batchSize ?? 100;
36228
+ for (; ; ) {
36229
+ const events = await this.deps.store.listAfter(this.cursor, batchSize);
36230
+ if (events.length === 0) break;
36231
+ for (const event of events) {
36232
+ this.cursor = event.id;
36233
+ if (event.originReplicaId === this.deps.owner) continue;
36234
+ const handler = this.deps.handlers[event.topic];
36235
+ if (!handler) continue;
36236
+ try {
36237
+ await this.deliverWithTimeout(handler, event.payload);
36238
+ } catch {
36239
+ this.deps.log?.warn("Outbox delivery failed", { topic: event.topic, id: event.id });
36240
+ }
36241
+ }
36242
+ if (events.length < batchSize) break;
36243
+ }
36244
+ } catch (error) {
36245
+ this.deps.log?.warn("Outbox poll failed", { error: error instanceof Error ? error.message : String(error) });
36246
+ } finally {
36247
+ this.polling = false;
36248
+ }
36249
+ }
36250
+ /**
36251
+ * Runs one handler bounded by `handlerTimeoutMs` so a hung listener cannot
36252
+ * stall cross-replica delivery for this replica.
36253
+ */
36254
+ async deliverWithTimeout(handler, payload) {
36255
+ const timeoutMs = this.deps.handlerTimeoutMs ?? 5e3;
36256
+ let timer;
36257
+ const timeout = new Promise((_resolve, reject) => {
36258
+ timer = setTimeout(() => reject(new Error("Outbox handler timed out")), timeoutMs);
36259
+ timer.unref?.();
36260
+ });
36261
+ try {
36262
+ await Promise.race([Promise.resolve().then(() => handler(payload)), timeout]);
36263
+ } finally {
36264
+ if (timer) clearTimeout(timer);
36265
+ }
36266
+ }
36267
+ };
36268
+
35185
36269
  // src/services/ProjectMutationMutex.ts
35186
36270
  import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
36271
+
36272
+ // src/services/coordinationLease.ts
36273
+ var configured;
36274
+ function configureCoordinationLeaseStore(store) {
36275
+ configured = store;
36276
+ }
36277
+ function getCoordinationLeaseStore() {
36278
+ return configured;
36279
+ }
36280
+
36281
+ // src/services/withLease.ts
36282
+ import { randomUUID as randomUUID8 } from "crypto";
36283
+ var LeaseAcquisitionTimeoutError = class extends Error {
36284
+ /**
36285
+ * @param key Lease key that could not be acquired.
36286
+ * @param holder Unique holder that attempted acquisition.
36287
+ * @param timeoutMs How long the caller waited before giving up.
36288
+ */
36289
+ constructor(key5, holder, timeoutMs) {
36290
+ super(`Could not acquire lease '${key5}' for '${holder}' within ${timeoutMs}ms`);
36291
+ this.key = key5;
36292
+ this.holder = holder;
36293
+ this.timeoutMs = timeoutMs;
36294
+ /** Stable machine-readable code. */
36295
+ this.code = "LEASE_ACQUISITION_TIMEOUT";
36296
+ this.name = "LeaseAcquisitionTimeoutError";
36297
+ }
36298
+ };
36299
+ function sleep(ms) {
36300
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
36301
+ }
36302
+ async function withLease(store, key5, ownerLabel, fn, options = {}) {
36303
+ const ttlMs = options.ttlMs ?? 3e4;
36304
+ const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
36305
+ const retryDelayMs = options.retryDelayMs ?? 50;
36306
+ const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
36307
+ const holder = `${ownerLabel}#${randomUUID8()}`;
36308
+ const deadline = Date.now() + acquireTimeoutMs;
36309
+ for (; ; ) {
36310
+ if (await store.tryAcquire(key5, holder, ttlMs)) break;
36311
+ if (Date.now() >= deadline) throw new LeaseAcquisitionTimeoutError(key5, holder, acquireTimeoutMs);
36312
+ await sleep(retryDelayMs);
36313
+ }
36314
+ const heartbeat = setInterval(() => {
36315
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
36316
+ }, heartbeatMs);
36317
+ heartbeat.unref?.();
36318
+ try {
36319
+ return await fn();
36320
+ } finally {
36321
+ clearInterval(heartbeat);
36322
+ await store.release(key5, holder).catch(() => void 0);
36323
+ }
36324
+ }
36325
+
36326
+ // src/services/ProjectMutationMutex.ts
36327
+ var MUTATION_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
35187
36328
  function projectKey(scope) {
35188
36329
  const row = snapshotExactRecord4(scope, ["tenantId", "projectId"]);
35189
36330
  if (!row || typeof row.tenantId !== "string" || !row.tenantId || row.tenantId.includes("\0") || typeof row.projectId !== "string" || !row.projectId || row.projectId.includes("\0")) {
@@ -35199,9 +36340,12 @@ var ProjectMutationMutex = class {
35199
36340
  /** Runs one mutation after all earlier mutations for the same Tenant and Project. */
35200
36341
  async runExclusive(scope, operation, signal) {
35201
36342
  const key5 = projectKey(scope);
36343
+ const leaseKey = `project-mutation:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.projectId)}`;
35202
36344
  await this.acquire(key5, signal);
35203
36345
  try {
35204
- return await operation();
36346
+ const store = getCoordinationLeaseStore();
36347
+ if (!store) return await operation();
36348
+ return await withLease(store, leaseKey, getReplicaId(), operation, MUTATION_LEASE_OPTIONS);
35205
36349
  } finally {
35206
36350
  this.release(key5);
35207
36351
  }
@@ -35259,7 +36403,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
35259
36403
  }
35260
36404
 
35261
36405
  // src/services/RoomAgentMessageService.ts
35262
- import { randomUUID as randomUUID7 } from "crypto";
36406
+ import { randomUUID as randomUUID9 } from "crypto";
35263
36407
  import { snapshotExactArray, snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
35264
36408
  var INPUT_KEYS = [
35265
36409
  "tenantId",
@@ -35347,7 +36491,7 @@ var RoomAgentMessageService = class {
35347
36491
  throw new Error("Invalid project room membership");
35348
36492
  }
35349
36493
  const candidate = {
35350
- id: (this.deps.idFactory ?? randomUUID7)(),
36494
+ id: (this.deps.idFactory ?? randomUUID9)(),
35351
36495
  tenantId: input.tenantId,
35352
36496
  workspaceId: input.workspaceId,
35353
36497
  projectId: input.projectId,
@@ -35687,6 +36831,8 @@ function buildParentBeliefActivity(input) {
35687
36831
  }
35688
36832
 
35689
36833
  // src/services/TaskLifecycleService.ts
36834
+ var WORK_ITEM_WRITE_ATTEMPTS = 3;
36835
+ var WORK_ITEM_WRITE_RETRY_DELAYS_MS = [20, 50];
35690
36836
  var RETRYABLE_LIFECYCLE_CODES = /* @__PURE__ */ new Set([
35691
36837
  "TASK_STATUS_CONFLICT",
35692
36838
  "TASK_STORE_WRITE_FAILED",
@@ -35706,9 +36852,9 @@ var BELIEF_OWNER_WRITE_ATTEMPTS = 3;
35706
36852
  function isWellFormedImpact(impact) {
35707
36853
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(impact.key) && Number.isInteger(impact.after) && impact.after >= 0 && impact.after <= 100 && impact.basis.trim().length > 0 && impact.basis.length <= 1e3 && !/[\r\n]/.test(impact.basis);
35708
36854
  }
35709
- async function applyBeliefImpacts(taskStore, owner, impacts) {
35710
- if (impacts.length === 0) return { description: owner.description ?? "" };
35711
- let current = owner;
36855
+ async function applyBeliefImpacts(taskStore, owner2, impacts) {
36856
+ if (impacts.length === 0) return { description: owner2.description ?? "" };
36857
+ let current = owner2;
35712
36858
  for (let attempt = 0; attempt < BELIEF_OWNER_WRITE_ATTEMPTS; attempt += 1) {
35713
36859
  const parsed = parseTaskBeliefState(current.description ?? "");
35714
36860
  const existingTable = parsed.success;
@@ -35837,14 +36983,14 @@ function sameBeliefOwner(left, right) {
35837
36983
  function sameBeliefOwnerIdentity(left, right) {
35838
36984
  return canonicalJson({ ...left, beliefState: void 0 }) === canonicalJson({ ...right, beliefState: void 0 });
35839
36985
  }
35840
- function canonicalBeliefOwner(owner) {
36986
+ function canonicalBeliefOwner(owner2) {
35841
36987
  const beliefState = {
35842
- entries: owner.beliefState.entries.map((entry) => ({
36988
+ entries: owner2.beliefState.entries.map((entry) => ({
35843
36989
  ...entry,
35844
36990
  basis: entry.basis.trim().replace(/\s+/g, " ")
35845
36991
  })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0)
35846
36992
  };
35847
- return { ...owner, beliefState };
36993
+ return { ...owner2, beliefState };
35848
36994
  }
35849
36995
  function eventKeyFor(taskId, result, impacts, beliefOwner) {
35850
36996
  const base = `belief-impact:${taskId}:${buildBeliefCompletionEventKey(taskId, result, impacts)}`;
@@ -36053,7 +37199,8 @@ var TaskLifecycleService = class {
36053
37199
  const resumeKey = this.resumeEventKey(task);
36054
37200
  const audit = { eventKey: resumeKey, previousInterruption };
36055
37201
  const context = withLifecycleAudit(task.context, "lifecycle_resume_audit", audit);
36056
- if (input.threadId && !task.context?.thread_id) context.thread_id = input.threadId;
37202
+ if (input.threadId) context.thread_id = input.threadId;
37203
+ if (input.runId) context.run_id = input.runId;
36057
37204
  const updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
36058
37205
  input.tenantId,
36059
37206
  input.taskId,
@@ -36150,7 +37297,13 @@ var TaskLifecycleService = class {
36150
37297
  if (task.status !== "pending") {
36151
37298
  return { ...failure2("TASK_STATUS_CONFLICT", "Task cannot start."), transitioned: false };
36152
37299
  }
36153
- const context = { ...task.context ?? {}, ...input.threadId ? { thread_id: input.threadId } : {} };
37300
+ const context = {
37301
+ ...task.context ?? {},
37302
+ ...input.threadId ? { thread_id: input.threadId } : {},
37303
+ ...input.runId ? { run_id: input.runId } : {},
37304
+ ...input.callerThreadId ? { caller_thread_id: input.callerThreadId } : {},
37305
+ ...input.callerAssistantId ? { caller_assistant_id: input.callerAssistantId } : {}
37306
+ };
36154
37307
  let updated;
36155
37308
  try {
36156
37309
  updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
@@ -36226,7 +37379,9 @@ var TaskLifecycleService = class {
36226
37379
  expectedStatuses: ["failed"],
36227
37380
  updates: { status: "in_progress", failureReason: null },
36228
37381
  summary: "Task retried",
36229
- detail: { previousStatus: "failed" }
37382
+ detail: { previousStatus: "failed" },
37383
+ // A retry dispatches a new run thread; replace a stale persisted link.
37384
+ replaceThread: true
36230
37385
  });
36231
37386
  }
36232
37387
  /**
@@ -37202,8 +38357,8 @@ var TaskLifecycleService = class {
37202
38357
  return { task: existing, parent: parentResult, eventKey, evidence, result, impacts };
37203
38358
  }
37204
38359
  beliefOwnerSnapshot(task, parent) {
37205
- const owner = parent?.ownerType === "agent" ? parent : task;
37206
- const parsed = parseTaskBeliefState(owner.description ?? "");
38360
+ const owner2 = parent?.ownerType === "agent" ? parent : task;
38361
+ const parsed = parseTaskBeliefState(owner2.description ?? "");
37207
38362
  const beliefState = parsed.success ? parsed.state : { entries: [] };
37208
38363
  if (parent?.ownerType === "agent") {
37209
38364
  return {
@@ -37712,7 +38867,9 @@ var TaskLifecycleService = class {
37712
38867
  }
37713
38868
  if (!input.expectedStatuses.includes(task.status)) return failure2("TASK_STATUS_CONFLICT", "Task status cannot make this transition.");
37714
38869
  const clearedContext = clearInterruptionContext(task.context);
37715
- const withThread = input.threadId && task.context?.thread_id === void 0 ? { ...clearedContext ?? {}, thread_id: input.threadId } : clearedContext;
38870
+ const threadInput = input.threadId && (input.replaceThread || task.context?.thread_id === void 0) ? { thread_id: input.threadId } : {};
38871
+ const runInput = input.runId ? { run_id: input.runId } : {};
38872
+ const withThread = Object.keys(threadInput).length > 0 || Object.keys(runInput).length > 0 ? { ...clearedContext ?? {}, ...threadInput, ...runInput } : clearedContext;
37716
38873
  const updates = { ...input.updates, context: withThread };
37717
38874
  let updated;
37718
38875
  try {
@@ -37750,6 +38907,31 @@ var TaskLifecycleService = class {
37750
38907
  statusEventKey(task, status, detail) {
37751
38908
  return `task-lifecycle:${task.id}:${status}:${task.updatedAt.toISOString()}:${sha256(detail)}`;
37752
38909
  }
38910
+ /**
38911
+ * Writes a lifecycle work-item event, retrying transient store failures.
38912
+ *
38913
+ * @param request Event request passed through to the work-item store.
38914
+ * @returns The persisted or pre-existing work item.
38915
+ * @remarks The write is idempotent by `eventKey`
38916
+ * (`createIfAbsentByEventKey`), so retrying after a transient failure (pool
38917
+ * exhaustion, shutdown, statement timeout, disk IO) is safe. The status CAS has
38918
+ * already committed by this point, and the event key embeds `updatedAt`, so a
38919
+ * lost event cannot be reconstructed later — hence the bounded retry.
38920
+ */
38921
+ async writeWorkItemWithRetry(request) {
38922
+ let lastError;
38923
+ for (let attempt = 0; attempt < WORK_ITEM_WRITE_ATTEMPTS; attempt += 1) {
38924
+ if (attempt > 0) {
38925
+ await new Promise((resolve4) => setTimeout(resolve4, WORK_ITEM_WRITE_RETRY_DELAYS_MS[attempt - 1]));
38926
+ }
38927
+ try {
38928
+ return await this.deps.workItemStore.createIfAbsentByEventKey(request);
38929
+ } catch (error) {
38930
+ lastError = error;
38931
+ }
38932
+ }
38933
+ throw lastError;
38934
+ }
37753
38935
  async writeStatusEvent(task, actor, threadId, action, summary, detail, eventKey) {
37754
38936
  const warningCode = `${action.toUpperCase()}_WORK_ITEM_WRITE_FAILED`;
37755
38937
  const request = {
@@ -37765,7 +38947,7 @@ var TaskLifecycleService = class {
37765
38947
  eventKey
37766
38948
  };
37767
38949
  try {
37768
- const item = await this.deps.workItemStore.createIfAbsentByEventKey(request);
38950
+ const item = await this.writeWorkItemWithRetry(request);
37769
38951
  if (item.eventKey !== request.eventKey) throw new Error("Canonical lifecycle event key mismatch");
37770
38952
  return { eventKey: item.eventKey };
37771
38953
  } catch {
@@ -37896,7 +39078,7 @@ function createTaskLifecycleService(deps) {
37896
39078
  }
37897
39079
 
37898
39080
  // src/services/TaskExecutionService.ts
37899
- import { randomUUID as randomUUID8 } from "crypto";
39081
+ import { randomUUID as randomUUID11 } from "crypto";
37900
39082
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
37901
39083
  import { isExecutionResultEventKey as isExecutionResultEventKey2 } from "@axiom-lattice/protocols";
37902
39084
 
@@ -37951,7 +39133,7 @@ function createTaskRunBridge(deps) {
37951
39133
  return { success: true, task: updated };
37952
39134
  },
37953
39135
  async beginRun(input) {
37954
- const { tenantId: tenantId2, actor, threadId } = input;
39136
+ const { tenantId: tenantId2, actor, threadId, runId, callerThreadId, callerAssistantId } = input;
37955
39137
  let task = input.task;
37956
39138
  const warnings = [];
37957
39139
  if (task.status === "pending") {
@@ -37960,7 +39142,10 @@ function createTaskRunBridge(deps) {
37960
39142
  tenantId: tenantId2,
37961
39143
  taskId: task.id,
37962
39144
  actor,
37963
- threadId
39145
+ ...threadId !== void 0 ? { threadId } : {},
39146
+ ...runId !== void 0 ? { runId } : {},
39147
+ ...callerThreadId ? { callerThreadId } : {},
39148
+ ...callerAssistantId ? { callerAssistantId } : {}
37964
39149
  });
37965
39150
  if (!started.success) {
37966
39151
  return {
@@ -38048,7 +39233,19 @@ function createTaskRunBridge(deps) {
38048
39233
  error: "Lifecycle transitions only support agent tasks."
38049
39234
  };
38050
39235
  }
38051
- const result = task.status === "interrupted" ? await deps.lifecycle.resumeInterruption({ tenantId: tenantId2, taskId: task.id, actor, threadId }) : await deps.lifecycle.retryTask({ tenantId: tenantId2, taskId: task.id, actor, threadId });
39236
+ const result = task.status === "interrupted" ? await deps.lifecycle.resumeInterruption({
39237
+ tenantId: tenantId2,
39238
+ taskId: task.id,
39239
+ actor,
39240
+ ...threadId !== void 0 ? { threadId } : {},
39241
+ ...runId !== void 0 ? { runId } : {}
39242
+ }) : await deps.lifecycle.retryTask({
39243
+ tenantId: tenantId2,
39244
+ taskId: task.id,
39245
+ actor,
39246
+ ...threadId !== void 0 ? { threadId } : {},
39247
+ ...runId !== void 0 ? { runId } : {}
39248
+ });
38052
39249
  if (!result.success) {
38053
39250
  return {
38054
39251
  success: false,
@@ -38113,6 +39310,132 @@ function createTaskRunBridge(deps) {
38113
39310
  };
38114
39311
  }
38115
39312
 
39313
+ // src/services/TaskRunRegistry.ts
39314
+ var TaskRunRegistry = class {
39315
+ constructor() {
39316
+ this.live = /* @__PURE__ */ new Set();
39317
+ }
39318
+ /** Marks a task as currently held by a live run. */
39319
+ register(taskId) {
39320
+ this.live.add(taskId);
39321
+ }
39322
+ /** Removes a task once its run has ended. */
39323
+ unregister(taskId) {
39324
+ this.live.delete(taskId);
39325
+ }
39326
+ /** Reports whether a task is currently held by a live run. */
39327
+ isLive(taskId) {
39328
+ return this.live.has(taskId);
39329
+ }
39330
+ /** Returns a snapshot of the live task ids (diagnostics/tests). */
39331
+ snapshot() {
39332
+ return [...this.live];
39333
+ }
39334
+ /** Clears all live entries (tests / process teardown). */
39335
+ clear() {
39336
+ this.live.clear();
39337
+ }
39338
+ };
39339
+ var taskRunRegistry = new TaskRunRegistry();
39340
+ async function safelyLive(probe, task) {
39341
+ try {
39342
+ return await probe(task);
39343
+ } catch {
39344
+ return true;
39345
+ }
39346
+ }
39347
+ async function reconcileStaleTaskRuns(input) {
39348
+ const registry4 = input.registry ?? taskRunRegistry;
39349
+ const converged = [];
39350
+ const convergedTasks = [];
39351
+ for (const task of input.tasks) {
39352
+ if (task.status !== "in_progress" || registry4.isLive(task.id)) continue;
39353
+ if (input.isTaskLive && await safelyLive(input.isTaskLive, task)) continue;
39354
+ const result = await input.lifecycle.failTask({
39355
+ tenantId: task.tenantId,
39356
+ taskId: task.id,
39357
+ actor: input.actor,
39358
+ failureReason: "Run is no longer executing this task (no_executor)"
39359
+ });
39360
+ if (result.success) {
39361
+ converged.push(task.id);
39362
+ convergedTasks.push(result.task);
39363
+ }
39364
+ }
39365
+ return { converged, convergedTasks };
39366
+ }
39367
+ async function reconcileTenantTaskRuns(input) {
39368
+ const tasks = await input.taskStore.list({ tenantId: input.tenantId, status: "in_progress" });
39369
+ const candidates = input.include ? tasks.filter(input.include) : tasks;
39370
+ return reconcileStaleTaskRuns({
39371
+ tasks: candidates,
39372
+ actor: input.actor,
39373
+ lifecycle: input.lifecycle,
39374
+ ...input.registry ? { registry: input.registry } : {},
39375
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
39376
+ });
39377
+ }
39378
+ async function reconcileAllTenantTaskRuns(input) {
39379
+ const tenants = await input.tenantStore.listTenants();
39380
+ const converged = [];
39381
+ const convergedTasks = [];
39382
+ for (const tenant of tenants) {
39383
+ let result;
39384
+ try {
39385
+ result = await reconcileTenantTaskRuns({
39386
+ tenantId: tenant.id,
39387
+ actor: input.actor,
39388
+ taskStore: input.taskStore,
39389
+ lifecycle: input.lifecycle,
39390
+ ...input.registry ? { registry: input.registry } : {},
39391
+ ...input.include ? { include: input.include } : {},
39392
+ ...input.isTaskLive ? { isTaskLive: input.isTaskLive } : {}
39393
+ });
39394
+ } catch {
39395
+ continue;
39396
+ }
39397
+ converged.push(...result.converged);
39398
+ convergedTasks.push(...result.convergedTasks);
39399
+ }
39400
+ return { converged, convergedTasks };
39401
+ }
39402
+
39403
+ // src/services/taskRunLease.ts
39404
+ import { randomUUID as randomUUID10 } from "crypto";
39405
+ function taskRunLeaseKey(taskId) {
39406
+ return `task-run:${taskId}`;
39407
+ }
39408
+ async function tryAcquireTaskRun(input) {
39409
+ const key5 = taskRunLeaseKey(input.taskId);
39410
+ const holder = `${input.owner}#${randomUUID10()}`;
39411
+ if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
39412
+ const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
39413
+ let released = false;
39414
+ let lost = false;
39415
+ const heartbeat = setInterval(() => {
39416
+ void input.store.renew(key5, holder, input.ttlMs).then((renewed) => {
39417
+ if (released || renewed || lost) return;
39418
+ lost = true;
39419
+ try {
39420
+ input.onLost?.();
39421
+ } catch {
39422
+ }
39423
+ }).catch(() => void 0);
39424
+ }, heartbeatMs);
39425
+ heartbeat.unref?.();
39426
+ return {
39427
+ get lost() {
39428
+ return lost;
39429
+ },
39430
+ release: async () => {
39431
+ if (released) return;
39432
+ released = true;
39433
+ clearInterval(heartbeat);
39434
+ await input.store.release(key5, holder).catch(() => void 0);
39435
+ }
39436
+ };
39437
+ }
39438
+
38116
39439
  // src/services/ExecutionAuthorityRegistry.ts
38117
39440
  var ManagedTaskStartError = class extends Error {
38118
39441
  /**
@@ -38374,276 +39697,23 @@ async function prepareTaskDispatch(deps, input) {
38374
39697
  agentId: input.agentId,
38375
39698
  actor: input.actor,
38376
39699
  ...input.callerThreadId !== void 0 ? { callerThreadId: input.callerThreadId } : {},
39700
+ ...input.callerAssistantId !== void 0 ? { callerAssistantId: input.callerAssistantId } : {},
38377
39701
  parent,
38378
39702
  completedDependencies
38379
39703
  }
38380
39704
  };
38381
39705
  }
38382
39706
 
38383
- // src/util/toJsonSafeValue.ts
38384
- var CIRCULAR_VALUE = "[Circular]";
38385
- var REPEATED_VALUE = "[Repeated]";
38386
- var arrayFrom = Array.from.bind(Array);
38387
- var arrayIsArray = Array.isArray.bind(Array);
38388
- var hasOwnProperty = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
38389
- var isPrototypeOf = Object.prototype.isPrototypeOf.call.bind(Object.prototype.isPrototypeOf);
38390
- var objectCreate = Object.create.bind(Object);
38391
- var objectDefineProperty = Object.defineProperty.bind(Object);
38392
- var objectIs = Object.is.bind(Object);
38393
- var reflectGet = Reflect.get.bind(Reflect);
38394
- var reflectGetOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor.bind(Reflect);
38395
- var reflectOwnKeys = Reflect.ownKeys.bind(Reflect);
38396
- var stringifyJson = JSON.stringify.bind(JSON);
38397
- var numberIsFinite = Number.isFinite.bind(Number);
38398
- var numberIsNaN = Number.isNaN.bind(Number);
38399
- var numberIsSafeInteger = Number.isSafeInteger.bind(Number);
38400
- var mathMax = Math.max.bind(Math);
38401
- var dateConstructor = Date;
38402
- var datePrototype = dateConstructor.prototype;
38403
- var dateGetTime = datePrototype.getTime.call.bind(datePrototype.getTime);
38404
- var dateToISOString = datePrototype.toISOString.call.bind(datePrototype.toISOString);
38405
- var bigintToString = BigInt.prototype.toString.call.bind(BigInt.prototype.toString);
38406
- var stringSlice = String.prototype.slice.call.bind(String.prototype.slice);
38407
- var weakMapGet = WeakMap.prototype.get.call.bind(WeakMap.prototype.get);
38408
- var weakMapSet = WeakMap.prototype.set.call.bind(WeakMap.prototype.set);
38409
- var WeakMapConstructor = WeakMap;
38410
- var utf8Encoder = new TextEncoder();
38411
- var encodeUtf8 = TextEncoder.prototype.encode.bind(utf8Encoder);
38412
- var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
38413
- var typedArrayByteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")?.get;
38414
- if (!typedArrayByteLengthGetter) throw new Error("TypedArray byteLength getter is unavailable");
38415
- var typedArrayByteLength = typedArrayByteLengthGetter.call.bind(typedArrayByteLengthGetter);
38416
- var JSON_SAFE_MAX_DEPTH = 32;
38417
- var JSON_SAFE_MAX_NODES = 1e4;
38418
- var JSON_SAFE_MAX_COLLECTION_ENTRIES = 1e4;
38419
- var JSON_SAFE_MAX_STRING_LENGTH = 64 * 1024;
38420
- var JSON_SAFE_MAX_PROPERTY_KEY_LENGTH = JSON_SAFE_MAX_STRING_LENGTH;
38421
- var JSON_SAFE_MAX_PROPERTY_KEY_BYTES = 256 * 1024;
38422
- var JSON_SAFE_MAX_SERIALIZED_BYTES = 256 * 1024;
38423
- var JsonSafeValueError = class extends Error {
38424
- /**
38425
- * Create a bounded normalization error.
38426
- *
38427
- * @param code Stable normalization failure code.
38428
- * @param message Human-readable bounded failure description.
38429
- */
38430
- constructor(code, message) {
38431
- super(message);
38432
- objectDefineProperty(this, "name", {
38433
- value: "JsonSafeValueError",
38434
- writable: true,
38435
- configurable: true
38436
- });
38437
- objectDefineProperty(this, "code", {
38438
- value: code,
38439
- writable: true,
38440
- configurable: true
38441
- });
38442
- }
38443
- };
38444
- function fail(code, message) {
38445
- throw new JsonSafeValueError(code, message);
38446
- }
38447
- function propertyAccess(operation, path8) {
39707
+ // src/services/TaskExecutionService.ts
39708
+ import { GraphInterrupt as GraphInterrupt2 } from "@langchain/langgraph";
39709
+ var TASK_RUN_LEASE_TTL_MS = 12e4;
39710
+ function getLeaseStore() {
38448
39711
  try {
38449
- return operation();
39712
+ return storeLatticeManager.getStoreLattice("default", "lease").store;
38450
39713
  } catch {
38451
- fail("PROPERTY_ACCESS_FAILED", `Unable to inspect JSON-safe value at ${path8}`);
38452
- }
38453
- }
38454
- function serializedStringBytes(value) {
38455
- if (value.length > JSON_SAFE_MAX_STRING_LENGTH) {
38456
- fail("MAX_STRING_LENGTH", `JSON-safe string exceeds ${JSON_SAFE_MAX_STRING_LENGTH} characters`);
38457
- }
38458
- return typedArrayByteLength(encodeUtf8(stringifyJson(value)));
38459
- }
38460
- function validatePropertyKey(key5) {
38461
- if (key5.length > JSON_SAFE_MAX_PROPERTY_KEY_LENGTH) {
38462
- fail(
38463
- "MAX_STRING_LENGTH",
38464
- `JSON-safe property key exceeds ${JSON_SAFE_MAX_PROPERTY_KEY_LENGTH} characters`
38465
- );
38466
- }
38467
- }
38468
- function chargePropertyKey(state, key5) {
38469
- state.propertyKeyBytes += typedArrayByteLength(encodeUtf8(key5));
38470
- if (state.propertyKeyBytes > JSON_SAFE_MAX_PROPERTY_KEY_BYTES) {
38471
- fail(
38472
- "MAX_PROPERTY_KEY_BYTES",
38473
- `JSON-safe property keys exceed ${JSON_SAFE_MAX_PROPERTY_KEY_BYTES} traversed UTF-8 bytes`
38474
- );
38475
- }
38476
- }
38477
- function chargeBytes(state, bytes) {
38478
- state.serializedBytes += bytes;
38479
- if (state.serializedBytes > JSON_SAFE_MAX_SERIALIZED_BYTES) {
38480
- fail(
38481
- "MAX_SERIALIZED_BYTES",
38482
- `JSON-safe value exceeds ${JSON_SAFE_MAX_SERIALIZED_BYTES} serialized bytes`
38483
- );
38484
- }
38485
- }
38486
- function chargeString(state, value) {
38487
- chargeBytes(state, serializedStringBytes(value));
38488
- }
38489
- function objectPropertyPath(path8, key5) {
38490
- const boundedKey = key5.length > 128 ? `${stringSlice(key5, 0, 128)}...` : key5;
38491
- return `${path8}[${stringifyJson(boundedKey)}]`;
38492
- }
38493
- function convertJsonValue(value, state, depth, inArray, path8) {
38494
- state.nodes += 1;
38495
- if (state.nodes > JSON_SAFE_MAX_NODES) {
38496
- fail("MAX_NODES", `JSON-safe value exceeds ${JSON_SAFE_MAX_NODES} total nodes`);
38497
- }
38498
- if (typeof value === "string") {
38499
- chargeString(state, value);
38500
- return value;
38501
- }
38502
- if (value === null) {
38503
- chargeBytes(state, 4);
38504
- return value;
38505
- }
38506
- if (typeof value === "boolean") {
38507
- chargeBytes(state, value ? 4 : 5);
38508
- return value;
38509
- }
38510
- if (typeof value === "number") {
38511
- const normalized = numberIsFinite(value) ? objectIs(value, -0) ? 0 : value : null;
38512
- chargeBytes(state, normalized === null ? 4 : stringifyJson(normalized).length);
38513
- return normalized;
38514
- }
38515
- if (typeof value === "bigint") {
38516
- const normalized = bigintToString(value);
38517
- chargeString(state, normalized);
38518
- return normalized;
38519
- }
38520
- if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
38521
- if (inArray) chargeBytes(state, 4);
38522
- return inArray ? null : void 0;
38523
- }
38524
- const isDate = propertyAccess(() => isPrototypeOf(datePrototype, value), path8);
38525
- if (isDate) {
38526
- const time = propertyAccess(() => dateGetTime(value), path8);
38527
- if (numberIsNaN(time)) {
38528
- chargeBytes(state, 4);
38529
- return null;
38530
- }
38531
- const normalized = propertyAccess(() => dateToISOString(value), path8);
38532
- chargeString(state, normalized);
38533
- return normalized;
38534
- }
38535
- if (depth > JSON_SAFE_MAX_DEPTH) {
38536
- fail("MAX_DEPTH", `JSON-safe value exceeds maximum depth ${JSON_SAFE_MAX_DEPTH}`);
38537
- }
38538
- const referenceState = weakMapGet(state.references, value);
38539
- if (referenceState === "visiting") {
38540
- chargeString(state, CIRCULAR_VALUE);
38541
- return CIRCULAR_VALUE;
38542
- }
38543
- if (referenceState === "visited") {
38544
- chargeString(state, REPEATED_VALUE);
38545
- return REPEATED_VALUE;
38546
- }
38547
- weakMapSet(state.references, value, "visiting");
38548
- if (propertyAccess(() => arrayIsArray(value), path8)) {
38549
- const arrayValue = value;
38550
- const length = propertyAccess(
38551
- () => arrayValue.length,
38552
- path8
38553
- );
38554
- if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 0) {
38555
- fail("PROPERTY_ACCESS_FAILED", `Unable to inspect JSON-safe array length at ${path8}`);
38556
- }
38557
- if (length > JSON_SAFE_MAX_COLLECTION_ENTRIES) {
38558
- fail(
38559
- "COLLECTION_TOO_LARGE",
38560
- `JSON-safe array exceeds ${JSON_SAFE_MAX_COLLECTION_ENTRIES} entries`
38561
- );
38562
- }
38563
- chargeBytes(state, 2 + mathMax(0, length - 1));
38564
- const result2 = arrayFrom({ length }, (_, index) => {
38565
- const entryPath = `${path8}[${index}]`;
38566
- const hasEntry = propertyAccess(
38567
- () => hasOwnProperty(arrayValue, index),
38568
- entryPath
38569
- );
38570
- if (!hasEntry) {
38571
- chargeBytes(state, 4);
38572
- return null;
38573
- }
38574
- const entry = propertyAccess(() => arrayValue[index], entryPath);
38575
- return convertJsonValue(entry, state, depth + 1, true, `${path8}[${index}]`);
38576
- });
38577
- objectDefineProperty(result2, "toJSON", {
38578
- value: void 0,
38579
- configurable: true,
38580
- writable: true
38581
- });
38582
- weakMapSet(state.references, value, "visited");
38583
- return result2;
38584
- }
38585
- const keys = propertyAccess(() => reflectOwnKeys(value), path8);
38586
- if (keys.length > JSON_SAFE_MAX_COLLECTION_ENTRIES) {
38587
- fail(
38588
- "COLLECTION_TOO_LARGE",
38589
- `JSON-safe object exceeds ${JSON_SAFE_MAX_COLLECTION_ENTRIES} own keys`
38590
- );
38591
- }
38592
- for (let index = 0; index < keys.length; index += 1) {
38593
- const key5 = keys[index];
38594
- if (typeof key5 !== "string") continue;
38595
- validatePropertyKey(key5);
38596
- chargePropertyKey(state, key5);
38597
- }
38598
- chargeBytes(state, 2);
38599
- const result = objectCreate(null);
38600
- let emittedProperties = 0;
38601
- for (let index = 0; index < keys.length; index += 1) {
38602
- const key5 = keys[index];
38603
- const entryPath = typeof key5 === "string" ? objectPropertyPath(path8, key5) : path8;
38604
- const descriptor = propertyAccess(
38605
- () => reflectGetOwnPropertyDescriptor(value, key5),
38606
- entryPath
38607
- );
38608
- if (typeof key5 !== "string" || !descriptor?.enumerable) continue;
38609
- const entry = propertyAccess(() => reflectGet(value, key5), entryPath);
38610
- const converted = convertJsonValue(entry, state, depth + 1, false, entryPath);
38611
- if (converted !== void 0) {
38612
- chargeBytes(state, serializedStringBytes(key5) + 1 + (emittedProperties === 0 ? 0 : 1));
38613
- propertyAccess(() => objectDefineProperty(result, key5, {
38614
- value: converted,
38615
- enumerable: true,
38616
- configurable: true,
38617
- writable: true
38618
- }), entryPath);
38619
- emittedProperties += 1;
38620
- }
38621
- }
38622
- weakMapSet(state.references, value, "visited");
38623
- return result;
38624
- }
38625
- function toJsonSafeValue(value) {
38626
- const state = {
38627
- nodes: 0,
38628
- propertyKeyBytes: 0,
38629
- serializedBytes: 0,
38630
- references: new WeakMapConstructor()
38631
- };
38632
- const converted = convertJsonValue(value, state, 0, false, "$");
38633
- if (converted === void 0) chargeBytes(state, 4);
38634
- const result = converted ?? null;
38635
- const serialized = propertyAccess(() => stringifyJson(result), "$");
38636
- if (typedArrayByteLength(encodeUtf8(serialized)) > JSON_SAFE_MAX_SERIALIZED_BYTES) {
38637
- fail(
38638
- "MAX_SERIALIZED_BYTES",
38639
- `JSON-safe value exceeds ${JSON_SAFE_MAX_SERIALIZED_BYTES} serialized bytes`
38640
- );
39714
+ return void 0;
38641
39715
  }
38642
- return result;
38643
39716
  }
38644
-
38645
- // src/services/TaskExecutionService.ts
38646
- import { GraphInterrupt as GraphInterrupt2 } from "@langchain/langgraph";
38647
39717
  function persistedExecutionObservation(item, attempted) {
38648
39718
  const detail = item.detail;
38649
39719
  const persistedObservation = toJsonSafeValue(detail?.observation);
@@ -38690,10 +39760,26 @@ function createTaskExecutionService(deps) {
38690
39760
  const run = async (plan) => {
38691
39761
  const { tenantId: tenantId2, agentId, actor, parent, completedDependencies } = plan;
38692
39762
  let task = plan.task;
38693
- const persistedThreadId = (task.status === "interrupted" || task.status === "failed") && typeof task.context?.thread_id === "string" && task.context.thread_id.trim() !== "" ? task.context.thread_id : void 0;
39763
+ const storedThreadId = (task.status === "interrupted" || task.status === "failed") && typeof task.context?.thread_id === "string" && task.context.thread_id.trim() !== "" ? task.context.thread_id : void 0;
39764
+ const storedRunId = typeof task.context?.run_id === "string" && task.context.run_id.trim() !== "" ? task.context.run_id : void 0;
39765
+ let reuseCurrentRun = false;
39766
+ if (task.status === "interrupted" && storedThreadId) {
39767
+ const currentKey = storedRunId ? `execution-result:${storedThreadId}:${storedRunId}` : `execution-result:${storedThreadId}`;
39768
+ try {
39769
+ reuseCurrentRun = await deps.workItemStore.findByEventKey(tenantId2, task.id, currentKey) === null;
39770
+ } catch {
39771
+ reuseCurrentRun = false;
39772
+ }
39773
+ }
38694
39774
  let threadId;
39775
+ let runId;
38695
39776
  try {
38696
- threadId = persistedThreadId ?? (deps.createThreadId ?? randomUUID8)();
39777
+ threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID11)();
39778
+ if (task.status === "interrupted" && reuseCurrentRun) {
39779
+ runId = storedRunId;
39780
+ } else if (task.status === "interrupted" || task.status === "failed") {
39781
+ runId = (deps.createRunId ?? randomUUID11)();
39782
+ }
38697
39783
  } catch (error) {
38698
39784
  return {
38699
39785
  success: false,
@@ -38703,7 +39789,7 @@ function createTaskExecutionService(deps) {
38703
39789
  agentId
38704
39790
  };
38705
39791
  }
38706
- const eventKey = `execution-result:${threadId}`;
39792
+ const eventKey = runId ? `execution-result:${threadId}:${runId}` : `execution-result:${threadId}`;
38707
39793
  if (!isExecutionResultEventKey2(eventKey)) {
38708
39794
  return {
38709
39795
  success: false,
@@ -38737,7 +39823,10 @@ function createTaskExecutionService(deps) {
38737
39823
  tenantId: tenantId2,
38738
39824
  task,
38739
39825
  actor,
38740
- threadId
39826
+ threadId,
39827
+ ...runId ? { runId } : {},
39828
+ ...plan.callerThreadId ? { callerThreadId: plan.callerThreadId } : {},
39829
+ ...plan.callerAssistantId ? { callerAssistantId: plan.callerAssistantId } : {}
38741
39830
  });
38742
39831
  if (!begun.success) return { ...begun, taskId: task.id };
38743
39832
  if (!begun.mutated || !begun.transitioned) {
@@ -38751,38 +39840,134 @@ function createTaskExecutionService(deps) {
38751
39840
  task = begun.task;
38752
39841
  warnings.push(...begun.warnings);
38753
39842
  }
38754
- const invocation = {
38755
- tenantId: tenantId2,
38756
- workspaceId: task.workspaceId,
38757
- projectId: task.projectId,
38758
- taskId: task.id,
38759
- agentId,
38760
- threadId,
38761
- message: buildWorkerMessage(task, parent, completedDependencies),
38762
- delegatedTaskId: task.id,
38763
- taskExecutionRole: "worker"
38764
- };
38765
- let observation;
39843
+ taskRunRegistry.register(task.id);
39844
+ let taskRunLease = null;
38766
39845
  try {
38767
- observation = toJsonSafeValue(await deps.invokeAgent(invocation));
38768
- } catch (error) {
38769
- if (error instanceof GraphInterrupt2) {
39846
+ const leaseStore = getLeaseStore();
39847
+ if (leaseStore) {
39848
+ try {
39849
+ taskRunLease = await tryAcquireTaskRun({
39850
+ store: leaseStore,
39851
+ taskId: task.id,
39852
+ owner: getReplicaId(),
39853
+ ttlMs: TASK_RUN_LEASE_TTL_MS
39854
+ });
39855
+ } catch {
39856
+ taskRunLease = null;
39857
+ }
39858
+ }
39859
+ const invocation = {
39860
+ tenantId: tenantId2,
39861
+ workspaceId: task.workspaceId,
39862
+ projectId: task.projectId,
39863
+ taskId: task.id,
39864
+ agentId,
39865
+ threadId,
39866
+ message: buildWorkerMessage(task, parent, completedDependencies),
39867
+ delegatedTaskId: task.id,
39868
+ taskExecutionRole: "worker"
39869
+ };
39870
+ let observation;
39871
+ try {
39872
+ observation = toJsonSafeValue(await deps.invokeAgent(invocation));
39873
+ } catch (error) {
39874
+ if (error instanceof GraphInterrupt2) {
39875
+ try {
39876
+ await bridge.settleRun({
39877
+ tenantId: tenantId2,
39878
+ task,
39879
+ actor,
39880
+ threadId,
39881
+ outcome: { kind: "interrupted", summary: "Awaiting user input" }
39882
+ });
39883
+ } catch {
39884
+ }
39885
+ throw error;
39886
+ }
39887
+ const normalizationError = error instanceof JsonSafeValueError;
39888
+ const message = normalizationError ? `${error.code}: ${error.message}` : errorMessage(error);
39889
+ const failureCode = normalizationError ? "EXECUTION_RESULT_NORMALIZATION_FAILED" : "AGENT_INVOCATION_FAILED";
38770
39890
  try {
38771
- await bridge.settleRun({
39891
+ const settled = await bridge.settleRun({
38772
39892
  tenantId: tenantId2,
38773
39893
  task,
38774
39894
  actor,
38775
39895
  threadId,
38776
- outcome: { kind: "interrupted", summary: "Awaiting user input" }
39896
+ outcome: {
39897
+ kind: "failed",
39898
+ lifecycleHandled: false,
39899
+ error: message.trim() || "Agent invocation failed"
39900
+ }
38777
39901
  });
39902
+ if (!settled.success) {
39903
+ warnings.push({ code: "TASK_FAILED_SETTLE_FAILED", taskId: task.id });
39904
+ }
38778
39905
  } catch {
39906
+ warnings.push({ code: "TASK_FAILED_SETTLE_FAILED", taskId: task.id });
39907
+ }
39908
+ const failedObservation = toJsonSafeValue(normalizationError ? { code: failureCode, error: message } : { error: message });
39909
+ try {
39910
+ const persisted = await deps.workItemStore.createIfAbsentByEventKey({
39911
+ taskId: task.id,
39912
+ tenantId: tenantId2,
39913
+ workspaceId: task.workspaceId,
39914
+ projectId: task.projectId,
39915
+ action: "execution_result",
39916
+ actor,
39917
+ threadId,
39918
+ summary: normalizationError ? `Agent ${agentId} returned an invalid execution observation` : `Agent ${agentId} invocation failed`,
39919
+ detail: {
39920
+ agentId,
39921
+ threadId,
39922
+ observation: failedObservation,
39923
+ success: false
39924
+ },
39925
+ eventKey
39926
+ });
39927
+ const validated = persistedExecutionObservation(persisted, {
39928
+ tenantId: tenantId2,
39929
+ taskId: task.id,
39930
+ agentId,
39931
+ threadId,
39932
+ eventKey,
39933
+ success: false,
39934
+ observation: failedObservation
39935
+ });
39936
+ if (!validated.matches) {
39937
+ return {
39938
+ success: false,
39939
+ code: "EXECUTION_RESULT_IDENTITY_CONFLICT",
39940
+ error: "The execution result identity is already bound to a different execution payload",
39941
+ ...!normalizationError ? { invocationError: message } : {},
39942
+ taskId: task.id,
39943
+ agentId,
39944
+ threadId,
39945
+ ...warnings.length > 0 ? { warnings } : {}
39946
+ };
39947
+ }
39948
+ return {
39949
+ success: false,
39950
+ code: failureCode,
39951
+ error: typeof validated.observation?.error === "string" ? validated.observation.error : message,
39952
+ taskId: task.id,
39953
+ agentId,
39954
+ threadId,
39955
+ executionResultId: eventKey,
39956
+ ...warnings.length > 0 ? { warnings } : {}
39957
+ };
39958
+ } catch (persistenceError) {
39959
+ return {
39960
+ success: false,
39961
+ code: "EXECUTION_RESULT_WRITE_FAILED",
39962
+ error: errorMessage(persistenceError),
39963
+ ...!normalizationError ? { invocationError: message } : {},
39964
+ taskId: task.id,
39965
+ agentId,
39966
+ threadId,
39967
+ ...warnings.length > 0 ? { warnings } : {}
39968
+ };
38779
39969
  }
38780
- throw error;
38781
39970
  }
38782
- const normalizationError = error instanceof JsonSafeValueError;
38783
- const message = normalizationError ? `${error.code}: ${error.message}` : errorMessage(error);
38784
- const failureCode = normalizationError ? "EXECUTION_RESULT_NORMALIZATION_FAILED" : "AGENT_INVOCATION_FAILED";
38785
- const failedObservation = toJsonSafeValue(normalizationError ? { code: failureCode, error: message } : { error: message });
38786
39971
  try {
38787
39972
  const persisted = await deps.workItemStore.createIfAbsentByEventKey({
38788
39973
  taskId: task.id,
@@ -38792,13 +39977,8 @@ function createTaskExecutionService(deps) {
38792
39977
  action: "execution_result",
38793
39978
  actor,
38794
39979
  threadId,
38795
- summary: normalizationError ? `Agent ${agentId} returned an invalid execution observation` : `Agent ${agentId} invocation failed`,
38796
- detail: {
38797
- agentId,
38798
- threadId,
38799
- observation: failedObservation,
38800
- success: false
38801
- },
39980
+ summary: `Agent ${agentId} returned an execution observation`,
39981
+ detail: { agentId, threadId, observation, success: true },
38802
39982
  eventKey
38803
39983
  });
38804
39984
  const validated = persistedExecutionObservation(persisted, {
@@ -38807,15 +39987,14 @@ function createTaskExecutionService(deps) {
38807
39987
  agentId,
38808
39988
  threadId,
38809
39989
  eventKey,
38810
- success: false,
38811
- observation: failedObservation
39990
+ success: true,
39991
+ observation
38812
39992
  });
38813
39993
  if (!validated.matches) {
38814
39994
  return {
38815
39995
  success: false,
38816
39996
  code: "EXECUTION_RESULT_IDENTITY_CONFLICT",
38817
39997
  error: "The execution result identity is already bound to a different execution payload",
38818
- ...!normalizationError ? { invocationError: message } : {},
38819
39998
  taskId: task.id,
38820
39999
  agentId,
38821
40000
  threadId,
@@ -38823,80 +40002,28 @@ function createTaskExecutionService(deps) {
38823
40002
  };
38824
40003
  }
38825
40004
  return {
38826
- success: false,
38827
- code: failureCode,
38828
- error: typeof validated.observation?.error === "string" ? validated.observation.error : message,
40005
+ success: true,
38829
40006
  taskId: task.id,
38830
40007
  agentId,
38831
40008
  threadId,
40009
+ observation: validated.observation,
38832
40010
  executionResultId: eventKey,
38833
40011
  ...warnings.length > 0 ? { warnings } : {}
38834
40012
  };
38835
- } catch (persistenceError) {
40013
+ } catch (error) {
38836
40014
  return {
38837
40015
  success: false,
38838
40016
  code: "EXECUTION_RESULT_WRITE_FAILED",
38839
- error: errorMessage(persistenceError),
38840
- ...!normalizationError ? { invocationError: message } : {},
38841
- taskId: task.id,
38842
- agentId,
38843
- threadId,
38844
- ...warnings.length > 0 ? { warnings } : {}
38845
- };
38846
- }
38847
- }
38848
- try {
38849
- const persisted = await deps.workItemStore.createIfAbsentByEventKey({
38850
- taskId: task.id,
38851
- tenantId: tenantId2,
38852
- workspaceId: task.workspaceId,
38853
- projectId: task.projectId,
38854
- action: "execution_result",
38855
- actor,
38856
- threadId,
38857
- summary: `Agent ${agentId} returned an execution observation`,
38858
- detail: { agentId, threadId, observation, success: true },
38859
- eventKey
38860
- });
38861
- const validated = persistedExecutionObservation(persisted, {
38862
- tenantId: tenantId2,
38863
- taskId: task.id,
38864
- agentId,
38865
- threadId,
38866
- eventKey,
38867
- success: true,
38868
- observation
38869
- });
38870
- if (!validated.matches) {
38871
- return {
38872
- success: false,
38873
- code: "EXECUTION_RESULT_IDENTITY_CONFLICT",
38874
- error: "The execution result identity is already bound to a different execution payload",
40017
+ error: errorMessage(error),
38875
40018
  taskId: task.id,
38876
40019
  agentId,
38877
40020
  threadId,
38878
40021
  ...warnings.length > 0 ? { warnings } : {}
38879
40022
  };
38880
40023
  }
38881
- return {
38882
- success: true,
38883
- taskId: task.id,
38884
- agentId,
38885
- threadId,
38886
- observation: validated.observation,
38887
- executionResultId: eventKey,
38888
- ...warnings.length > 0 ? { warnings } : {}
38889
- };
38890
- } catch (error) {
38891
- return {
38892
- success: false,
38893
- code: "EXECUTION_RESULT_WRITE_FAILED",
38894
- error: errorMessage(error),
38895
- taskId: task.id,
38896
- agentId,
38897
- threadId,
38898
- ...warnings.length > 0 ? { warnings } : {}
38899
- };
40024
+ } finally {
40025
+ taskRunRegistry.unregister(task.id);
40026
+ if (taskRunLease) await taskRunLease.release();
38900
40027
  }
38901
40028
  };
38902
40029
  return {
@@ -38909,74 +40036,41 @@ function createTaskExecutionService(deps) {
38909
40036
  };
38910
40037
  }
38911
40038
 
38912
- // src/services/TaskRunRegistry.ts
38913
- var TaskRunRegistry = class {
38914
- constructor() {
38915
- this.live = /* @__PURE__ */ new Set();
38916
- }
38917
- /** Marks a task as currently held by a live run. */
38918
- register(taskId) {
38919
- this.live.add(taskId);
38920
- }
38921
- /** Removes a task once its run has ended. */
38922
- unregister(taskId) {
38923
- this.live.delete(taskId);
38924
- }
38925
- /** Reports whether a task is currently held by a live run. */
38926
- isLive(taskId) {
38927
- return this.live.has(taskId);
38928
- }
38929
- /** Returns a snapshot of the live task ids (diagnostics/tests). */
38930
- snapshot() {
38931
- return [...this.live];
38932
- }
38933
- /** Clears all live entries (tests / process teardown). */
38934
- clear() {
38935
- this.live.clear();
38936
- }
38937
- };
38938
- var taskRunRegistry = new TaskRunRegistry();
38939
- async function reconcileStaleTaskRuns(input) {
38940
- const registry4 = input.registry ?? taskRunRegistry;
38941
- const converged = [];
38942
- for (const task of input.tasks) {
38943
- if (task.status !== "in_progress" || registry4.isLive(task.id)) continue;
38944
- const result = await input.lifecycle.interruptTask({
38945
- tenantId: task.tenantId,
38946
- taskId: task.id,
38947
- actor: input.actor,
38948
- type: "missing_input",
38949
- summary: "Run is no longer executing this task (no_executor)"
38950
- });
38951
- if (result.success) converged.push(task.id);
40039
+ // src/services/leadership.ts
40040
+ import { randomUUID as randomUUID12 } from "crypto";
40041
+ async function runAsLeader(store, name, fn, options = {}) {
40042
+ const key5 = `leader:${name}`;
40043
+ const owner2 = options.owner ?? getReplicaId();
40044
+ const holder = `${owner2}#${randomUUID12()}`;
40045
+ const ttlMs = options.ttlMs ?? 6e4;
40046
+ if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
40047
+ const heartbeat = setInterval(() => {
40048
+ void store.renew(key5, holder, ttlMs).catch(() => void 0);
40049
+ }, Math.max(1e3, Math.floor(ttlMs / 3)));
40050
+ heartbeat.unref?.();
40051
+ try {
40052
+ const result = await fn();
40053
+ return { ran: true, result };
40054
+ } finally {
40055
+ clearInterval(heartbeat);
40056
+ if (!options.hold) await store.release(key5, holder).catch(() => void 0);
38952
40057
  }
38953
- return { converged };
38954
40058
  }
38955
- async function reconcileTenantTaskRuns(input) {
38956
- const tasks = await input.taskStore.list({ tenantId: input.tenantId, status: "in_progress" });
38957
- const candidates = input.include ? tasks.filter(input.include) : tasks;
38958
- return reconcileStaleTaskRuns({
38959
- tasks: candidates,
38960
- actor: input.actor,
38961
- lifecycle: input.lifecycle,
38962
- ...input.registry ? { registry: input.registry } : {}
38963
- });
40059
+
40060
+ // src/services/pluginCatalogSync.ts
40061
+ var PLUGIN_CATALOG_CHANGED_TOPIC = "plugin-catalog-changed";
40062
+ var outbox;
40063
+ var owner = "unknown";
40064
+ function configurePluginCatalogSync(next) {
40065
+ outbox = next?.outbox;
40066
+ owner = next?.owner ?? "unknown";
38964
40067
  }
38965
- async function reconcileAllTenantTaskRuns(input) {
38966
- const tenants = await input.tenantStore.listTenants();
38967
- const converged = [];
38968
- for (const tenant of tenants) {
38969
- const result = await reconcileTenantTaskRuns({
38970
- tenantId: tenant.id,
38971
- actor: input.actor,
38972
- taskStore: input.taskStore,
38973
- lifecycle: input.lifecycle,
38974
- ...input.registry ? { registry: input.registry } : {},
38975
- ...input.include ? { include: input.include } : {}
38976
- });
38977
- converged.push(...result.converged);
40068
+ async function publishPluginCatalogChanged(tenantId2) {
40069
+ if (!outbox || !tenantId2) return;
40070
+ try {
40071
+ await outbox.append({ topic: PLUGIN_CATALOG_CHANGED_TOPIC, payload: { tenantId: tenantId2 }, originReplicaId: owner });
40072
+ } catch {
38978
40073
  }
38979
- return { converged };
38980
40074
  }
38981
40075
 
38982
40076
  // src/index.ts
@@ -39121,9 +40215,9 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39121
40215
  if (!skillName) continue;
39122
40216
  if (await isPluginSkillManagedInSandbox(sandbox, skillName)) {
39123
40217
  const enabledDefinition = pluginSkills?.[skillName];
39124
- const owner = pluginSkillOwners?.[skillName];
39125
- const registered = owner && enabledDefinition ? PluginRegistry.getSkillForPlugin(owner, skillName) : void 0;
39126
- if (!registered || registered.definition !== enabledDefinition || !owner || !await isPluginSkillAuthorizedInSandbox(sandbox, skillName, owner) || await getPluginSkillManifestVersion(sandbox, skillName) !== enabledDefinition.version) {
40218
+ const owner2 = pluginSkillOwners?.[skillName];
40219
+ const registered = owner2 && enabledDefinition ? PluginRegistry.getSkillForPlugin(owner2, skillName) : void 0;
40220
+ if (!registered || registered.definition !== enabledDefinition || !owner2 || !await isPluginSkillAuthorizedInSandbox(sandbox, skillName, owner2) || await getPluginSkillManifestVersion(sandbox, skillName) !== enabledDefinition.version) {
39127
40221
  continue;
39128
40222
  }
39129
40223
  }
@@ -39149,8 +40243,8 @@ var createLoadSkillsTool = ({ skills, readAll, pluginSkills, pluginSkillOwners }
39149
40243
  const seenNames = new Set(filteredSkills.map((skill) => skill.name));
39150
40244
  try {
39151
40245
  for (const [name, definition] of Object.entries(pluginSkills ?? {})) {
39152
- const owner = pluginSkillOwners?.[name];
39153
- const registered = owner ? PluginRegistry.getSkillForPlugin(owner, name) : void 0;
40246
+ const owner2 = pluginSkillOwners?.[name];
40247
+ const registered = owner2 ? PluginRegistry.getSkillForPlugin(owner2, name) : void 0;
39154
40248
  if (!registered || registered.definition !== definition) continue;
39155
40249
  const { meta } = parseSkillFrontmatter(definition.content);
39156
40250
  if (!meta.name || !meta.description || seenNames.has(meta.name)) continue;
@@ -39187,24 +40281,24 @@ import z54 from "zod";
39187
40281
  import { tool as tool48 } from "langchain";
39188
40282
 
39189
40283
  // src/sandbox_plugin/pluginSkillResolution.ts
39190
- async function resolveTenantPluginSkill(tenantId2, owner, skillName) {
40284
+ async function resolveTenantPluginSkill(tenantId2, owner2, skillName) {
39191
40285
  try {
39192
40286
  const { plugins } = await getSandboxPluginCatalog().ensureLoaded(tenantId2);
39193
- const plugin = plugins.find((candidate) => candidate.meta.type === owner);
40287
+ const plugin = plugins.find((candidate) => candidate.meta.type === owner2);
39194
40288
  return plugin?.skills?.[skillName];
39195
40289
  } catch {
39196
40290
  return void 0;
39197
40291
  }
39198
40292
  }
39199
- async function resolveEnabledPluginSkill(owner, skillName, enabledDefinition, tenantId2) {
39200
- if (!owner || !enabledDefinition) return void 0;
39201
- const registered = PluginRegistry.getSkillForPlugin(owner, skillName);
39202
- if (registered && registered.pluginType === owner && registered.definition === enabledDefinition) {
39203
- return { pluginType: owner, definition: enabledDefinition };
40293
+ async function resolveEnabledPluginSkill(owner2, skillName, enabledDefinition, tenantId2) {
40294
+ if (!owner2 || !enabledDefinition) return void 0;
40295
+ const registered = PluginRegistry.getSkillForPlugin(owner2, skillName);
40296
+ if (registered && registered.pluginType === owner2 && registered.definition === enabledDefinition) {
40297
+ return { pluginType: owner2, definition: enabledDefinition };
39204
40298
  }
39205
40299
  if (!tenantId2) return void 0;
39206
- const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner, skillName);
39207
- return tenantDefinition ? { pluginType: owner, definition: tenantDefinition } : void 0;
40300
+ const tenantDefinition = await resolveTenantPluginSkill(tenantId2, owner2, skillName);
40301
+ return tenantDefinition ? { pluginType: owner2, definition: tenantDefinition } : void 0;
39208
40302
  }
39209
40303
 
39210
40304
  // src/tool_lattice/skill/load_skill_content.ts
@@ -39266,10 +40360,10 @@ var createLoadSkillContentTool = (pluginSkills, pluginSkillOwnersOrStaticSelecti
39266
40360
  return result2;
39267
40361
  }
39268
40362
  const enabledDefinition = pluginSkills?.[input.skill_name];
39269
- const owner = pluginSkillOwners?.[input.skill_name];
40363
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39270
40364
  const runConfig = _exe_config?.configurable?.runConfig || {};
39271
40365
  const pluginSkill = await resolveEnabledPluginSkill(
39272
- owner,
40366
+ owner2,
39273
40367
  input.skill_name,
39274
40368
  enabledDefinition,
39275
40369
  runConfig.tenantId
@@ -39376,9 +40470,9 @@ var createLoadSkillResourceTool = (pluginSkills, pluginSkillOwners) => {
39376
40470
  validateRelativePath(input.resource_path, "resource");
39377
40471
  const sandbox = await getSandboxFromExeConfig3(_exe_config);
39378
40472
  const definition = pluginSkills?.[input.skill_name];
39379
- const owner = pluginSkillOwners?.[input.skill_name];
40473
+ const owner2 = pluginSkillOwners?.[input.skill_name];
39380
40474
  const tenantId2 = _exe_config?.configurable?.runConfig?.tenantId;
39381
- const resolved = await resolveEnabledPluginSkill(owner, input.skill_name, definition, tenantId2);
40475
+ const resolved = await resolveEnabledPluginSkill(owner2, input.skill_name, definition, tenantId2);
39382
40476
  if (definition) {
39383
40477
  if (!resolved || !resolved.definition.resources?.[input.resource_path]) {
39384
40478
  return `Resource "${input.resource_path}" not found for skill "${input.skill_name}".`;
@@ -41178,61 +42272,28 @@ var widgetPlugin = {
41178
42272
  import { createMiddleware as createMiddleware23, tool as tool64 } from "langchain";
41179
42273
  import { z as z70 } from "zod";
41180
42274
  import { GraphInterrupt as GraphInterrupt3, interrupt as interrupt4 } from "@langchain/langgraph";
42275
+ import { resolveRunScope as resolveRunScope2 } from "@axiom-lattice/protocols";
41181
42276
 
41182
- // src/util/truncateForJson.ts
41183
- var TRUNCATION_MARKER = "...[truncated]";
41184
- function truncateString(value, cap) {
41185
- if (value.length <= cap) return value;
41186
- const budget = Math.max(0, cap - TRUNCATION_MARKER.length);
41187
- return value.slice(0, budget) + TRUNCATION_MARKER;
42277
+ // src/services/taskWorkerRunConfig.ts
42278
+ import { firstNonEmptyString, resolveRunScope } from "@axiom-lattice/protocols";
42279
+ function resolveWorkerRunScope(input) {
42280
+ const caller = resolveRunScope(input.runConfig);
42281
+ return {
42282
+ workspaceId: firstNonEmptyString(input.invocation.workspaceId) ?? caller.workspaceId,
42283
+ projectId: firstNonEmptyString(input.invocation.projectId) ?? caller.projectId
42284
+ };
41188
42285
  }
41189
- function truncateValue(value, depth, maxDepth, maxEntries, maxStringLength, seen) {
41190
- if (typeof value === "string") return truncateString(value, maxStringLength);
41191
- if (value === null || typeof value !== "object") return value;
41192
- if (depth >= maxDepth) return "[truncated:max-depth]";
41193
- if (seen.has(value)) return "[Circular]";
41194
- seen.add(value);
41195
- try {
41196
- if (value instanceof Date) return value.toISOString();
41197
- if (Array.isArray(value)) {
41198
- const limit2 = Math.min(value.length, maxEntries);
41199
- const output2 = [];
41200
- for (let index = 0; index < limit2; index += 1) {
41201
- output2.push(truncateValue(value[index], depth + 1, maxDepth, maxEntries, maxStringLength, seen));
41202
- }
41203
- if (value.length > limit2) output2.push("[truncated:entries]");
41204
- return output2;
41205
- }
41206
- const record = value;
41207
- const output = {};
41208
- const keys = Object.keys(record);
41209
- const limit = Math.min(keys.length, maxEntries);
41210
- for (let index = 0; index < limit; index += 1) {
41211
- const key5 = keys[index];
41212
- output[truncateString(key5, maxStringLength)] = truncateValue(
41213
- record[key5],
41214
- depth + 1,
41215
- maxDepth,
41216
- maxEntries,
41217
- maxStringLength,
41218
- seen
41219
- );
41220
- }
41221
- if (keys.length > limit) output["[truncated]"] = true;
41222
- return output;
41223
- } finally {
41224
- seen.delete(value);
42286
+ function applyWorkerRunScope(runConfig, scope) {
42287
+ const next = { ...runConfig };
42288
+ if (scope.workspaceId !== void 0) {
42289
+ next.workspaceId = scope.workspaceId;
42290
+ next["x-workspace-id"] = scope.workspaceId;
41225
42291
  }
41226
- }
41227
- function truncateForJson(value, options = {}) {
41228
- return truncateValue(
41229
- value,
41230
- 0,
41231
- options.maxDepth ?? JSON_SAFE_MAX_DEPTH,
41232
- options.maxEntries ?? JSON_SAFE_MAX_COLLECTION_ENTRIES,
41233
- options.maxStringLength ?? JSON_SAFE_MAX_STRING_LENGTH,
41234
- /* @__PURE__ */ new WeakSet()
41235
- );
42292
+ if (scope.projectId !== void 0) {
42293
+ next.projectId = scope.projectId;
42294
+ next["x-project-id"] = scope.projectId;
42295
+ }
42296
+ return next;
41236
42297
  }
41237
42298
 
41238
42299
  // src/middlewares/taskMiddleware.ts
@@ -41643,9 +42704,9 @@ async function renderAvailableExecutors(rc, tenantId2) {
41643
42704
  const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
41644
42705
  const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
41645
42706
  const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
41646
- const cached = executorCache.get(cacheKey);
42707
+ const cached2 = executorCache.get(cacheKey);
41647
42708
  const now = Date.now();
41648
- if (cached && now - cached.at < EXECUTOR_CACHE_TTL_MS) return cached.text;
42709
+ if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
41649
42710
  const lines = [];
41650
42711
  try {
41651
42712
  if (governed && projectId) {
@@ -42246,8 +43307,9 @@ function createTaskMiddleware(options = {}) {
42246
43307
  const trustedProject = authority.kind === "projectRoom" || authority.kind === "projectTask" ? authority.trusted : void 0;
42247
43308
  const trustedScope = trustedProject?.projectTask ?? trustedProject?.projectRoom;
42248
43309
  const tenantId2 = trustedScope?.tenantId ?? (rc.tenantId || "default");
42249
- const workspaceId = trustedScope?.workspaceId ?? rc.workspaceId;
42250
- const projectId = trustedScope?.projectId ?? rc.projectId;
43310
+ const runScope = resolveRunScope2(rc);
43311
+ const workspaceId = trustedScope?.workspaceId ?? runScope.workspaceId;
43312
+ const projectId = trustedScope?.projectId ?? runScope.projectId;
42251
43313
  const revalidateCaller = async () => {
42252
43314
  if (!trustedProject || trustedRosterExempt(trustedProject)) return void 0;
42253
43315
  try {
@@ -43476,15 +44538,16 @@ function createTaskMiddleware(options = {}) {
43476
44538
  assistant_id: invocation.agentId,
43477
44539
  thread_id: invocation.threadId,
43478
44540
  input: { message: invocation.message },
43479
- runConfig: {
43480
- ...rc,
43481
- assistant_id: invocation.agentId,
43482
- thread_id: invocation.threadId,
43483
- taskId: invocation.taskId,
43484
- tenantId: invocation.tenantId,
43485
- workspaceId: invocation.workspaceId,
43486
- projectId: invocation.projectId
43487
- }
44541
+ runConfig: applyWorkerRunScope(
44542
+ {
44543
+ ...rc,
44544
+ assistant_id: invocation.agentId,
44545
+ thread_id: invocation.threadId,
44546
+ taskId: invocation.taskId,
44547
+ tenantId: invocation.tenantId
44548
+ },
44549
+ resolveWorkerRunScope({ invocation, runConfig: rc })
44550
+ )
43488
44551
  });
43489
44552
  const finalState = workerResult?.finalState;
43490
44553
  return boundedWorkerObservation(finalState, void 0);
@@ -43499,7 +44562,7 @@ function createTaskMiddleware(options = {}) {
43499
44562
  agentId,
43500
44563
  actor,
43501
44564
  callerThreadId: rc.thread_id,
43502
- ...trustedCaller ? { callerMembershipId: trustedCaller.membershipId, callerAssistantId: trustedCaller.assistantId } : {}
44565
+ ...trustedCaller ? { callerMembershipId: trustedCaller.membershipId, callerAssistantId: trustedCaller.assistantId } : rc.assistant_id ? { callerAssistantId: rc.assistant_id } : {}
43503
44566
  }));
43504
44567
  };
43505
44568
  return createMiddleware23({
@@ -43559,7 +44622,14 @@ function createTaskMiddleware(options = {}) {
43559
44622
  task,
43560
44623
  actor: getRuntimeActor2(rc) ?? "system",
43561
44624
  ...threadId ? { threadId } : {},
43562
- outcome: { kind: "interrupted", summary: "Run ended without a task lifecycle transition" }
44625
+ // A run that ended without its own lifecycle transition is a mechanical
44626
+ // failure, NOT HITL. Human-in-the-loop pauses arrive as GraphInterrupt
44627
+ // and never reach afterAgent.
44628
+ outcome: {
44629
+ kind: "failed",
44630
+ lifecycleHandled: false,
44631
+ error: "Run ended without a task lifecycle transition"
44632
+ }
43563
44633
  });
43564
44634
  }
43565
44635
  taskRunRegistry.unregister(taskId);
@@ -45922,6 +46992,7 @@ var sandboxPluginsPlugin = {
45922
46992
  }
45923
46993
  case "reload": {
45924
46994
  const loaded = await catalog2.reload(tenantId2);
46995
+ void publishPluginCatalogChanged(tenantId2);
45925
46996
  return JSON.stringify({ reloaded: true, plugins: loaded.plugins.length, diagnostics: loaded.diagnostics });
45926
46997
  }
45927
46998
  case "create":
@@ -45941,12 +47012,14 @@ var sandboxPluginsPlugin = {
45941
47012
  if (!check.manifest) return JSON.stringify({ written: false, diagnostics: check.diagnostics });
45942
47013
  await catalog2.writePlugin(tenantId2, input.type, input.manifest, handlers);
45943
47014
  const loaded = await catalog2.reload(tenantId2);
47015
+ void publishPluginCatalogChanged(tenantId2);
45944
47016
  return JSON.stringify({ written: true, diagnostics: loaded.diagnostics, hash: loaded.hash });
45945
47017
  }
45946
47018
  case "delete": {
45947
47019
  if (!input.type) return "type is required";
45948
47020
  await catalog2.deletePlugin(tenantId2, input.type);
45949
47021
  await catalog2.reload(tenantId2);
47022
+ void publishPluginCatalogChanged(tenantId2);
45950
47023
  return JSON.stringify({ deleted: true });
45951
47024
  }
45952
47025
  case "validate": {
@@ -46058,6 +47131,26 @@ function registerBuiltinPlugins() {
46058
47131
  }
46059
47132
  }
46060
47133
 
47134
+ // src/sandbox_plugin/provisionOpenClient.ts
47135
+ import { readFileSync } from "fs";
47136
+ var SDK_ROOT = "/root/.agents/sdk";
47137
+ var CLIENT_DIR = `${SDK_ROOT}/open-mcp-client`;
47138
+ var MANIFEST_PATH2 = `${SDK_ROOT}/.open-client-manifest.json`;
47139
+ async function provisionSandboxOpenClient(sandbox, bundle) {
47140
+ await sandbox.file.createDirectory(SDK_ROOT);
47141
+ await sandbox.file.createDirectory(CLIENT_DIR);
47142
+ await sandbox.file.writeFile(`${CLIENT_DIR}/index.js`, bundle.indexJs);
47143
+ await sandbox.file.writeFile(`${CLIENT_DIR}/cli.js`, bundle.cliJs);
47144
+ await sandbox.file.writeFile(MANIFEST_PATH2, JSON.stringify({ version: bundle.version }));
47145
+ }
47146
+ function buildBundleFromDist(distDir, version) {
47147
+ return {
47148
+ indexJs: readFileSync(`${distDir}/index.js`, "utf8"),
47149
+ cliJs: readFileSync(`${distDir}/cli.js`, "utf8"),
47150
+ version
47151
+ };
47152
+ }
47153
+
46061
47154
  // src/personal_assistant/PersonalAssistantConfig.ts
46062
47155
  function deepClone(obj) {
46063
47156
  return JSON.parse(JSON.stringify(obj));
@@ -46519,10 +47612,12 @@ export {
46519
47612
  InMemoryChunkBuffer,
46520
47613
  InMemoryCollectionStore,
46521
47614
  InMemoryDatabaseConfigStore,
47615
+ InMemoryLeaseStore,
46522
47616
  InMemoryMailboxStore,
46523
47617
  InMemoryMenuStore,
46524
47618
  InMemoryModelProviderStore,
46525
47619
  InMemoryOpenAuditStore,
47620
+ InMemoryOutboxStore,
46526
47621
  InMemoryProjectBotMembershipStore,
46527
47622
  InMemoryProjectMembershipStore,
46528
47623
  InMemoryProjectRoomCoordinator,
@@ -46542,6 +47637,7 @@ export {
46542
47637
  LatticeEval,
46543
47638
  LatticeEvalProject,
46544
47639
  LatticeEvalSuite,
47640
+ LeaseAcquisitionTimeoutError,
46545
47641
  LocalSandboxInstance,
46546
47642
  LocalSandboxProvider,
46547
47643
  LoggerLatticeManager,
@@ -46564,7 +47660,10 @@ export {
46564
47660
  MicrosandboxServiceClient,
46565
47661
  ModelLatticeManager,
46566
47662
  MysqlDatabase,
47663
+ OutboxPoller,
46567
47664
  PACK_DIRECTORY_TIMEOUT_SECONDS,
47665
+ PLATFORM_SECRET_ENV_VARS,
47666
+ PLUGIN_CATALOG_CHANGED_TOPIC,
46568
47667
  PersonalAssistantConfig,
46569
47668
  PinoLoggerClient,
46570
47669
  PluginRegistry,
@@ -46585,6 +47684,8 @@ export {
46585
47684
  RepeatedToolCallError,
46586
47685
  RoomAgentMessageService,
46587
47686
  SAFE_PATH_COMPONENT,
47687
+ SANDBOX_OPEN_GATEWAY_URL_ENV,
47688
+ SANDBOX_OPEN_TOKEN_ENV,
46588
47689
  STTModelLattice,
46589
47690
  STTModelLatticeManager,
46590
47691
  SandboxFilesystem,
@@ -46625,11 +47726,13 @@ export {
46625
47726
  appendReconciliationWarning,
46626
47727
  appendUiFence,
46627
47728
  assertSafeUpstreamUrl,
47729
+ buildBundleFromDist,
46628
47730
  buildGrepResultsDict,
46629
47731
  buildInput,
46630
47732
  buildNamedVolumeName,
46631
47733
  buildRemotePackDirectoryCommand,
46632
47734
  buildSandboxMetadataEnv,
47735
+ buildSandboxOpenEnv,
46633
47736
  buildSkillFile,
46634
47737
  buildStateAnnotation,
46635
47738
  buildTableName,
@@ -46638,11 +47741,14 @@ export {
46638
47741
  checkEmptyContent,
46639
47742
  clearEncryptionKeyCache,
46640
47743
  clearEvalRunService,
47744
+ clearSandboxOpenCredentialProviderForTests,
46641
47745
  collectionLatticeManager,
46642
47746
  compileInternal,
46643
47747
  compileSandboxPlugin,
46644
47748
  compileWorkflow,
46645
47749
  computeSandboxName,
47750
+ configureCoordinationLeaseStore,
47751
+ configurePluginCatalogSync,
46646
47752
  configureStores,
46647
47753
  connectAllChannels,
46648
47754
  convertToConnection,
@@ -46674,6 +47780,7 @@ export {
46674
47780
  createSandboxResolver,
46675
47781
  createSchedulerMiddleware,
46676
47782
  createSharePayload,
47783
+ createShellExecTool,
46677
47784
  createTaskExecutionService,
46678
47785
  createTaskLifecycleService,
46679
47786
  createTaskMiddleware,
@@ -46708,6 +47815,7 @@ export {
46708
47815
  generateDirectHumanProjectId,
46709
47816
  generateToken,
46710
47817
  getAgentClient,
47818
+ getAgentClientAsync,
46711
47819
  getAgentConfig,
46712
47820
  getAllAgentConfigs,
46713
47821
  getAllBuiltInSkillMetas,
@@ -46719,6 +47827,7 @@ export {
46719
47827
  getCheckpointSaver,
46720
47828
  getChunkBuffer,
46721
47829
  getCollectionEntryCount,
47830
+ getCoordinationLeaseStore,
46722
47831
  getEmbeddingsClient,
46723
47832
  getEmbeddingsLattice,
46724
47833
  getEncryptionKey,
@@ -46730,15 +47839,18 @@ export {
46730
47839
  getOrCreateCollectionVectorStore,
46731
47840
  getPluginSkillManifestVersion,
46732
47841
  getQueueLattice,
47842
+ getReplicaId,
46733
47843
  getRuntimeCapabilityConfig,
46734
47844
  getRuntimeSkillSelection,
46735
47845
  getSTTClient,
46736
47846
  getSTTClientWithTenant,
46737
47847
  getSTTModelLattice,
46738
47848
  getSandBoxManager,
47849
+ getSandboxOpenCredential,
46739
47850
  getSandboxPluginCatalog,
46740
47851
  getScheduleLattice,
46741
47852
  getStoreLattice,
47853
+ getThreadRunOwner,
46742
47854
  getToolClient,
46743
47855
  getToolClientWithTenant,
46744
47856
  getToolDefinition,
@@ -46792,6 +47904,8 @@ export {
46792
47904
  projectMutationMutex,
46793
47905
  projectRoomPlugin,
46794
47906
  projectTaskAdmissionNotifier,
47907
+ provisionSandboxOpenClient,
47908
+ publishPluginCatalogChanged,
46795
47909
  queueLatticeManager,
46796
47910
  readLoopBreakerConfig,
46797
47911
  reconcileAllTenantTaskRuns,
@@ -46810,6 +47924,7 @@ export {
46810
47924
  registerModelLatticeForTenant,
46811
47925
  registerQueueLattice,
46812
47926
  registerSTTModelLattice,
47927
+ registerSandboxOpenCredentialProvider,
46813
47928
  registerSandboxProviderType,
46814
47929
  registerScheduleLattice,
46815
47930
  registerStoreLattice,
@@ -46820,6 +47935,7 @@ export {
46820
47935
  registerWorkflowRun,
46821
47936
  removeCollectionVectorStore,
46822
47937
  renderTemplate,
47938
+ resetReplicaIdForTests,
46823
47939
  resolveEnabledPluginSkill,
46824
47940
  resolveJudgeModelKey,
46825
47941
  resolvePath,
@@ -46827,6 +47943,7 @@ export {
46827
47943
  resolveSandboxPluginConnections,
46828
47944
  resolveTenantPluginSkill,
46829
47945
  roomCoordinatorConfig,
47946
+ runAsLeader,
46830
47947
  runRemotePackDirectory,
46831
47948
  safeTaskResponseId,
46832
47949
  sandboxLatticeManager,
@@ -46845,11 +47962,15 @@ export {
46845
47962
  storeLatticeManager,
46846
47963
  sttModelLatticeManager,
46847
47964
  taskLifecycleNotifier,
47965
+ taskRunLeaseKey,
46848
47966
  taskRunRegistry,
47967
+ threadRunLeaseKey,
46849
47968
  toJsonSchema,
46850
47969
  toSafeStateExpr,
46851
47970
  toolLatticeManager,
46852
47971
  truncateIfTooLong,
47972
+ tryAcquireTaskRun,
47973
+ tryAcquireThreadRun,
46853
47974
  unregisterTeammateAgent,
46854
47975
  unregisterWorkflowRun,
46855
47976
  updateFileData,
@@ -46865,6 +47986,7 @@ export {
46865
47986
  validateToolInput,
46866
47987
  vectorStoreLatticeManager,
46867
47988
  vectorStoreProviderManager,
47989
+ withLease,
46868
47990
  withTimeout,
46869
47991
  wrapMcpToolWithUi
46870
47992
  };