@axiom-lattice/core 5.0.0 → 6.1.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.js CHANGED
@@ -1662,6 +1662,7 @@ __export(index_exports, {
1662
1662
  InMemoryDatabaseConfigStore: () => InMemoryDatabaseConfigStore,
1663
1663
  InMemoryMailboxStore: () => InMemoryMailboxStore,
1664
1664
  InMemoryMenuStore: () => InMemoryMenuStore,
1665
+ InMemoryModelProviderStore: () => InMemoryModelProviderStore,
1665
1666
  InMemoryOpenAuditStore: () => InMemoryOpenAuditStore,
1666
1667
  InMemoryProjectBotMembershipStore: () => InMemoryProjectBotMembershipStore,
1667
1668
  InMemoryProjectMembershipStore: () => InMemoryProjectMembershipStore,
@@ -1755,6 +1756,7 @@ __export(index_exports, {
1755
1756
  ThreadStatus: () => ThreadStatus2,
1756
1757
  TokenCache: () => TokenCache,
1757
1758
  ToolLatticeManager: () => ToolLatticeManager,
1759
+ UnsafeUpstreamUrlError: () => UnsafeUpstreamUrlError,
1758
1760
  VectorStoreLatticeManager: () => VectorStoreLatticeManager,
1759
1761
  VectorStoreProviderManager: () => VectorStoreProviderManager,
1760
1762
  VolumeFilesystem: () => VolumeFilesystem,
@@ -1763,6 +1765,7 @@ __export(index_exports, {
1763
1765
  agentLatticeManager: () => agentLatticeManager,
1764
1766
  appendReconciliationWarning: () => appendReconciliationWarning,
1765
1767
  appendUiFence: () => appendUiFence,
1768
+ assertSafeUpstreamUrl: () => assertSafeUpstreamUrl,
1766
1769
  buildGrepResultsDict: () => buildGrepResultsDict,
1767
1770
  buildInput: () => buildInput,
1768
1771
  buildNamedVolumeName: () => buildNamedVolumeName,
@@ -1894,6 +1897,7 @@ __export(index_exports, {
1894
1897
  invokeWithRetry: () => invokeWithRetry,
1895
1898
  isBuiltInSkill: () => isBuiltInSkill,
1896
1899
  isDirectRoom: () => isDirectRoom,
1900
+ isMcpConnectionError: () => isMcpConnectionError,
1897
1901
  isPluginSkillAuthorizedInSandbox: () => isPluginSkillAuthorizedInSandbox,
1898
1902
  isPluginSkillManagedInSandbox: () => isPluginSkillManagedInSandbox,
1899
1903
  isUsingDefaultKey: () => isUsingDefaultKey,
@@ -1916,7 +1920,6 @@ __export(index_exports, {
1916
1920
  parallelLimit: () => parallelLimit,
1917
1921
  parseCronExpression: () => parseCronExpression,
1918
1922
  parseDirectRoomType: () => parseDirectRoomType,
1919
- parseExternalDependency: () => parseExternalDependency,
1920
1923
  parseJudgeVerdict: () => parseJudgeVerdict,
1921
1924
  parsePackSize: () => parsePackSize,
1922
1925
  parseQueuedExecutionMode: () => import_protocols2.parseQueuedExecutionMode,
@@ -1945,6 +1948,7 @@ __export(index_exports, {
1945
1948
  registerExistingToolWithTenant: () => registerExistingToolWithTenant,
1946
1949
  registerLoggerLattice: () => registerLoggerLattice,
1947
1950
  registerModelLattice: () => registerModelLattice,
1951
+ registerModelLatticeForTenant: () => registerModelLatticeForTenant,
1948
1952
  registerQueueLattice: () => registerQueueLattice,
1949
1953
  registerSTTModelLattice: () => registerSTTModelLattice,
1950
1954
  registerSandboxProviderType: () => registerSandboxProviderType,
@@ -2026,6 +2030,17 @@ var ModelLattice = class extends import_chat_models.BaseChatModel {
2026
2030
  this.config = config;
2027
2031
  this.llm = this.initChatModel(config);
2028
2032
  }
2033
+ /**
2034
+ * 返回构造时使用的配置
2035
+ *
2036
+ * 供调用方(如 models 控制器)读取 apiKey 是否存在、以及展示元数据,
2037
+ * 避免反射读取私有字段。
2038
+ *
2039
+ * @returns 构造时传入的 LLMConfig
2040
+ */
2041
+ getConfig() {
2042
+ return this.config;
2043
+ }
2029
2044
  /**
2030
2045
  * 返回模型类型
2031
2046
  */
@@ -2165,6 +2180,9 @@ var ModelLattice = class extends import_chat_models.BaseChatModel {
2165
2180
  maxRetries: config.maxRetries || 2,
2166
2181
  streaming: config.streaming,
2167
2182
  reasoning: { effort: "none" },
2183
+ // Responses API and Chat Completions are different paths with different
2184
+ // request fields; an endpoint that supports only one rejects the other.
2185
+ ...config.useResponsesApi ? { useResponsesApi: true } : {},
2168
2186
  apiKey: config.apiKey || process.env[config.apiKeyEnvName || "OPENAI_API_KEY"],
2169
2187
  configuration: {
2170
2188
  baseURL: config.baseURL
@@ -2255,9 +2273,74 @@ var ModelLatticeManager = class _ModelLatticeManager extends BaseLatticeManager
2255
2273
  getLatticeKeys() {
2256
2274
  return this.keys();
2257
2275
  }
2276
+ // ========== 租户维度 API ==========
2277
+ // 键由基类拼装为 models:{tenantId}:{key}。注意 "default" 租户的键与旧版
2278
+ // key-only API 完全一致(models:default:{key}),因此对该租户是逐字节兼容的。
2279
+ /**
2280
+ * 以租户维度注册模型Lattice
2281
+ *
2282
+ * 同名 key 采用「后写覆盖」语义:先移除再注册,避免基类重复注册时抛错。
2283
+ *
2284
+ * @param tenantId 租户ID
2285
+ * @param key Lattice键名
2286
+ * @param config 模型配置
2287
+ */
2288
+ registerLatticeWithTenant(tenantId2, key5, config) {
2289
+ const client = new ModelLattice(config);
2290
+ const modelLattice = {
2291
+ key: key5,
2292
+ client
2293
+ };
2294
+ if (this.hasWithTenant(tenantId2, key5)) {
2295
+ this.removeWithTenant(tenantId2, key5);
2296
+ }
2297
+ this.registerWithTenant(tenantId2, key5, modelLattice);
2298
+ }
2299
+ /**
2300
+ * 获取指定租户的模型Lattice
2301
+ * @param tenantId 租户ID
2302
+ * @param key Lattice键名
2303
+ */
2304
+ getModelLatticeWithTenant(tenantId2, key5) {
2305
+ return this.getWithTenant(tenantId2, key5);
2306
+ }
2307
+ /**
2308
+ * 检查指定租户的模型Lattice是否存在
2309
+ * @param tenantId 租户ID
2310
+ * @param key Lattice键名
2311
+ */
2312
+ hasLatticeWithTenant(tenantId2, key5) {
2313
+ return this.hasWithTenant(tenantId2, key5);
2314
+ }
2315
+ /**
2316
+ * 获取指定租户的所有模型Lattice
2317
+ * @param tenantId 租户ID
2318
+ */
2319
+ getAllLatticesByTenant(tenantId2) {
2320
+ return this.getAllByTenant(tenantId2);
2321
+ }
2322
+ /**
2323
+ * 获取指定租户的所有模型键名
2324
+ *
2325
+ * 只返回键名,不构造 Lattice 对象数组,适合仅需判断存在性的调用方。
2326
+ *
2327
+ * @param tenantId 租户ID
2328
+ */
2329
+ getLatticeKeysByTenant(tenantId2) {
2330
+ return this.keysByTenant(tenantId2);
2331
+ }
2332
+ /**
2333
+ * 移除指定租户的模型Lattice
2334
+ * @param tenantId 租户ID
2335
+ * @param key Lattice键名
2336
+ */
2337
+ removeLatticeWithTenant(tenantId2, key5) {
2338
+ return this.removeWithTenant(tenantId2, key5);
2339
+ }
2258
2340
  };
2259
2341
  var modelLatticeManager = ModelLatticeManager.getInstance();
2260
2342
  var registerModelLattice = (key5, config) => modelLatticeManager.registerLattice(key5, config);
2343
+ var registerModelLatticeForTenant = (tenantId2, key5, config) => modelLatticeManager.registerLatticeWithTenant(tenantId2, key5, config);
2261
2344
  var getModelLattice = (key5) => modelLatticeManager.getModelLattice(key5);
2262
2345
 
2263
2346
  // src/tool_lattice/get_current_date_time/index.ts
@@ -3993,6 +4076,240 @@ var InMemoryMcpServerConfigStore = class {
3993
4076
  }
3994
4077
  };
3995
4078
 
4079
+ // src/store_lattice/InMemoryModelProviderStore.ts
4080
+ var InMemoryModelProviderStore = class {
4081
+ constructor() {
4082
+ this.providers = /* @__PURE__ */ new Map();
4083
+ this.models = /* @__PURE__ */ new Map();
4084
+ }
4085
+ /**
4086
+ * Get composite key for providers
4087
+ */
4088
+ getProviderKey(tenantId2, id) {
4089
+ return `${tenantId2}:${id}`;
4090
+ }
4091
+ /**
4092
+ * Get composite key for snapshot models
4093
+ */
4094
+ getModelKey(tenantId2, providerId, modelId) {
4095
+ return `${tenantId2}:${providerId}:${modelId}`;
4096
+ }
4097
+ /**
4098
+ * Get all provider configurations for a tenant
4099
+ */
4100
+ async listProviders(tenantId2) {
4101
+ return Array.from(this.providers.values()).filter(
4102
+ (provider) => provider.tenantId === tenantId2
4103
+ );
4104
+ }
4105
+ /**
4106
+ * Get a provider configuration by ID
4107
+ */
4108
+ async getProviderById(tenantId2, id) {
4109
+ return this.providers.get(this.getProviderKey(tenantId2, id)) || null;
4110
+ }
4111
+ /**
4112
+ * Get a provider configuration by business name
4113
+ */
4114
+ async getProviderByName(tenantId2, name) {
4115
+ const providers = await this.listProviders(tenantId2);
4116
+ return providers.find((provider) => provider.name === name) || null;
4117
+ }
4118
+ /**
4119
+ * Create a new provider configuration
4120
+ */
4121
+ async createProvider(tenantId2, id, data) {
4122
+ const now = /* @__PURE__ */ new Date();
4123
+ const entry = {
4124
+ id,
4125
+ tenantId: tenantId2,
4126
+ name: data.name,
4127
+ displayName: data.displayName,
4128
+ protocol: data.protocol,
4129
+ apiStyle: data.apiStyle ?? "chat-completions",
4130
+ // Phase 1 pins the LLM provider: the UI does not offer a choice, and some
4131
+ // initChatModel branches hardcode their own baseURL and would silently
4132
+ // ignore the tenant's (see ADR-138 decision 7b).
4133
+ llmProvider: "openai",
4134
+ baseURL: data.baseURL,
4135
+ apiKey: data.apiKey || void 0,
4136
+ apiKeyHint: data.apiKey ? data.apiKey.slice(-4) : void 0,
4137
+ enabled: data.enabled ?? true,
4138
+ status: "unknown",
4139
+ createdAt: now,
4140
+ updatedAt: now
4141
+ };
4142
+ this.providers.set(this.getProviderKey(tenantId2, id), entry);
4143
+ return entry;
4144
+ }
4145
+ /**
4146
+ * Update an existing provider configuration.
4147
+ * An omitted or empty `apiKey` leaves the stored key untouched.
4148
+ */
4149
+ async updateProvider(tenantId2, id, updates) {
4150
+ const key5 = this.getProviderKey(tenantId2, id);
4151
+ const existing = this.providers.get(key5);
4152
+ if (!existing) {
4153
+ return null;
4154
+ }
4155
+ const hasNewApiKey = Boolean(updates.apiKey);
4156
+ const clearsApiKey = updates.clearApiKey === true;
4157
+ const updated = {
4158
+ ...existing,
4159
+ name: updates.name ?? existing.name,
4160
+ displayName: updates.displayName !== void 0 ? updates.displayName : existing.displayName,
4161
+ protocol: updates.protocol ?? existing.protocol,
4162
+ apiStyle: updates.apiStyle ?? existing.apiStyle,
4163
+ baseURL: updates.baseURL ?? existing.baseURL,
4164
+ apiKey: clearsApiKey ? void 0 : hasNewApiKey ? updates.apiKey : existing.apiKey,
4165
+ apiKeyHint: clearsApiKey ? void 0 : hasNewApiKey ? updates.apiKey?.slice(-4) : existing.apiKeyHint,
4166
+ enabled: updates.enabled ?? existing.enabled,
4167
+ updatedAt: /* @__PURE__ */ new Date()
4168
+ };
4169
+ this.providers.set(key5, updated);
4170
+ return updated;
4171
+ }
4172
+ /**
4173
+ * Delete a provider configuration and its model snapshot
4174
+ */
4175
+ async deleteProvider(tenantId2, id) {
4176
+ await this.deleteModelsByProvider(tenantId2, id);
4177
+ return this.providers.delete(this.getProviderKey(tenantId2, id));
4178
+ }
4179
+ /**
4180
+ * Record the outcome of a discovery attempt
4181
+ */
4182
+ async markProviderStatus(tenantId2, id, status, lastError, discoveredAt) {
4183
+ const key5 = this.getProviderKey(tenantId2, id);
4184
+ const existing = this.providers.get(key5);
4185
+ if (!existing) {
4186
+ return;
4187
+ }
4188
+ this.providers.set(key5, {
4189
+ ...existing,
4190
+ status,
4191
+ lastError: status === "ok" ? void 0 : lastError,
4192
+ lastDiscoveredAt: discoveredAt ?? existing.lastDiscoveredAt,
4193
+ updatedAt: /* @__PURE__ */ new Date()
4194
+ });
4195
+ }
4196
+ /**
4197
+ * List snapshot models for a tenant
4198
+ */
4199
+ async listModels(tenantId2, providerId) {
4200
+ return Array.from(this.models.values()).filter(
4201
+ (model) => model.tenantId === tenantId2 && (providerId === void 0 || model.providerId === providerId)
4202
+ );
4203
+ }
4204
+ /**
4205
+ * Get a single snapshot model.
4206
+ * Keyed by provider as well as model id, since two providers in the same
4207
+ * tenant may expose the same upstream model id.
4208
+ */
4209
+ async getModel(tenantId2, providerId, modelId) {
4210
+ return this.models.get(this.getModelKey(tenantId2, providerId, modelId)) || null;
4211
+ }
4212
+ /**
4213
+ * Replace a provider's model snapshot with the result of one discovery.
4214
+ *
4215
+ * Model ids are scoped per provider, so the same upstream id may appear
4216
+ * under several providers in one tenant — each is an independent model.
4217
+ *
4218
+ * Models the upstream no longer offers are removed; models that survive keep
4219
+ * the tenant's enable/disable choice rather than being reset.
4220
+ */
4221
+ async replaceProviderModels(tenantId2, providerId, models) {
4222
+ const previous = await this.listModels(tenantId2, providerId);
4223
+ const previousById = new Map(
4224
+ previous.map((model) => [model.modelId, model])
4225
+ );
4226
+ const incomingIds = new Set(models.map((model) => model.id));
4227
+ for (const model of previous) {
4228
+ if (!incomingIds.has(model.modelId)) {
4229
+ this.models.delete(
4230
+ this.getModelKey(tenantId2, providerId, model.modelId)
4231
+ );
4232
+ }
4233
+ }
4234
+ const now = /* @__PURE__ */ new Date();
4235
+ const upserted = [];
4236
+ for (const model of models) {
4237
+ const existing = previousById.get(model.id);
4238
+ const entry = {
4239
+ id: `${providerId}:${model.id}`,
4240
+ tenantId: tenantId2,
4241
+ providerId,
4242
+ modelId: model.id,
4243
+ displayName: model.displayName,
4244
+ ownedBy: model.ownedBy,
4245
+ upstreamCreatedAt: model.createdAt ? new Date(model.createdAt) : void 0,
4246
+ // An explicit `enabled` from the caller wins; discovery omits it and
4247
+ // the tenant's earlier choice is preserved.
4248
+ enabled: model.enabled ?? existing?.enabled ?? true,
4249
+ // Unlike `enabled`, this is not preserved: a model present in the list
4250
+ // the caller just read is, by definition, no longer stale. Only an
4251
+ // explicit flag marks one.
4252
+ stale: model.stale ?? false,
4253
+ discoveredAt: now
4254
+ };
4255
+ this.models.set(this.getModelKey(tenantId2, providerId, model.id), entry);
4256
+ upserted.push(model.id);
4257
+ }
4258
+ return upserted;
4259
+ }
4260
+ /**
4261
+ * Enable or disable a single snapshot model
4262
+ */
4263
+ async setModelEnabled(tenantId2, providerId, modelId, enabled) {
4264
+ const key5 = this.getModelKey(tenantId2, providerId, modelId);
4265
+ const existing = this.models.get(key5);
4266
+ if (!existing) {
4267
+ return null;
4268
+ }
4269
+ const updated = {
4270
+ ...existing,
4271
+ enabled
4272
+ };
4273
+ this.models.set(key5, updated);
4274
+ return updated;
4275
+ }
4276
+ /**
4277
+ * Delete every snapshot model belonging to a provider
4278
+ */
4279
+ async deleteModelsByProvider(tenantId2, providerId) {
4280
+ let deleted = 0;
4281
+ for (const [key5, model] of Array.from(this.models.entries())) {
4282
+ if (model.tenantId === tenantId2 && model.providerId === providerId) {
4283
+ this.models.delete(key5);
4284
+ deleted += 1;
4285
+ }
4286
+ }
4287
+ return deleted;
4288
+ }
4289
+ /**
4290
+ * Clear all providers and models (useful for testing)
4291
+ */
4292
+ clear() {
4293
+ this.providers.clear();
4294
+ this.models.clear();
4295
+ }
4296
+ /**
4297
+ * Clear providers and models for a specific tenant
4298
+ */
4299
+ clearByTenant(tenantId2) {
4300
+ for (const [key5, provider] of Array.from(this.providers.entries())) {
4301
+ if (provider.tenantId === tenantId2) {
4302
+ this.providers.delete(key5);
4303
+ }
4304
+ }
4305
+ for (const [key5, model] of Array.from(this.models.entries())) {
4306
+ if (model.tenantId === tenantId2) {
4307
+ this.models.delete(key5);
4308
+ }
4309
+ }
4310
+ }
4311
+ };
4312
+
3996
4313
  // src/store_lattice/InMemoryAgentWebAppStore.ts
3997
4314
  var import_crypto = require("crypto");
3998
4315
  function cloneAppearance(appearance) {
@@ -6473,6 +6790,7 @@ var defaultProjectStore = new InMemoryProjectStore(defaultCapabilityBundleCoordi
6473
6790
  var defaultDatabaseConfigStore = new InMemoryDatabaseConfigStore();
6474
6791
  var defaultMetricsServerConfigStore = new InMemoryMetricsServerConfigStore();
6475
6792
  var defaultMcpServerConfigStore = new InMemoryMcpServerConfigStore();
6793
+ var defaultModelProviderStore = new InMemoryModelProviderStore();
6476
6794
  var defaultAgentWebAppStore = new InMemoryAgentWebAppStore();
6477
6795
  var defaultCapabilityBundleStore = new InMemoryCapabilityBundleStore(defaultProjectStore, defaultCapabilityBundleCoordinator);
6478
6796
  var defaultProjectRoomCoordinator = new InMemoryProjectRoomCoordinator();
@@ -6494,6 +6812,7 @@ storeLatticeManager.registerLattice("default", "project", defaultProjectStore);
6494
6812
  storeLatticeManager.registerLattice("default", "database", defaultDatabaseConfigStore);
6495
6813
  storeLatticeManager.registerLattice("default", "metrics", defaultMetricsServerConfigStore);
6496
6814
  storeLatticeManager.registerLattice("default", "mcp", defaultMcpServerConfigStore);
6815
+ storeLatticeManager.registerLattice("default", "modelProvider", defaultModelProviderStore);
6497
6816
  storeLatticeManager.registerLattice("default", "agentWebApp", defaultAgentWebAppStore);
6498
6817
  storeLatticeManager.registerLattice("default", "capabilityBundle", defaultCapabilityBundleStore);
6499
6818
  storeLatticeManager.registerLattice("default", "projectRoom", defaultProjectRoomStore);
@@ -30804,7 +31123,7 @@ var agentArchitectConfig = {
30804
31123
  "delete_agent",
30805
31124
  "manage_binding"
30806
31125
  ],
30807
- subAgents: ["document-parser-benchmark"],
31126
+ subAgents: ["document-parser", "document-parser-benchmark"],
30808
31127
  middleware: [
30809
31128
  {
30810
31129
  id: "filesystem",
@@ -32513,6 +32832,7 @@ async function removeCollectionVectorStore(name, tenantId2) {
32513
32832
 
32514
32833
  // src/mcp_lattice/McpLatticeManager.ts
32515
32834
  var import_mcp_adapters = require("@langchain/mcp-adapters");
32835
+ var import_tools3 = require("@langchain/core/tools");
32516
32836
  init_BaseLatticeManager();
32517
32837
 
32518
32838
  // src/mcp_lattice/reconnectPolicy.ts
@@ -32522,6 +32842,13 @@ var DEFAULT_DELAY_MS = 1e3;
32522
32842
  var MAX_BACKOFF_MS = 3e4;
32523
32843
  var HEALTH_CHECK_INTERVAL_MS = 3e4;
32524
32844
  var HEALTH_CHECK_TIMEOUT_MS = 5e3;
32845
+ function isMcpConnectionError(error) {
32846
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
32847
+ if (!message) return false;
32848
+ return /not connected|connection closed|session not found|no session|maximum reconnection/i.test(
32849
+ message
32850
+ );
32851
+ }
32525
32852
  function nextBackoffDelay(attempt, base = DEFAULT_DELAY_MS, max = MAX_BACKOFF_MS) {
32526
32853
  if (attempt <= 0) return base;
32527
32854
  return Math.min(base * Math.pow(2, attempt - 1), max);
@@ -32546,12 +32873,21 @@ async function withTimeout2(operation, ms, onTimeout) {
32546
32873
 
32547
32874
  // src/mcp_lattice/McpLatticeManager.ts
32548
32875
  var realClientFactory = ({ mcpServers }) => new import_mcp_adapters.MultiServerMCPClient({ mcpServers });
32876
+ function extractRawCall(tool68) {
32877
+ const rawCall = tool68._call;
32878
+ if (typeof rawCall === "function") return rawCall.bind(tool68);
32879
+ return async (...args) => {
32880
+ const invoke = tool68.invoke.bind(tool68);
32881
+ return invoke(args[0], args[2]);
32882
+ };
32883
+ }
32549
32884
  var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
32550
32885
  constructor() {
32551
32886
  super(...arguments);
32552
32887
  this.runtimes = /* @__PURE__ */ new Map();
32553
32888
  this.clientFactory = realClientFactory;
32554
32889
  this.healthCheckInFlight = false;
32890
+ this.reconnectPromises = /* @__PURE__ */ new Map();
32555
32891
  }
32556
32892
  static getInstance() {
32557
32893
  if (!_McpLatticeManager._instance) {
@@ -32809,6 +33145,64 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
32809
33145
  rt.attempt = 0;
32810
33146
  await this.reconnectRuntime(rt);
32811
33147
  }
33148
+ /**
33149
+ * Single-flight a forced reconnect per `(tenantId, serverKey)` so concurrent
33150
+ * tool calls that all hit the same dead session reconnect once, not once each.
33151
+ */
33152
+ reconnectShared(rt) {
33153
+ const key5 = this.compositeKey(rt.id.tenantId, rt.id.serverKey);
33154
+ const existing = this.reconnectPromises.get(key5);
33155
+ if (existing) return existing;
33156
+ const work = this.reconnectServer(rt.id.tenantId, rt.id.serverKey).finally(() => {
33157
+ if (this.reconnectPromises.get(key5) === work) this.reconnectPromises.delete(key5);
33158
+ });
33159
+ this.reconnectPromises.set(key5, work);
33160
+ return work;
33161
+ }
33162
+ /**
33163
+ * Wrap a raw MCP tool so a call landing on a torn-down session
33164
+ * (`Not connected` / `Connection closed` / session expiry) transparently
33165
+ * forces a reconnect and retries once against the fresh client, instead of
33166
+ * failing the surrounding run. Non-connection errors propagate untouched.
33167
+ */
33168
+ wrapToolForResilience(rt, raw) {
33169
+ const call = extractRawCall(raw);
33170
+ return new import_tools3.DynamicStructuredTool({
33171
+ name: raw.name,
33172
+ description: raw.description,
33173
+ schema: raw.schema,
33174
+ responseFormat: raw.responseFormat,
33175
+ defaultConfig: raw.defaultConfig,
33176
+ metadata: raw.metadata,
33177
+ returnDirect: raw.returnDirect,
33178
+ func: async (...args) => this.invokeResilient(rt, raw.name, call, args)
33179
+ });
33180
+ }
33181
+ async invokeResilient(rt, toolName2, call, args) {
33182
+ try {
33183
+ await this.ensureConnected(rt.id.tenantId, rt.id.serverKey);
33184
+ return await call(...args);
33185
+ } catch (error) {
33186
+ if (!isMcpConnectionError(error) || !this.isActive(rt)) throw error;
33187
+ console.warn(
33188
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: tool "${toolName2}" hit a dead session; reconnecting and retrying once`,
33189
+ error instanceof Error ? error.message : error
33190
+ );
33191
+ try {
33192
+ await this.reconnectShared(rt);
33193
+ } catch (reconnectError) {
33194
+ console.warn(
33195
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: reconnect for tool "${toolName2}" failed`,
33196
+ reconnectError instanceof Error ? reconnectError.message : reconnectError
33197
+ );
33198
+ throw error;
33199
+ }
33200
+ const fresh = await this.getRawTools(rt.id.tenantId, rt.id.serverKey);
33201
+ const match = fresh.find((t) => t.name === toolName2);
33202
+ if (!match) throw error;
33203
+ return await extractRawCall(match)(...args);
33204
+ }
33205
+ }
32812
33206
  async checkHealth() {
32813
33207
  if (this.healthCheckInFlight) return;
32814
33208
  this.healthCheckInFlight = true;
@@ -32827,8 +33221,12 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
32827
33221
  await withTimeout2(session.listTools(), HEALTH_CHECK_TIMEOUT_MS, async () => {
32828
33222
  });
32829
33223
  if (!this.isActive(rt) || rt.state !== "connected") continue;
32830
- } catch {
33224
+ } catch (error) {
32831
33225
  if (this.isActive(rt) && rt.state === "connected") {
33226
+ console.warn(
33227
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: health check failed; scheduling reconnect`,
33228
+ error instanceof Error ? error.message : error
33229
+ );
32832
33230
  rt.state = "failed";
32833
33231
  this.scheduleReconnect(rt);
32834
33232
  }
@@ -33034,7 +33432,8 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
33034
33432
  const visibility = Array.isArray(rawVisibility) ? rawVisibility : typeof rawVisibility === "string" ? [rawVisibility] : void 0;
33035
33433
  if (visibility && !visibility.includes("model")) continue;
33036
33434
  const ref = extractAppsRef(meta, serverKey);
33037
- const registered = ref ? wrapMcpToolWithUi(tool68, ref) : tool68;
33435
+ const resilient = this.wrapToolForResilience(rt, tool68);
33436
+ const registered = ref ? wrapMcpToolWithUi(resilient, ref) : resilient;
33038
33437
  const toolKey = _McpLatticeManager.buildToolKey(serverKey, tool68.name);
33039
33438
  try {
33040
33439
  toolLatticeManager.registerExistingToolWithTenant(tenantId2, toolKey, registered);
@@ -33103,6 +33502,7 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
33103
33502
  }
33104
33503
  }
33105
33504
  this.runtimes.clear();
33505
+ this.reconnectPromises.clear();
33106
33506
  }
33107
33507
  };
33108
33508
  var mcpManager = McpLatticeManager.getInstance();
@@ -36253,13 +36653,16 @@ var LatticeEvalProject = class {
36253
36653
  constructor(project, onCaseComplete) {
36254
36654
  this.suites = /* @__PURE__ */ new Map();
36255
36655
  this.project = project;
36256
- const judgeModelKey = resolveJudgeModelKey(this.project.judge_agent_config.modelKey, modelLatticeManager);
36656
+ const tenantId2 = this.project.lattice_server_config.tenant_id || "default";
36657
+ const judgeModelKey = resolveJudgeModelKey(
36658
+ this.project.judge_agent_config.modelKey,
36659
+ modelLatticeManager
36660
+ );
36257
36661
  if (!modelLatticeManager.hasLattice(judgeModelKey)) {
36258
36662
  throw new Error(
36259
36663
  `Judge model "${judgeModelKey}" not found. Register it first or provide a valid modelKey.`
36260
36664
  );
36261
36665
  }
36262
- const tenantId2 = this.project.lattice_server_config.tenant_id || "default";
36263
36666
  this.judgeAgentKey = `${this.project.projectName}_judge_agent`;
36264
36667
  const judgeAgentConfig = {
36265
36668
  key: this.judgeAgentKey,
@@ -37314,14 +37717,6 @@ async function applyBeliefImpacts(taskStore, owner, impacts) {
37314
37717
  }
37315
37718
  return { description: current.description ?? "", task: current };
37316
37719
  }
37317
- function parseExternalDependency(value) {
37318
- const row = (0, import_protocols31.snapshotExactRecord)(value, ["type", "summary", "requestedAt", "dependencyTaskIds"]);
37319
- const ids = (0, import_protocols31.snapshotExactArray)(row?.dependencyTaskIds);
37320
- if (!row || row.type !== "external_dependency" || typeof row.summary !== "string" || !row.summary.trim() || typeof row.requestedAt !== "string" || !Number.isFinite(Date.parse(row.requestedAt)) || !ids || ids.length === 0 || ids.some((id) => typeof id !== "string" || !id.trim())) return void 0;
37321
- const trimmed = ids.map((id) => id.trim());
37322
- if (new Set(trimmed).size !== trimmed.length) return void 0;
37323
- return { type: "external_dependency", summary: row.summary, requestedAt: row.requestedAt, dependencyTaskIds: trimmed };
37324
- }
37325
37720
  function unsupportedTaskStatus() {
37326
37721
  return failure2(
37327
37722
  "TASK_STATUS_UNSUPPORTED",
@@ -37620,9 +38015,6 @@ var TaskLifecycleService = class {
37620
38015
  if (interruption && typeof interruption === "object" && interruption.type === "review_required") {
37621
38016
  return failure2("REVIEW_APPROVAL_REQUIRED", "Review interruption requires explicit approval or rejection.");
37622
38017
  }
37623
- if (interruption && typeof interruption === "object" && interruption.type === "external_dependency" && !parseExternalDependency(interruption)) {
37624
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "Stored external dependency interruption is malformed.");
37625
- }
37626
38018
  const previousInterruption = interruption && typeof interruption === "object" ? interruption : {};
37627
38019
  const resumeKey = this.resumeEventKey(task);
37628
38020
  const audit = { eventKey: resumeKey, previousInterruption };
@@ -37803,6 +38195,108 @@ var TaskLifecycleService = class {
37803
38195
  detail: { previousStatus: "failed" }
37804
38196
  });
37805
38197
  }
38198
+ /**
38199
+ * Reopens a terminal agent task by returning it to in_progress.
38200
+ *
38201
+ * @param input Task, actor, and optional thread.
38202
+ * @returns Structured lifecycle result. `completed` and `cancelled` tasks
38203
+ * reopen to `in_progress`; an already-`in_progress` task is acknowledged
38204
+ * without mutation; any other status or a lost CAS yields
38205
+ * `TASK_STATUS_CONFLICT`.
38206
+ * @remarks This is the single reverse edge of the terminal-irreversibility
38207
+ * relaxation. It records the status change only; re-dispatch is owned by
38208
+ * the application layer and is never triggered here.
38209
+ */
38210
+ async reopenTask(input) {
38211
+ return this.transitionAgentTask({
38212
+ ...input,
38213
+ targetStatus: "in_progress",
38214
+ expectedStatuses: ["completed", "cancelled"],
38215
+ updates: { status: "in_progress", failureReason: null, result: null },
38216
+ summary: "Task reopened",
38217
+ detail: { type: "reopen" }
38218
+ });
38219
+ }
38220
+ /**
38221
+ * Transfers a human-authored task's ownership to a chosen Agent before dispatch.
38222
+ *
38223
+ * @param input Task, actor, destination Agent, and optional Task Thread.
38224
+ * @returns Structured result with the reassigned owner and an `owner_assigned` audit.
38225
+ * @remarks Only tasks with `context.source === "user"` can be assigned this way.
38226
+ * Repeated assignment to the same owner is idempotent (repair-writes the audit
38227
+ * without a CAS). This changes owner only, never status, so it publishes no
38228
+ * Project lifecycle notification.
38229
+ */
38230
+ async assignOwner(input) {
38231
+ let task;
38232
+ try {
38233
+ task = await this.deps.taskStore.getById(input.tenantId, input.taskId);
38234
+ } catch {
38235
+ return failure2("TASK_STORE_READ_FAILED", "Failed to read task lifecycle state.");
38236
+ }
38237
+ if (!task) return failure2("TASK_NOT_FOUND", "Task was not found.");
38238
+ if (task.context?.source !== "user") {
38239
+ return failure2("TASK_NOT_HUMAN_MANAGED", "Only user-authored tasks can be assigned to an Agent.");
38240
+ }
38241
+ const storedThreadId = typeof task.context?.thread_id === "string" && task.context.thread_id.length > 0 ? task.context.thread_id : void 0;
38242
+ const effectiveThreadId = storedThreadId ?? input.threadId;
38243
+ const detail = { type: "owner_assigned", fromOwnerType: task.ownerType, toOwnerId: input.ownerId };
38244
+ if (task.ownerType === "agent" && task.ownerId === input.ownerId && (input.threadId === void 0 || storedThreadId === input.threadId)) {
38245
+ const eventKey2 = this.statusEventKey(task, "owner_assigned", detail);
38246
+ const event2 = await this.writeStatusEvent(
38247
+ task,
38248
+ input.actor,
38249
+ effectiveThreadId,
38250
+ "activity",
38251
+ "Task owner assigned",
38252
+ detail,
38253
+ eventKey2
38254
+ );
38255
+ return {
38256
+ success: true,
38257
+ task,
38258
+ mutated: false,
38259
+ eventKey: event2.eventKey,
38260
+ ...event2.warning ? { warnings: [event2.warning] } : {}
38261
+ };
38262
+ }
38263
+ const context = { ...task.context ?? {} };
38264
+ if (input.threadId && !storedThreadId) context.thread_id = input.threadId;
38265
+ let updated;
38266
+ try {
38267
+ updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
38268
+ input.tenantId,
38269
+ input.taskId,
38270
+ { ownerType: "agent", ownerId: input.ownerId, context },
38271
+ [task.status],
38272
+ task.updatedAt
38273
+ );
38274
+ } catch {
38275
+ return failure2(
38276
+ "TASK_STORE_WRITE_FAILED",
38277
+ "Failed to assign the task owner.",
38278
+ "The store rejected the write."
38279
+ );
38280
+ }
38281
+ if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
38282
+ const eventKey = this.statusEventKey(updated, "owner_assigned", detail);
38283
+ const event = await this.writeStatusEvent(
38284
+ updated,
38285
+ input.actor,
38286
+ effectiveThreadId,
38287
+ "activity",
38288
+ "Task owner assigned",
38289
+ detail,
38290
+ eventKey
38291
+ );
38292
+ return {
38293
+ success: true,
38294
+ task: updated,
38295
+ mutated: true,
38296
+ eventKey: event.eventKey,
38297
+ ...event.warning ? { warnings: [event.warning] } : {}
38298
+ };
38299
+ }
37806
38300
  /** Atomically cancels a nonterminal agent task and records an idempotent status event. */
37807
38301
  async cancelTask(input) {
37808
38302
  return this.transitionAgentTask({
@@ -37883,19 +38377,7 @@ var TaskLifecycleService = class {
37883
38377
  async interruptTask(input) {
37884
38378
  const summary = input.summary.trim();
37885
38379
  if (!summary) return failure2("MISSING_INTERRUPTION_SUMMARY", "Agent interruption requires a nonblank summary.");
37886
- if (input.type !== "external_dependency" && input.dependencyTaskIds !== void 0) {
37887
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "dependencyTaskIds are only valid for external dependencies.");
37888
- }
37889
38380
  const requestedAt = this.now().toISOString();
37890
- const external = input.type === "external_dependency" ? parseExternalDependency({
37891
- type: input.type,
37892
- summary,
37893
- requestedAt,
37894
- dependencyTaskIds: input.dependencyTaskIds
37895
- }) : void 0;
37896
- if (input.type === "external_dependency" && !external) {
37897
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependencies require exact, unique nonblank task IDs.");
37898
- }
37899
38381
  let task;
37900
38382
  try {
37901
38383
  task = await this.deps.taskStore.getById(input.tenantId, input.taskId);
@@ -37909,29 +38391,17 @@ var TaskLifecycleService = class {
37909
38391
  if (threadFailure) return threadFailure;
37910
38392
  const currentInterruption = task.context?.interruption;
37911
38393
  if (task.status === "interrupted" && currentInterruption && typeof currentInterruption === "object") {
37912
- const storedExternal = parseExternalDependency(currentInterruption);
37913
- if (input.type === "external_dependency") {
37914
- if (!external || !storedExternal || storedExternal.summary !== external.summary || canonicalJson(storedExternal.dependencyTaskIds) !== canonicalJson(external.dependencyTaskIds)) {
37915
- return failure2(
37916
- "TASK_STATUS_CONFLICT",
37917
- "Task is already interrupted by a different or malformed external dependency.",
37918
- "Use manage_task get to inspect the existing interruption in task context.interruption."
37919
- );
37920
- }
37921
- } else if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
38394
+ if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
37922
38395
  return failure2(
37923
38396
  "TASK_STATUS_CONFLICT",
37924
38397
  "Task is already interrupted by a different request.",
37925
38398
  "Use manage_task get to inspect the existing interruption, or resume it first."
37926
38399
  );
37927
38400
  }
37928
- const detail = storedExternal ? { ...storedExternal } : currentInterruption;
38401
+ const detail = currentInterruption;
37929
38402
  const eventKey2 = this.statusEventKey(task, "interrupted", detail);
37930
38403
  const event2 = await this.writeStatusEvent(task, input.actor, input.threadId, "interrupted", summary, detail, eventKey2);
37931
38404
  const warnings2 = event2.warning ? [event2.warning] : [];
37932
- if (storedExternal && task.workspaceId && task.projectId) {
37933
- return this.finishExternalDependencyInterruption(task, input, storedExternal, event2, warnings2, false);
37934
- }
37935
38405
  return {
37936
38406
  success: true,
37937
38407
  task,
@@ -37947,11 +38417,7 @@ var TaskLifecycleService = class {
37947
38417
  "Only pending or in_progress tasks can be interrupted. Use manage_task get to check the current status."
37948
38418
  );
37949
38419
  }
37950
- if (external && task.workspaceId && task.projectId) {
37951
- const dependencyFailure = await this.validateExternalDependencies(task, external.dependencyTaskIds);
37952
- if (dependencyFailure) return dependencyFailure;
37953
- }
37954
- const interruption = external ? { ...external } : { type: input.type, summary, requestedAt };
38420
+ const interruption = { type: input.type, summary, requestedAt };
37955
38421
  let updated;
37956
38422
  try {
37957
38423
  updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(input.tenantId, input.taskId, {
@@ -37973,9 +38439,6 @@ var TaskLifecycleService = class {
37973
38439
  const eventKey = this.statusEventKey(updated, "interrupted", interruption);
37974
38440
  const event = await this.writeStatusEvent(updated, input.actor, input.threadId, "interrupted", summary, interruption, eventKey);
37975
38441
  const warnings = event.warning ? [event.warning] : await this.notifyWinningTransition(updated, "interrupted", event);
37976
- if (external && updated.workspaceId && updated.projectId) {
37977
- return this.finishExternalDependencyInterruption(updated, input, external, event, warnings, true);
37978
- }
37979
38442
  return {
37980
38443
  success: true,
37981
38444
  task: updated,
@@ -39214,7 +39677,9 @@ var TaskLifecycleService = class {
39214
39677
  };
39215
39678
  }
39216
39679
  if (!input.expectedStatuses.includes(task.status)) return failure2("TASK_STATUS_CONFLICT", "Task status cannot make this transition.");
39217
- const updates = { ...input.updates, context: clearInterruptionContext(task.context) };
39680
+ const clearedContext = clearInterruptionContext(task.context);
39681
+ const withThread = input.threadId && task.context?.thread_id === void 0 ? { ...clearedContext ?? {}, thread_id: input.threadId } : clearedContext;
39682
+ const updates = { ...input.updates, context: withThread };
39218
39683
  let updated;
39219
39684
  try {
39220
39685
  updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
@@ -39302,85 +39767,6 @@ var TaskLifecycleService = class {
39302
39767
  return [{ code: "LIFECYCLE_NOTIFICATION_FAILED", taskId: task.id, eventKey: event.eventKey }];
39303
39768
  }
39304
39769
  }
39305
- async validateExternalDependencies(task, dependencyTaskIds) {
39306
- const declared = new Set(task.dependencies ?? []);
39307
- if (dependencyTaskIds.some((id) => id === task.id || !declared.has(id))) {
39308
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependency IDs must be declared non-self dependencies.");
39309
- }
39310
- try {
39311
- for (const id of dependencyTaskIds) {
39312
- const dependency = await this.deps.taskStore.getById(task.tenantId, id);
39313
- if (!dependency || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId || dependency.status === "completed") {
39314
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependencies must exist, remain incomplete, and share exact scope.");
39315
- }
39316
- }
39317
- } catch {
39318
- return failure2("TASK_STORE_READ_FAILED", "Failed to validate external dependencies.");
39319
- }
39320
- return void 0;
39321
- }
39322
- async externalDependencyReadiness(task, dependencyTaskIds) {
39323
- try {
39324
- for (const id of dependencyTaskIds) {
39325
- const dependency = await this.deps.taskStore.getById(task.tenantId, id);
39326
- if (!dependency || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId || dependency.status !== "completed") return { kind: "blocked" };
39327
- }
39328
- return { kind: "ready" };
39329
- } catch {
39330
- return {
39331
- kind: "error",
39332
- warning: { code: "DEPENDENCY_READ_FAILED", taskId: task.id }
39333
- };
39334
- }
39335
- }
39336
- async finishExternalDependencyInterruption(task, input, interruption, event, warnings, mutated) {
39337
- if (event.warning) {
39338
- return { success: true, task, mutated, eventKey: event.eventKey, warnings };
39339
- }
39340
- const readiness = await this.externalDependencyReadiness(task, interruption.dependencyTaskIds);
39341
- if (readiness.kind === "blocked") {
39342
- return { success: true, task, mutated, eventKey: event.eventKey, ...warnings.length ? { warnings } : {} };
39343
- }
39344
- if (readiness.kind === "error") {
39345
- return {
39346
- success: true,
39347
- task,
39348
- mutated,
39349
- eventKey: event.eventKey,
39350
- warnings: [...warnings, { ...readiness.warning, eventKey: event.eventKey }]
39351
- };
39352
- }
39353
- const resumed = await this.resumeInterruption({
39354
- tenantId: input.tenantId,
39355
- taskId: input.taskId,
39356
- actor: input.actor,
39357
- threadId: input.threadId
39358
- });
39359
- if (resumed.success) {
39360
- const combined = [...warnings, ...resumed.warnings ?? []];
39361
- return {
39362
- ...resumed,
39363
- mutated: mutated || resumed.mutated,
39364
- ...combined.length ? { warnings: combined } : {}
39365
- };
39366
- }
39367
- let current;
39368
- try {
39369
- current = await this.deps.taskStore.getById(input.tenantId, input.taskId);
39370
- } catch {
39371
- return resumed;
39372
- }
39373
- if (current?.status === "in_progress" && (!current.context?.thread_id || current.context?.thread_id === input.threadId)) {
39374
- return {
39375
- success: true,
39376
- task: current,
39377
- mutated,
39378
- eventKey: event.eventKey,
39379
- ...warnings.length ? { warnings } : {}
39380
- };
39381
- }
39382
- return resumed;
39383
- }
39384
39770
  async clearAuditMarker(task, key5) {
39385
39771
  const context = withoutContextKey(task.context, key5);
39386
39772
  try {
@@ -40626,6 +41012,47 @@ function clearEncryptionKeyCache() {
40626
41012
  keyValidated = false;
40627
41013
  }
40628
41014
 
41015
+ // src/util/safeUpstreamUrl.ts
41016
+ var UnsafeUpstreamUrlError = class extends Error {
41017
+ constructor(reason, message, statusCode = 400) {
41018
+ super(message);
41019
+ this.reason = reason;
41020
+ this.statusCode = statusCode;
41021
+ this.name = "UnsafeUpstreamUrlError";
41022
+ }
41023
+ };
41024
+ async function assertSafeUpstreamUrl(raw) {
41025
+ let url;
41026
+ try {
41027
+ url = new URL(raw);
41028
+ } catch {
41029
+ throw new UnsafeUpstreamUrlError(
41030
+ "INVALID_BASE_URL",
41031
+ "Base URL is not a valid URL"
41032
+ );
41033
+ }
41034
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
41035
+ throw new UnsafeUpstreamUrlError(
41036
+ "UNSUPPORTED_SCHEME",
41037
+ "Base URL must use http or https"
41038
+ );
41039
+ }
41040
+ if (url.username.length > 0 || url.password.length > 0) {
41041
+ throw new UnsafeUpstreamUrlError(
41042
+ "CREDENTIALS_IN_URL",
41043
+ "Base URL must not embed credentials; configure the API key separately"
41044
+ );
41045
+ }
41046
+ const hostname = url.hostname.replace(/^\[/, "").replace(/\]$/, "");
41047
+ if (hostname.length === 0) {
41048
+ throw new UnsafeUpstreamUrlError(
41049
+ "INVALID_BASE_URL",
41050
+ "Base URL must include a host"
41051
+ );
41052
+ }
41053
+ return { url };
41054
+ }
41055
+
40629
41056
  // src/middlewares/skillMiddleware.ts
40630
41057
  var import_langchain66 = require("langchain");
40631
41058
 
@@ -43273,6 +43700,9 @@ function trustedRosterScope(trusted) {
43273
43700
  callerAssistantId: context.assistantId
43274
43701
  };
43275
43702
  }
43703
+ function trustedRosterExempt(trusted) {
43704
+ return trusted.projectTask?.rosterExempt === true;
43705
+ }
43276
43706
  async function requireTrustedOwner(trusted, targetAssistantId) {
43277
43707
  const context = trusted.projectTask ?? trusted.projectRoom;
43278
43708
  const memberships = getStoreLattice("default", "projectBotMembership").store;
@@ -43537,6 +43967,7 @@ async function validateTrustedChildDependencyWait(store, tenantId2, parent, depe
43537
43967
  if (dependency.status === "completed") continue;
43538
43968
  if (!["pending", "in_progress", "interrupted"].includes(dependency.status) || dependency.parentId !== parent.id || dependency.ownerType !== "agent" || !hasExactContextSource(dependency, expectedSource)) return void 0;
43539
43969
  incompleteChildren += 1;
43970
+ if (trustedRosterExempt(trusted)) continue;
43540
43971
  try {
43541
43972
  await requireTrustedOwner(trusted, dependency.ownerId);
43542
43973
  } catch (error) {
@@ -43611,7 +44042,6 @@ var manageTaskSchema = import_zod68.z.object({
43611
44042
  ownerId: import_zod68.z.string().optional().describe("Owner ID. Auto-filled from current user/agent if omitted"),
43612
44043
  requireReview: import_zod68.z.boolean().optional().describe("If true, agent completion pauses as interrupted(review_required) for HITL approval"),
43613
44044
  dependencies: import_zod68.z.array(import_zod68.z.string()).optional().describe("List of task IDs that must be completed before this task can start"),
43614
- dependencyTaskIds: import_zod68.z.array(import_zod68.z.string().trim().min(1)).optional().describe("Exact blocking task IDs required for an external_dependency interruption"),
43615
44045
  result: import_zod68.z.string().optional().describe("Result summary. Used with set_status status='completed' or update status='completed'"),
43616
44046
  failureReason: import_zod68.z.string().optional().describe("Failure reason. Used with set_status status='failed' or update status='failed'"),
43617
44047
  files: import_zod68.z.array(import_zod68.z.object({
@@ -43780,7 +44210,7 @@ function createTaskMiddleware(options = {}) {
43780
44210
  const workspaceId = trustedScope?.workspaceId ?? rc.workspaceId;
43781
44211
  const projectId = trustedScope?.projectId ?? rc.projectId;
43782
44212
  const revalidateCaller = async () => {
43783
- if (!trustedProject) return void 0;
44213
+ if (!trustedProject || trustedRosterExempt(trustedProject)) return void 0;
43784
44214
  try {
43785
44215
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
43786
44216
  } catch (error) {
@@ -43788,7 +44218,7 @@ function createTaskMiddleware(options = {}) {
43788
44218
  }
43789
44219
  return void 0;
43790
44220
  };
43791
- if (trustedProject) {
44221
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
43792
44222
  try {
43793
44223
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
43794
44224
  } catch (error) {
@@ -43821,10 +44251,12 @@ function createTaskMiddleware(options = {}) {
43821
44251
  if (trustedProject) {
43822
44252
  const snapshot = taskMutationSnapshot(existing);
43823
44253
  if (!snapshot) return trustedContextError();
43824
- try {
43825
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
43826
- } catch (error) {
43827
- return projectPolicyError(error);
44254
+ if (!trustedRosterExempt(trustedProject)) {
44255
+ try {
44256
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
44257
+ } catch (error) {
44258
+ return projectPolicyError(error);
44259
+ }
43828
44260
  }
43829
44261
  deleted = await store.deleteIfSnapshot(tenantId2, id, snapshot);
43830
44262
  } else {
@@ -43877,13 +44309,15 @@ function createTaskMiddleware(options = {}) {
43877
44309
  "Target Project task owner is not active in the exact roster."
43878
44310
  ));
43879
44311
  }
43880
- try {
43881
- await requireTrustedOwner(trustedProject, ownerId);
43882
- } catch (error) {
43883
- return projectPolicyError(error instanceof ProjectTaskOwnerPolicyError ? error : new ProjectTaskOwnerPolicyError(
43884
- "PROJECT_TASK_OWNER_INACTIVE",
43885
- "Target Project task owner is not active in the exact roster."
43886
- ));
44312
+ if (!trustedRosterExempt(trustedProject)) {
44313
+ try {
44314
+ await requireTrustedOwner(trustedProject, ownerId);
44315
+ } catch (error) {
44316
+ return projectPolicyError(error instanceof ProjectTaskOwnerPolicyError ? error : new ProjectTaskOwnerPolicyError(
44317
+ "PROJECT_TASK_OWNER_INACTIVE",
44318
+ "Target Project task owner is not active in the exact roster."
44319
+ ));
44320
+ }
43887
44321
  }
43888
44322
  }
43889
44323
  if (effectiveOwnerType === "agent" && (input.result !== void 0 || input.failureReason !== void 0)) {
@@ -43931,10 +44365,12 @@ function createTaskMiddleware(options = {}) {
43931
44365
  )) return trustedContextError("Parent or dependency task changed before creation.");
43932
44366
  const callerError = await revalidateCaller();
43933
44367
  if (callerError) return callerError;
43934
- try {
43935
- await requireTrustedOwner(trustedProject, ownerId);
43936
- } catch (error) {
43937
- return projectPolicyError(error);
44368
+ if (!trustedRosterExempt(trustedProject)) {
44369
+ try {
44370
+ await requireTrustedOwner(trustedProject, ownerId);
44371
+ } catch (error) {
44372
+ return projectPolicyError(error);
44373
+ }
43938
44374
  }
43939
44375
  }
43940
44376
  return store.create({
@@ -44192,18 +44628,6 @@ function createTaskMiddleware(options = {}) {
44192
44628
  return trustedFailure;
44193
44629
  }
44194
44630
  }
44195
- if (trustedProject && input.dependencyTaskIds !== void 0) {
44196
- const interruption = input.context?.interruption;
44197
- const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
44198
- if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
44199
- taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
44200
- return JSON.stringify({
44201
- success: false,
44202
- code: "INVALID_EXTERNAL_DEPENDENCY",
44203
- error: "dependencyTaskIds are only valid for external dependency interruptions."
44204
- });
44205
- }
44206
- }
44207
44631
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
44208
44632
  taskUpdateBlocked(input.id, "TASK_OWNERSHIP_VIA_DISPATCH_REQUIRED", {
44209
44633
  inputOwnerType: input.ownerType,
@@ -44395,7 +44819,7 @@ function createTaskMiddleware(options = {}) {
44395
44819
  return trustedContextError("Parent or dependency task changed before update.");
44396
44820
  }
44397
44821
  }
44398
- if (trustedProject) {
44822
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
44399
44823
  try {
44400
44824
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
44401
44825
  } catch (error) {
@@ -44435,7 +44859,7 @@ function createTaskMiddleware(options = {}) {
44435
44859
  const workItemSummary = input.summary;
44436
44860
  let activityWarnings;
44437
44861
  try {
44438
- if (trustedProject) {
44862
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
44439
44863
  try {
44440
44864
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
44441
44865
  } catch (error) {
@@ -44520,18 +44944,6 @@ function createTaskMiddleware(options = {}) {
44520
44944
  return trustedFailure;
44521
44945
  }
44522
44946
  }
44523
- if (trustedProject && input.dependencyTaskIds !== void 0) {
44524
- const interruption = input.context?.interruption;
44525
- const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
44526
- if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
44527
- taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
44528
- return JSON.stringify({
44529
- success: false,
44530
- code: "INVALID_EXTERNAL_DEPENDENCY",
44531
- error: "dependencyTaskIds are only valid for external dependency interruptions."
44532
- });
44533
- }
44534
- }
44535
44947
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
44536
44948
  taskUpdateBlocked(input.id, "TASK_OWNERSHIP_VIA_DISPATCH_REQUIRED", {});
44537
44949
  return JSON.stringify({
@@ -44613,11 +45025,11 @@ function createTaskMiddleware(options = {}) {
44613
45025
  const interruption = input.context?.interruption;
44614
45026
  const explicitType = interruption && typeof interruption === "object" ? interruption.type : void 0;
44615
45027
  const type = explicitType ?? "missing_input";
44616
- if (type !== "missing_input" && type !== "external_dependency" && type !== "user_decision") {
45028
+ if (type !== "missing_input" && type !== "user_decision") {
44617
45029
  return JSON.stringify({
44618
45030
  success: false,
44619
45031
  code: "INVALID_INTERRUPTION_TYPE",
44620
- error: "Agent interruption type must be missing_input, external_dependency, or user_decision."
45032
+ error: "Agent interruption type must be missing_input or user_decision."
44621
45033
  });
44622
45034
  }
44623
45035
  await validateExecutionResult();
@@ -44629,8 +45041,7 @@ function createTaskMiddleware(options = {}) {
44629
45041
  type,
44630
45042
  summary: input.summary ?? "",
44631
45043
  actor,
44632
- threadId: statusThreadId,
44633
- dependencyTaskIds: input.dependencyTaskIds
45044
+ threadId: statusThreadId
44634
45045
  });
44635
45046
  return lifecycleMutationResponse(interrupted);
44636
45047
  }
@@ -45338,7 +45749,6 @@ canonical Belief State:
45338
45749
  };
45339
45750
 
45340
45751
  // src/middlewares/documentLearningMiddleware.ts
45341
- var import_protocols34 = require("@axiom-lattice/protocols");
45342
45752
  var import_langchain84 = require("langchain");
45343
45753
 
45344
45754
  // src/middlewares/documentLearningSkills.ts
@@ -45583,15 +45993,6 @@ var createDocumentLearningMiddleware = () => (0, import_langchain84.createMiddle
45583
45993
  contextSchema,
45584
45994
  tools: []
45585
45995
  });
45586
- var DOCUMENT_LEARNER_SYSTEM_PROMPT = `You are a capability learning specialist.
45587
-
45588
- CRITICAL FIRST ACTION \u2014 before any response about the task:
45589
- 1. Call the \`skill\` tool with skill_name: "agent-architecture" to load the
45590
- authoritative Charter.
45591
- 2. Call the \`skill\` tool with skill_name: "document-learning-learn-capability" to
45592
- load the source-to-capability workflow.
45593
- Never announce that you will follow a skill. Load both in this order and follow
45594
- their content. If either load fails, retry it once, then report the failure.`;
45595
45996
  var documentLearningPlugin = {
45596
45997
  meta: {
45597
45998
  type: "document-learning",
@@ -45600,92 +46001,6 @@ var documentLearningPlugin = {
45600
46001
  description: "\u4ECE\u6587\u6863\u4E2D\u5B66\u4E60\u77E5\u8BC6\uFF0C\u81EA\u52A8\u6784\u5EFA\u6280\u80FD\u4F53\u7CFB\u548C\u8BC4\u6D4B\u3002 Learn knowledge from documents and build structured skill systems with evaluations.",
45601
46002
  recommends: ["skill", "eval"]
45602
46003
  },
45603
- agents: {
45604
- "document-learner": {
45605
- key: "document-learner",
45606
- name: "Document Learning Agent",
45607
- description: "Read documents and build structured skill systems with evaluations. Use this agent when you need to extract knowledge from documents and turn it into reusable, testable agent skills.",
45608
- type: import_protocols34.AgentType.DEEP_AGENT,
45609
- prompt: DOCUMENT_LEARNER_SYSTEM_PROMPT,
45610
- subAgents: ["document-parser-benchmark"],
45611
- tools: [
45612
- "invoke_agent",
45613
- "list_agents",
45614
- "create_agent",
45615
- "update_agent",
45616
- "delete_agent",
45617
- "get_agent",
45618
- "list_tools",
45619
- "list_middleware_types"
45620
- ],
45621
- middleware: [
45622
- {
45623
- id: "skill",
45624
- type: "skill",
45625
- name: "Skill",
45626
- description: "Skill creation and loading",
45627
- enabled: true,
45628
- config: { readAll: true }
45629
- },
45630
- {
45631
- id: "document-learning",
45632
- type: "document-learning",
45633
- name: "Document Learning",
45634
- description: "Source-to-capability learning workflow",
45635
- enabled: true,
45636
- config: {}
45637
- },
45638
- {
45639
- id: "eval",
45640
- type: "eval",
45641
- name: "Eval",
45642
- description: "Evaluation design and execution",
45643
- enabled: true,
45644
- config: {}
45645
- },
45646
- {
45647
- id: "task",
45648
- type: "task",
45649
- name: "Task",
45650
- description: "Training process tracking with approval gates",
45651
- enabled: true,
45652
- config: {}
45653
- },
45654
- {
45655
- id: "widget",
45656
- type: "widget",
45657
- name: "Widget",
45658
- description: "Visualize benchmark results and eval dashboards",
45659
- enabled: true,
45660
- config: {}
45661
- },
45662
- {
45663
- id: "ask_user_to_clarify",
45664
- type: "ask_user_to_clarify",
45665
- name: "Ask User",
45666
- description: "Wait for user input at approval gates",
45667
- enabled: true,
45668
- config: {}
45669
- },
45670
- {
45671
- id: "filesystem",
45672
- type: "filesystem",
45673
- name: "Filesystem",
45674
- description: "Read documents, write skill files",
45675
- enabled: true,
45676
- config: {}
45677
- },
45678
- {
45679
- id: "document-parser",
45680
- type: "document-parser",
45681
- name: "Document Parser",
45682
- description: "Parse documents with the chosen engine",
45683
- enabled: true,
45684
- config: { connectAll: true }
45685
- }
45686
- ]
45687
- }
45688
- },
45689
46004
  skills: {
45690
46005
  "document-learning-learn-capability": {
45691
46006
  version: "1.0.0",
@@ -45914,7 +46229,7 @@ function createParseDocumentTool({
45914
46229
  }
45915
46230
 
45916
46231
  // src/middlewares/documentParserMiddleware.ts
45917
- var import_protocols35 = require("@axiom-lattice/protocols");
46232
+ var import_protocols34 = require("@axiom-lattice/protocols");
45918
46233
 
45919
46234
  // src/middlewares/documentBenchmarkWorkflow.ts
45920
46235
  var DOCUMENT_PARSER_BENCHMARK_YAML = `name: document-parser-benchmark
@@ -46367,6 +46682,23 @@ function createDocumentParserMiddleware(config) {
46367
46682
  tools: [createParseDocumentTool({ connectAll, connections, baseUrl, apiKey })]
46368
46683
  });
46369
46684
  }
46685
+ var DOCUMENT_PARSER_SYSTEM_PROMPT = `You are the Document Parser. Turn documents (pdf, docx, images, spreadsheets, and similar) into clean Markdown.
46686
+
46687
+ CRITICAL FIRST ACTION: call the \`skill\` tool with skill_name: "document-parser-markitdown" and follow it. Never announce skill loads.
46688
+
46689
+ DEFAULT \u2014 local MarkItDown:
46690
+ - Single file: run \`markitdown <input> -o <output.md>\` via shell.
46691
+ - Batch or literature collections: use the scripts bundled in the skill.
46692
+ - After converting, verify the output is non-empty and its structure/tables are intact, then report the exact input -> output paths.
46693
+
46694
+ ESCALATION \u2014 remote parse_document:
46695
+ - Call parse_document only when local conversion fails, the format is unsupported, the output is empty or materially incomplete, the structure is badly damaged, OCR quality is inadequate, or the user explicitly asks for professional parsing.
46696
+ - When the user is unsatisfied with quality, use ask_user_to_clarify to ask whether to switch to a remote engine, and recommend one engine based on the observed file features and the user's feedback. If the user names an engine, use it.
46697
+
46698
+ BENCHMARK:
46699
+ - Only when the user explicitly asks to compare engines. Confirm with ask_user_to_clarify first, then delegate to the document-parser-benchmark subagent. Never benchmark by default.
46700
+
46701
+ Report only what you actually produced; never claim success without inspecting the output file.`;
46370
46702
  var documentParserPlugin = {
46371
46703
  meta: {
46372
46704
  type: "document-parser",
@@ -46437,9 +46769,59 @@ var documentParserPlugin = {
46437
46769
  },
46438
46770
  middleware: (config) => createDocumentParserMiddleware(config),
46439
46771
  agents: {
46772
+ "document-parser": {
46773
+ key: "document-parser",
46774
+ type: import_protocols34.AgentType.DEEP_AGENT,
46775
+ name: "Document Parser",
46776
+ description: "Parse documents (pdf, docx, images, spreadsheets) into clean Markdown. Defaults to local MarkItDown; escalates to remote engines only when quality or format needs it; runs engine benchmarking only on explicit request.",
46777
+ prompt: DOCUMENT_PARSER_SYSTEM_PROMPT,
46778
+ subAgents: ["document-parser-benchmark"],
46779
+ middleware: [
46780
+ {
46781
+ id: "skill",
46782
+ type: "skill",
46783
+ name: "Skill",
46784
+ description: "Load the local MarkItDown parsing procedure",
46785
+ enabled: true,
46786
+ config: { readAll: true }
46787
+ },
46788
+ {
46789
+ id: "filesystem",
46790
+ type: "filesystem",
46791
+ name: "Filesystem",
46792
+ description: "Read source documents and write Markdown output",
46793
+ enabled: true,
46794
+ config: { vmIsolation: "project" }
46795
+ },
46796
+ {
46797
+ id: "code_eval",
46798
+ type: "code_eval",
46799
+ name: "Code Runner",
46800
+ description: "Run the markitdown CLI and bundled conversion scripts",
46801
+ enabled: true,
46802
+ config: { vmIsolation: "project" }
46803
+ },
46804
+ {
46805
+ id: "document-parser",
46806
+ type: "document-parser",
46807
+ name: "Document Parser",
46808
+ description: "Remote parse_document fallback for hard documents",
46809
+ enabled: true,
46810
+ config: { connectAll: true }
46811
+ },
46812
+ {
46813
+ id: "ask_user_to_clarify",
46814
+ type: "ask_user_to_clarify",
46815
+ name: "Ask User",
46816
+ description: "Confirm engine switches and benchmark runs",
46817
+ enabled: true,
46818
+ config: {}
46819
+ }
46820
+ ]
46821
+ },
46440
46822
  "document-parser-benchmark": {
46441
46823
  key: "document-parser-benchmark",
46442
- type: import_protocols35.AgentType.WORKFLOW,
46824
+ type: import_protocols34.AgentType.WORKFLOW,
46443
46825
  name: "Document Parser Benchmark",
46444
46826
  description: "Benchmark document parsing engines. Parses a document with 5 engines in parallel, scores results, and produces a ranking report. Use this when you need to evaluate which parsing engine works best for a given document.",
46445
46827
  prompt: "Benchmark document parsing engines: parse with 5 engines, score, and rank results.",
@@ -46476,7 +46858,7 @@ var documentParserPlugin = {
46476
46858
 
46477
46859
  // src/middlewares/projectRoomMiddleware.ts
46478
46860
  var import_crypto10 = require("crypto");
46479
- var import_protocols36 = require("@axiom-lattice/protocols");
46861
+ var import_protocols35 = require("@axiom-lattice/protocols");
46480
46862
  var import_langchain87 = require("langchain");
46481
46863
  var import_zod70 = require("zod");
46482
46864
  var PROJECT_ROOM_CONTEXT_REQUIRED = "PROJECT_ROOM_CONTEXT_REQUIRED";
@@ -46506,7 +46888,7 @@ function ownDataValue2(record, key5) {
46506
46888
  if (typeof record !== "object" || record === null || Array.isArray(record)) return void 0;
46507
46889
  try {
46508
46890
  const descriptor = Object.getOwnPropertyDescriptor(record, key5);
46509
- return (0, import_protocols36.descriptorDataValue)(descriptor)?.value;
46891
+ return (0, import_protocols35.descriptorDataValue)(descriptor)?.value;
46510
46892
  } catch {
46511
46893
  return void 0;
46512
46894
  }
@@ -46516,14 +46898,14 @@ function readTrustedRoomScope(config) {
46516
46898
  const runConfig = ownDataValue2(configurable, "runConfig");
46517
46899
  const projectRoom = ownDataValue2(runConfig, "projectRoom");
46518
46900
  try {
46519
- return (0, import_protocols36.parseTrustedRunContext)({ projectRoom }).projectRoom;
46901
+ return (0, import_protocols35.parseTrustedRunContext)({ projectRoom }).projectRoom;
46520
46902
  } catch {
46521
46903
  return void 0;
46522
46904
  }
46523
46905
  }
46524
46906
  function exactValues2(value, required, optional = []) {
46525
46907
  try {
46526
- return (0, import_protocols36.snapshotExactRecord)(value, required, optional);
46908
+ return (0, import_protocols35.snapshotExactRecord)(value, required, optional);
46527
46909
  } catch {
46528
46910
  return void 0;
46529
46911
  }
@@ -46559,7 +46941,7 @@ function snapshotMembership3(value) {
46559
46941
  return { ...values, joinedAt, updatedAt };
46560
46942
  }
46561
46943
  function snapshotRows(value) {
46562
- return (0, import_protocols36.snapshotExactArray)(value) ?? [];
46944
+ return (0, import_protocols35.snapshotExactArray)(value) ?? [];
46563
46945
  }
46564
46946
  async function hasActiveCurrentMembership(scope) {
46565
46947
  const memberships = getStoreLattice("default", "projectBotMembership").store;
@@ -46646,7 +47028,7 @@ var projectRoomPlugin = {
46646
47028
  // src/middlewares/sandboxPluginsMiddleware.ts
46647
47029
  var import_langchain88 = require("langchain");
46648
47030
  var import_zod71 = require("zod");
46649
- var import_protocols37 = require("@axiom-lattice/protocols");
47031
+ var import_protocols36 = require("@axiom-lattice/protocols");
46650
47032
 
46651
47033
  // src/middlewares/sandboxPluginsSkills.ts
46652
47034
  var PLUGIN_JSON_TEMPLATE = `{
@@ -47396,7 +47778,7 @@ var sandboxPluginsPlugin = {
47396
47778
  key: "plugin-builder",
47397
47779
  name: "Plugin Builder",
47398
47780
  description: "Author and manage tenant plugins in the shared sandbox: clarify goal, inputs/outputs, credentials, and whether a UI is needed; then write the manifest and JS handlers, add an optional HTML MCP App UI, and validate/test with the manage_plugin tool.",
47399
- type: import_protocols37.AgentType.DEEP_AGENT,
47781
+ type: import_protocols36.AgentType.DEEP_AGENT,
47400
47782
  prompt: PLUGIN_BUILDER_PROMPT,
47401
47783
  middleware: [
47402
47784
  {
@@ -48084,6 +48466,7 @@ registerBuiltinPlugins();
48084
48466
  InMemoryDatabaseConfigStore,
48085
48467
  InMemoryMailboxStore,
48086
48468
  InMemoryMenuStore,
48469
+ InMemoryModelProviderStore,
48087
48470
  InMemoryOpenAuditStore,
48088
48471
  InMemoryProjectBotMembershipStore,
48089
48472
  InMemoryProjectMembershipStore,
@@ -48177,6 +48560,7 @@ registerBuiltinPlugins();
48177
48560
  ThreadStatus,
48178
48561
  TokenCache,
48179
48562
  ToolLatticeManager,
48563
+ UnsafeUpstreamUrlError,
48180
48564
  VectorStoreLatticeManager,
48181
48565
  VectorStoreProviderManager,
48182
48566
  VolumeFilesystem,
@@ -48185,6 +48569,7 @@ registerBuiltinPlugins();
48185
48569
  agentLatticeManager,
48186
48570
  appendReconciliationWarning,
48187
48571
  appendUiFence,
48572
+ assertSafeUpstreamUrl,
48188
48573
  buildGrepResultsDict,
48189
48574
  buildInput,
48190
48575
  buildNamedVolumeName,
@@ -48316,6 +48701,7 @@ registerBuiltinPlugins();
48316
48701
  invokeWithRetry,
48317
48702
  isBuiltInSkill,
48318
48703
  isDirectRoom,
48704
+ isMcpConnectionError,
48319
48705
  isPluginSkillAuthorizedInSandbox,
48320
48706
  isPluginSkillManagedInSandbox,
48321
48707
  isUsingDefaultKey,
@@ -48338,7 +48724,6 @@ registerBuiltinPlugins();
48338
48724
  parallelLimit,
48339
48725
  parseCronExpression,
48340
48726
  parseDirectRoomType,
48341
- parseExternalDependency,
48342
48727
  parseJudgeVerdict,
48343
48728
  parsePackSize,
48344
48729
  parseQueuedExecutionMode,
@@ -48367,6 +48752,7 @@ registerBuiltinPlugins();
48367
48752
  registerExistingToolWithTenant,
48368
48753
  registerLoggerLattice,
48369
48754
  registerModelLattice,
48755
+ registerModelLatticeForTenant,
48370
48756
  registerQueueLattice,
48371
48757
  registerSTTModelLattice,
48372
48758
  registerSandboxProviderType,