@axiom-lattice/core 6.3.2 → 6.4.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
@@ -1621,6 +1621,7 @@ __export(index_exports, {
1621
1621
  CapabilityBundleValidationError: () => CapabilityBundleValidationError,
1622
1622
  CapabilityToolNameConflictError: () => CapabilityToolNameConflictError,
1623
1623
  CapabilityToolRegistry: () => CapabilityToolRegistry,
1624
+ ChannelConnectionLeader: () => ChannelConnectionLeader,
1624
1625
  ChunkBuffer: () => ChunkBuffer,
1625
1626
  ChunkBufferLatticeManager: () => ChunkBufferLatticeManager,
1626
1627
  CollectionLatticeManager: () => CollectionLatticeManager,
@@ -1632,7 +1633,9 @@ __export(index_exports, {
1632
1633
  DEFAULT_CALIBRATION_PROBES: () => DEFAULT_CALIBRATION_PROBES,
1633
1634
  DEFAULT_CONNECT_TIMEOUT_MS: () => DEFAULT_CONNECT_TIMEOUT_MS,
1634
1635
  DEFAULT_DELAY_MS: () => DEFAULT_DELAY_MS,
1636
+ DEFAULT_HANDLER_TIMEOUT_MS: () => DEFAULT_HANDLER_TIMEOUT_MS,
1635
1637
  DEFAULT_MAX_ATTEMPTS: () => DEFAULT_MAX_ATTEMPTS,
1638
+ DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS: () => DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS,
1636
1639
  DaytonaInstance: () => DaytonaInstance,
1637
1640
  DaytonaProvider: () => DaytonaProvider,
1638
1641
  DefaultScheduleClient: () => DefaultScheduleClient,
@@ -1650,6 +1653,7 @@ __export(index_exports, {
1650
1653
  HEALTH_CHECK_INTERVAL_MS: () => HEALTH_CHECK_INTERVAL_MS,
1651
1654
  HEALTH_CHECK_TIMEOUT_MS: () => HEALTH_CHECK_TIMEOUT_MS,
1652
1655
  HumanMessage: () => import_messages9.HumanMessage,
1656
+ INLINE_INPUT_LIMIT_BYTES: () => INLINE_INPUT_LIMIT_BYTES2,
1653
1657
  IdRemapper: () => IdRemapper,
1654
1658
  InMemoryA2AApiKeyStore: () => InMemoryA2AApiKeyStore,
1655
1659
  InMemoryAgentWebAppStore: () => InMemoryAgentWebAppStore,
@@ -1690,10 +1694,12 @@ __export(index_exports, {
1690
1694
  LocalSandboxProvider: () => LocalSandboxProvider,
1691
1695
  LoggerLatticeManager: () => LoggerLatticeManager,
1692
1696
  MAX_BACKOFF_MS: () => MAX_BACKOFF_MS,
1697
+ MAX_HANDLER_TIMEOUT_MS: () => MAX_HANDLER_TIMEOUT_MS,
1693
1698
  MAX_LINE_LENGTH: () => MAX_LINE_LENGTH,
1694
1699
  MAX_PACK_ENTRIES: () => MAX_PACK_ENTRIES,
1695
1700
  MAX_PACK_FILE_BYTES: () => MAX_PACK_FILE_BYTES,
1696
1701
  MAX_PACK_TOTAL_BYTES: () => MAX_PACK_TOTAL_BYTES,
1702
+ MIN_HANDLER_TIMEOUT_MS: () => MIN_HANDLER_TIMEOUT_MS,
1697
1703
  ManagedTaskStartError: () => ManagedTaskStartError,
1698
1704
  McpLatticeManager: () => McpLatticeManager,
1699
1705
  MemoryBackend: () => MemoryBackend,
@@ -1712,8 +1718,12 @@ __export(index_exports, {
1712
1718
  PACK_DIRECTORY_TIMEOUT_SECONDS: () => PACK_DIRECTORY_TIMEOUT_SECONDS,
1713
1719
  PLATFORM_SECRET_ENV_VARS: () => PLATFORM_SECRET_ENV_VARS,
1714
1720
  PLUGIN_CATALOG_CHANGED_TOPIC: () => PLUGIN_CATALOG_CHANGED_TOPIC,
1721
+ PLUGIN_HOST_DIR: () => PLUGIN_HOST_DIR,
1722
+ PLUGIN_HOST_SERVER_SOURCE: () => PLUGIN_HOST_SERVER_SOURCE,
1723
+ PLUGIN_HOST_VERSION: () => PLUGIN_HOST_VERSION,
1715
1724
  PersonalAssistantConfig: () => PersonalAssistantConfig,
1716
1725
  PinoLoggerClient: () => PinoLoggerClient,
1726
+ PluginDeploymentRuntimeRegistry: () => PluginDeploymentRuntimeRegistry,
1717
1727
  PluginRegistry: () => PluginRegistry,
1718
1728
  PostgresDatabase: () => PostgresDatabase,
1719
1729
  ProjectBotMembershipIdConflictError: () => ProjectBotMembershipIdConflictError,
@@ -1775,7 +1785,9 @@ __export(index_exports, {
1775
1785
  appendUiFence: () => appendUiFence,
1776
1786
  assertSafeUpstreamUrl: () => assertSafeUpstreamUrl,
1777
1787
  buildBundleFromDist: () => buildBundleFromDist,
1788
+ buildConnectionPayload: () => buildConnectionPayload2,
1778
1789
  buildGrepResultsDict: () => buildGrepResultsDict,
1790
+ buildHandlerCommand: () => buildHandlerCommand,
1779
1791
  buildInput: () => buildInput,
1780
1792
  buildNamedVolumeName: () => buildNamedVolumeName,
1781
1793
  buildRemotePackDirectoryCommand: () => buildRemotePackDirectoryCommand,
@@ -1790,6 +1802,7 @@ __export(index_exports, {
1790
1802
  clearEncryptionKeyCache: () => clearEncryptionKeyCache,
1791
1803
  clearEvalRunService: () => clearEvalRunService,
1792
1804
  clearSandboxOpenCredentialProviderForTests: () => clearSandboxOpenCredentialProviderForTests,
1805
+ collectPluginSources: () => collectPluginSources,
1793
1806
  collectionLatticeManager: () => collectionLatticeManager,
1794
1807
  compileInternal: () => compileInternal,
1795
1808
  compileSandboxPlugin: () => compileSandboxPlugin,
@@ -1842,9 +1855,11 @@ __export(index_exports, {
1842
1855
  decrypt: () => decrypt,
1843
1856
  decryptConfigFields: () => decryptConfigFields,
1844
1857
  describeCronExpression: () => describeCronExpression,
1858
+ disconnectAllChannels: () => disconnectAllChannels,
1845
1859
  documentLearningPlugin: () => documentLearningPlugin,
1846
1860
  documentParserPlugin: () => documentParserPlugin,
1847
1861
  embeddingsLatticeManager: () => embeddingsLatticeManager,
1862
+ encodeInput: () => encodeInput,
1848
1863
  encrypt: () => encrypt,
1849
1864
  encryptConfigFields: () => encryptConfigFields,
1850
1865
  ensureBuiltinAgentsForTenant: () => ensureBuiltinAgentsForTenant,
@@ -1888,6 +1903,7 @@ __export(index_exports, {
1888
1903
  getModelLattice: () => getModelLattice,
1889
1904
  getNextCronTime: () => getNextCronTime,
1890
1905
  getOrCreateCollectionVectorStore: () => getOrCreateCollectionVectorStore,
1906
+ getPluginDeploymentService: () => getPluginDeploymentService,
1891
1907
  getPluginSkillManifestVersion: () => getPluginSkillManifestVersion,
1892
1908
  getQueueLattice: () => getQueueLattice,
1893
1909
  getReplicaId: () => getReplicaId,
@@ -1916,6 +1932,8 @@ __export(index_exports, {
1916
1932
  hasChunkBuffer: () => hasChunkBuffer,
1917
1933
  hasDelegatedWorkerToken: () => hasDelegatedWorkerToken,
1918
1934
  hasRuntimeCapabilityPolicy: () => hasRuntimeCapabilityPolicy,
1935
+ installPluginHost: () => installPluginHost,
1936
+ invokeResident: () => invokeResident,
1919
1937
  invokeWithRetry: () => invokeWithRetry,
1920
1938
  isBuiltInSkill: () => isBuiltInSkill,
1921
1939
  isDirectRoom: () => isDirectRoom,
@@ -1929,6 +1947,7 @@ __export(index_exports, {
1929
1947
  jsonSchemaToZod: () => jsonSchemaToZod,
1930
1948
  listCollectionEntries: () => listCollectionEntries,
1931
1949
  listSandboxProviderTypes: () => listSandboxProviderTypes,
1950
+ listTenantPlugins: () => listTenantPlugins,
1932
1951
  loggerLatticeManager: () => loggerLatticeManager,
1933
1952
  mcpManager: () => mcpManager,
1934
1953
  mergeCapabilityBundles: () => mergeCapabilityBundles,
@@ -1942,6 +1961,8 @@ __export(index_exports, {
1942
1961
  parallelLimit: () => parallelLimit,
1943
1962
  parseCronExpression: () => parseCronExpression,
1944
1963
  parseDirectRoomType: () => parseDirectRoomType,
1964
+ parseHandlerEnvelope: () => parseHandlerEnvelope,
1965
+ parseHostArgs: () => parseHostArgs,
1945
1966
  parseJudgeVerdict: () => parseJudgeVerdict,
1946
1967
  parsePackSize: () => parsePackSize,
1947
1968
  parseQueuedExecutionMode: () => import_protocols2.parseQueuedExecutionMode,
@@ -1950,6 +1971,7 @@ __export(index_exports, {
1950
1971
  parseTrustedRunContext: () => import_protocols2.parseTrustedRunContext,
1951
1972
  parseYaml: () => parseYaml,
1952
1973
  performStringReplacement: () => performStringReplacement,
1974
+ pluginHostContentHash: () => pluginHostContentHash,
1953
1975
  postProcessTaskMutationResponse: () => postProcessTaskMutationResponse,
1954
1976
  prepareTaskDispatch: () => prepareTaskDispatch,
1955
1977
  projectMutationMutex: () => projectMutationMutex,
@@ -1988,6 +2010,7 @@ __export(index_exports, {
1988
2010
  renderTemplate: () => renderTemplate,
1989
2011
  resetReplicaIdForTests: () => resetReplicaIdForTests,
1990
2012
  resolveEnabledPluginSkill: () => resolveEnabledPluginSkill,
2013
+ resolveHandlerTimeoutMs: () => resolveHandlerTimeoutMs,
1991
2014
  resolveJudgeModelKey: () => resolveJudgeModelKey,
1992
2015
  resolvePath: () => resolvePath,
1993
2016
  resolvePluginConnections: () => resolvePluginConnections,
@@ -2007,6 +2030,7 @@ __export(index_exports, {
2007
2030
  setBindingRegistry: () => setBindingRegistry,
2008
2031
  setEvalRunService: () => setEvalRunService,
2009
2032
  setMenuRegistry: () => setMenuRegistry,
2033
+ setPluginDeploymentService: () => setPluginDeploymentService,
2010
2034
  setSandboxPluginCatalog: () => setSandboxPluginCatalog,
2011
2035
  skillLatticeManager: () => skillLatticeManager,
2012
2036
  sqlDatabaseManager: () => sqlDatabaseManager,
@@ -3704,6 +3728,9 @@ var InMemoryProjectStore = class {
3704
3728
  workspaceId,
3705
3729
  name: data.name,
3706
3730
  description: data.description,
3731
+ // Persist `kind` like the Local/PostgreSQL stores so `public`/`personal`/
3732
+ // `training` project semantics survive in in-memory deployments.
3733
+ ...data.kind === void 0 ? {} : { kind: data.kind },
3707
3734
  config: data.config === void 0 && Object.keys(capabilityBundleIds).length === 0 ? void 0 : { ...data.config ?? {}, ...capabilityBundleIds },
3708
3735
  createdAt: existing?.createdAt ?? now,
3709
3736
  updatedAt: now
@@ -5323,6 +5350,93 @@ var InMemoryOutboxStore = class {
5323
5350
  }
5324
5351
  };
5325
5352
 
5353
+ // src/store_lattice/InMemoryPluginDeploymentStore.ts
5354
+ var InMemoryPluginDeploymentStore = class {
5355
+ constructor() {
5356
+ this.entries = /* @__PURE__ */ new Map();
5357
+ }
5358
+ /**
5359
+ * Lists every deployment for a tenant.
5360
+ *
5361
+ * @param tenantId - Tenant whose deployments are listed
5362
+ * @returns Defensive copies of the tenant's deployments
5363
+ */
5364
+ async listByTenant(tenantId2) {
5365
+ return [...this.entries.values()].filter((entry) => entry.tenantId === tenantId2).map((entry) => structuredClone(entry));
5366
+ }
5367
+ /**
5368
+ * Returns the deployment for a tenant + plugin type.
5369
+ *
5370
+ * @param tenantId - Owning tenant
5371
+ * @param pluginType - Plugin type key
5372
+ * @returns Defensive copy of the deployment, or undefined when absent
5373
+ */
5374
+ async get(tenantId2, pluginType) {
5375
+ const entry = this.entries.get(entryKey2(tenantId2, pluginType));
5376
+ return entry ? structuredClone(entry) : void 0;
5377
+ }
5378
+ /**
5379
+ * Inserts or replaces a deployment keyed by tenant + plugin type.
5380
+ *
5381
+ * On conflict the existing `id` and `createdAt` are preserved while every
5382
+ * other field (including `updatedAt`, when supplied) is updated — mirroring
5383
+ * the PostgreSQL store's `ON CONFLICT` semantics.
5384
+ *
5385
+ * @param deployment - Deployment record to persist
5386
+ * @returns Defensive copy of the stored deployment
5387
+ */
5388
+ async upsert(deployment) {
5389
+ const key5 = entryKey2(deployment.tenantId, deployment.pluginType);
5390
+ const existing = this.entries.get(key5);
5391
+ const next = {
5392
+ ...structuredClone(deployment),
5393
+ id: existing?.id ?? deployment.id,
5394
+ createdAt: existing?.createdAt ?? deployment.createdAt,
5395
+ updatedAt: deployment.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
5396
+ };
5397
+ this.entries.set(key5, next);
5398
+ return structuredClone(next);
5399
+ }
5400
+ /**
5401
+ * Applies a partial update to a deployment.
5402
+ *
5403
+ * `patch.createdAt` is ignored (immutable) and `updatedAt` is taken from the
5404
+ * patch when supplied, otherwise set to the current time — matching the
5405
+ * PostgreSQL store.
5406
+ *
5407
+ * @param tenantId - Owning tenant
5408
+ * @param pluginType - Plugin type key
5409
+ * @param patch - Mutable fields to apply
5410
+ * @returns Defensive copy of the updated deployment, or undefined when absent
5411
+ */
5412
+ async updateStatus(tenantId2, pluginType, patch) {
5413
+ const key5 = entryKey2(tenantId2, pluginType);
5414
+ const existing = this.entries.get(key5);
5415
+ if (!existing) return void 0;
5416
+ const next = {
5417
+ ...existing,
5418
+ ...patch,
5419
+ createdAt: existing.createdAt,
5420
+ updatedAt: patch.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
5421
+ };
5422
+ this.entries.set(key5, next);
5423
+ return structuredClone(next);
5424
+ }
5425
+ /**
5426
+ * Deletes a deployment by tenant + plugin type.
5427
+ *
5428
+ * @param tenantId - Owning tenant
5429
+ * @param pluginType - Plugin type key
5430
+ * @returns Whether a record was removed
5431
+ */
5432
+ async delete(tenantId2, pluginType) {
5433
+ return this.entries.delete(entryKey2(tenantId2, pluginType));
5434
+ }
5435
+ };
5436
+ function entryKey2(tenantId2, pluginType) {
5437
+ return JSON.stringify([tenantId2, pluginType]);
5438
+ }
5439
+
5326
5440
  // src/store_lattice/InMemoryUserStore.ts
5327
5441
  var InMemoryUserStore = class {
5328
5442
  constructor() {
@@ -6985,6 +7099,7 @@ var defaultProjectRoomReadStateStore = new InMemoryProjectRoomReadStateStore();
6985
7099
  var defaultUserPushSubscriptionStore = new InMemoryUserPushSubscriptionStore();
6986
7100
  var defaultLeaseStore = new InMemoryLeaseStore();
6987
7101
  var defaultOutboxStore = new InMemoryOutboxStore();
7102
+ var defaultPluginDeploymentStore = new InMemoryPluginDeploymentStore();
6988
7103
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
6989
7104
  storeLatticeManager.registerLattice(
6990
7105
  "default",
@@ -7008,6 +7123,7 @@ storeLatticeManager.registerLattice("default", "projectRoomReadState", defaultPr
7008
7123
  storeLatticeManager.registerLattice("default", "userPushSubscription", defaultUserPushSubscriptionStore);
7009
7124
  storeLatticeManager.registerLattice("default", "lease", defaultLeaseStore);
7010
7125
  storeLatticeManager.registerLattice("default", "outbox", defaultOutboxStore);
7126
+ storeLatticeManager.registerLattice("default", "pluginDeployment", defaultPluginDeploymentStore);
7011
7127
  var defaultUserStore = new InMemoryUserStore();
7012
7128
  var defaultTenantStore = new InMemoryTenantStore();
7013
7129
  var defaultUserTenantLinkStore = new InMemoryUserTenantLinkStore();
@@ -15067,6 +15183,82 @@ var evalPlugin = {
15067
15183
  })
15068
15184
  };
15069
15185
 
15186
+ // src/sandbox_plugin/residentTransport.ts
15187
+ var DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS = 6e4;
15188
+ function isRecord3(value) {
15189
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15190
+ }
15191
+ function isEnvelope(value) {
15192
+ return isRecord3(value) && typeof value.ok === "boolean";
15193
+ }
15194
+ function errorFromBody(value) {
15195
+ if (isRecord3(value) && typeof value.error === "string" && value.error.length > 0) {
15196
+ return value.error;
15197
+ }
15198
+ return void 0;
15199
+ }
15200
+ async function readJson(response) {
15201
+ try {
15202
+ return await response.json();
15203
+ } catch {
15204
+ return void 0;
15205
+ }
15206
+ }
15207
+ async function invokeResident(args) {
15208
+ const {
15209
+ baseUrl,
15210
+ endpointPath,
15211
+ serviceKey,
15212
+ payload,
15213
+ timeoutMs = DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS
15214
+ } = args;
15215
+ const fetchImpl = args.fetchImpl ?? fetch;
15216
+ const url = `${baseUrl.replace(/\/$/, "")}${endpointPath}/invoke`;
15217
+ const headers = {
15218
+ "Content-Type": "application/json"
15219
+ };
15220
+ if (serviceKey) {
15221
+ headers["Authorization"] = `Bearer ${serviceKey}`;
15222
+ }
15223
+ const controller = new AbortController();
15224
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
15225
+ try {
15226
+ const response = await fetchImpl(url, {
15227
+ method: "POST",
15228
+ headers,
15229
+ body: JSON.stringify({ ...payload, timeoutMs: payload.timeoutMs ?? timeoutMs }),
15230
+ signal: controller.signal
15231
+ });
15232
+ const parsed = await readJson(response);
15233
+ if (!response.ok) {
15234
+ return {
15235
+ ok: false,
15236
+ error: errorFromBody(parsed) ?? `resident service responded with HTTP ${response.status}`,
15237
+ status: response.status
15238
+ };
15239
+ }
15240
+ if (!isEnvelope(parsed)) {
15241
+ return {
15242
+ ok: false,
15243
+ error: "resident service returned an invalid envelope",
15244
+ status: response.status
15245
+ };
15246
+ }
15247
+ if (!parsed.ok) {
15248
+ return {
15249
+ ok: false,
15250
+ error: errorFromBody(parsed) ?? "resident handler reported failure",
15251
+ status: response.status
15252
+ };
15253
+ }
15254
+ return { ok: true, result: parsed.result, status: response.status };
15255
+ } catch (error) {
15256
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
15257
+ } finally {
15258
+ clearTimeout(timer);
15259
+ }
15260
+ }
15261
+
15070
15262
  // src/sandbox_plugin/SandboxPluginExecutor.ts
15071
15263
  var PLUGINS_ROOT = "/root/.agents/plugins";
15072
15264
  var INLINE_INPUT_LIMIT_BYTES = 48 * 1024;
@@ -15079,11 +15271,11 @@ var MAX_MAX_RESULT_BYTES = 1048576;
15079
15271
  function encode(value) {
15080
15272
  return Buffer.from(JSON.stringify(value ?? null), "utf8").toString("base64");
15081
15273
  }
15082
- function isRecord3(value) {
15274
+ function isRecord4(value) {
15083
15275
  return typeof value === "object" && value !== null && !Array.isArray(value);
15084
15276
  }
15085
- function isEnvelope(value) {
15086
- return isRecord3(value) && typeof value.ok === "boolean";
15277
+ function isEnvelope2(value) {
15278
+ return isRecord4(value) && typeof value.ok === "boolean";
15087
15279
  }
15088
15280
  function clamp(value, min, max) {
15089
15281
  return Math.min(Math.max(value, min), max);
@@ -15104,7 +15296,7 @@ function parseEnvelope(stdout) {
15104
15296
  const trimmed = stdout.trim();
15105
15297
  try {
15106
15298
  const parsed = JSON.parse(trimmed || "null");
15107
- if (isEnvelope(parsed)) return parsed;
15299
+ if (isEnvelope2(parsed)) return parsed;
15108
15300
  } catch {
15109
15301
  }
15110
15302
  const lines = stdout.split("\n");
@@ -15113,7 +15305,7 @@ function parseEnvelope(stdout) {
15113
15305
  if (!line) continue;
15114
15306
  try {
15115
15307
  const parsed = JSON.parse(line);
15116
- if (isEnvelope(parsed)) return parsed;
15308
+ if (isEnvelope2(parsed)) return parsed;
15117
15309
  } catch {
15118
15310
  }
15119
15311
  }
@@ -15126,26 +15318,258 @@ function truncate(value, cap) {
15126
15318
  while (end > 0 && (buf[end] & 192) === 128) end -= 1;
15127
15319
  return buf.subarray(0, end).toString("utf8") + "\n...[truncated]";
15128
15320
  }
15321
+ function capResult(result, cap) {
15322
+ const serialized = typeof result === "string" ? result : JSON.stringify(result ?? null);
15323
+ return Buffer.byteLength(serialized, "utf8") <= cap ? result : truncate(serialized, cap);
15324
+ }
15129
15325
  function errorFromStdout(stdout) {
15130
15326
  let text = stdout;
15131
15327
  try {
15132
15328
  const parsed = JSON.parse(stdout);
15133
- if (isRecord3(parsed)) {
15329
+ if (isRecord4(parsed)) {
15134
15330
  text = JSON.stringify(sanitize(parsed)) ?? stdout;
15135
15331
  }
15136
15332
  } catch {
15137
15333
  }
15138
15334
  return text.slice(0, 4096);
15139
15335
  }
15336
+ function buildResidentPayload(args, timeoutMs) {
15337
+ return {
15338
+ handler: args.handler,
15339
+ input: args.input,
15340
+ ...args.connection === void 0 ? {} : { connection: args.connection },
15341
+ ...args.connections === void 0 ? {} : { connections: args.connections },
15342
+ context: args.context,
15343
+ timeoutMs
15344
+ };
15345
+ }
15140
15346
  var SandboxPluginExecutor = class {
15141
15347
  /**
15142
- * @param deps - Sandbox resolver and optional logger
15348
+ * @param deps - Sandbox resolver, optional logger, and optional resident routing
15143
15349
  */
15144
15350
  constructor(deps) {
15145
15351
  this.deps = deps;
15146
15352
  }
15147
15353
  /**
15148
- * Invoke a plugin handler inside the tenant sandbox shell.
15354
+ * Invoke a plugin handler.
15355
+ *
15356
+ * Routes to the published plugin's resident service when {@link
15357
+ * SandboxPluginExecutorDeps.resident} is configured and a deployment is
15358
+ * routable (`running` with an endpoint, or a resumed `onDemand` deployment).
15359
+ * Otherwise, or when the resident call fails and degradation is enabled, runs
15360
+ * the handler as a short `node <handler>` process inside the tenant sandbox.
15361
+ *
15362
+ * @param args - Invocation arguments including manifest, handler, and input
15363
+ * @returns The normalized handler result, exit code, timing, and raw stdout
15364
+ * @throws When `args.handler` is not a safe relative path
15365
+ */
15366
+ async invoke(args) {
15367
+ const started = Date.now();
15368
+ validateRelativePath(args.handler, "handler");
15369
+ if (this.deps.resident) {
15370
+ const resident = await this.tryResident(args, this.deps.resident, started);
15371
+ if (resident) return resident;
15372
+ }
15373
+ return this.invokeShortProcess(args, started);
15374
+ }
15375
+ /**
15376
+ * Attempt to route an invocation to a published plugin's resident service.
15377
+ *
15378
+ * @param args - Invocation arguments
15379
+ * @param resident - Resident routing dependencies
15380
+ * @param started - Invocation start timestamp used for `durationMs`
15381
+ * @returns A terminal result (success, or failure with degradation disabled),
15382
+ * or `undefined` when the caller should fall back to the short-process path
15383
+ */
15384
+ async tryResident(args, resident, started) {
15385
+ const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
15386
+ const pluginType = args.manifest.type;
15387
+ if (!tenantId2) return void 0;
15388
+ const deployment = await this.lookupDeployment(resident, tenantId2, pluginType);
15389
+ if (!deployment) return void 0;
15390
+ const timeoutMs = clamp(args.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
15391
+ const payload = buildResidentPayload(args, timeoutMs);
15392
+ if (deployment.status === "running" && deployment.endpoint) {
15393
+ let outcome = await this.callResident(resident, deployment.endpoint, payload, timeoutMs);
15394
+ if (!outcome.ok && deployment.mode === "onDemand" && resident.ensureRunning) {
15395
+ outcome = await this.ensureRunningAndRetry(
15396
+ resident,
15397
+ tenantId2,
15398
+ pluginType,
15399
+ deployment.endpoint,
15400
+ payload,
15401
+ timeoutMs,
15402
+ outcome
15403
+ );
15404
+ }
15405
+ return this.finalizeResident(outcome, args, resident, pluginType, tenantId2, started);
15406
+ }
15407
+ if (deployment.mode === "onDemand" && resident.ensureRunning) {
15408
+ const outcome = await this.wakeOnDemand(resident, tenantId2, pluginType, payload, timeoutMs);
15409
+ if (outcome) {
15410
+ return this.finalizeResident(outcome, args, resident, pluginType, tenantId2, started);
15411
+ }
15412
+ }
15413
+ return void 0;
15414
+ }
15415
+ /**
15416
+ * Normalize a resident invocation outcome into the caller-facing result.
15417
+ *
15418
+ * @param outcome - Normalized resident transport result
15419
+ * @param args - Invocation arguments (for the result-size cap)
15420
+ * @param resident - Resident routing dependencies (degradation policy)
15421
+ * @param pluginType - Plugin type, for diagnostics
15422
+ * @param tenantId - Tenant owning the plugin, for diagnostics
15423
+ * @param started - Invocation start timestamp used for `durationMs`
15424
+ * @returns The normalized result, or `undefined` when the caller should fall
15425
+ * back to the short-process path
15426
+ */
15427
+ finalizeResident(outcome, args, resident, pluginType, tenantId2, started) {
15428
+ if (outcome.ok) {
15429
+ const cap = clamp(
15430
+ args.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES,
15431
+ MIN_MAX_RESULT_BYTES,
15432
+ MAX_MAX_RESULT_BYTES
15433
+ );
15434
+ return {
15435
+ ok: true,
15436
+ result: capResult(outcome.result, cap),
15437
+ exitCode: 0,
15438
+ httpStatus: outcome.status,
15439
+ durationMs: Date.now() - started,
15440
+ stdout: "",
15441
+ stderr: ""
15442
+ };
15443
+ }
15444
+ const error = outcome.error ?? "resident plugin invocation failed";
15445
+ if (resident.degradeToShortProcess ?? true) {
15446
+ this.deps.logger?.info(
15447
+ { event: "sandbox-plugin.resident.degrade", pluginType, tenantId: tenantId2, error },
15448
+ "resident plugin invocation failed; falling back to short process"
15449
+ );
15450
+ return void 0;
15451
+ }
15452
+ return {
15453
+ ok: false,
15454
+ error,
15455
+ exitCode: 1,
15456
+ httpStatus: outcome.status,
15457
+ durationMs: Date.now() - started,
15458
+ stdout: "",
15459
+ stderr: ""
15460
+ };
15461
+ }
15462
+ /**
15463
+ * Look up a deployment, degrading to `undefined` when the lookup throws.
15464
+ *
15465
+ * @param resident - Resident routing dependencies
15466
+ * @param tenantId - Tenant owning the plugin
15467
+ * @param pluginType - Plugin type addressed by the invocation
15468
+ * @returns The deployment, or `undefined` when absent or lookup failed
15469
+ */
15470
+ async lookupDeployment(resident, tenantId2, pluginType) {
15471
+ try {
15472
+ return await resident.getDeployment(tenantId2, pluginType);
15473
+ } catch (error) {
15474
+ this.deps.logger?.info(
15475
+ {
15476
+ event: "sandbox-plugin.resident.lookup.failed",
15477
+ pluginType,
15478
+ tenantId: tenantId2,
15479
+ error: error instanceof Error ? error.message : String(error)
15480
+ },
15481
+ "resident deployment lookup failed; falling back to short process"
15482
+ );
15483
+ return void 0;
15484
+ }
15485
+ }
15486
+ /**
15487
+ * Resume a non-running `onDemand` deployment once, then invoke it.
15488
+ *
15489
+ * Re-reads the deployment after the resume so a cold-started endpoint is
15490
+ * picked up. Never throws: a failed resume or a deployment that is still not
15491
+ * `running` with an endpoint resolves to `undefined`, letting the caller use
15492
+ * the short-process path.
15493
+ *
15494
+ * @param resident - Resident routing dependencies
15495
+ * @param tenantId - Tenant owning the plugin
15496
+ * @param pluginType - Plugin type addressed by the invocation
15497
+ * @param payload - Invocation payload
15498
+ * @param timeoutMs - Clamped per-call timeout
15499
+ * @returns The resident outcome when routed, or `undefined` to short-process
15500
+ */
15501
+ async wakeOnDemand(resident, tenantId2, pluginType, payload, timeoutMs) {
15502
+ try {
15503
+ await resident.ensureRunning?.(tenantId2, pluginType);
15504
+ } catch (error) {
15505
+ this.deps.logger?.info(
15506
+ {
15507
+ event: "sandbox-plugin.resident.ensure.failed",
15508
+ pluginType,
15509
+ tenantId: tenantId2,
15510
+ error: error instanceof Error ? error.message : String(error)
15511
+ },
15512
+ "resident service resume failed; falling back to short process"
15513
+ );
15514
+ return void 0;
15515
+ }
15516
+ const refreshed = await this.lookupDeployment(resident, tenantId2, pluginType);
15517
+ if (!refreshed || refreshed.status !== "running" || !refreshed.endpoint) {
15518
+ return void 0;
15519
+ }
15520
+ return this.callResident(resident, refreshed.endpoint, payload, timeoutMs);
15521
+ }
15522
+ /**
15523
+ * Resume an `onDemand` deployment once and retry the resident call once.
15524
+ *
15525
+ * @param resident - Resident routing dependencies
15526
+ * @param tenantId - Tenant owning the plugin
15527
+ * @param pluginType - Plugin type addressed by the invocation
15528
+ * @param endpoint - Endpoint used by the first attempt
15529
+ * @param payload - Invocation payload
15530
+ * @param timeoutMs - Clamped per-call timeout
15531
+ * @param firstOutcome - Failed first attempt, returned when resume fails
15532
+ * @returns The retry outcome, or `firstOutcome` when resume itself failed
15533
+ */
15534
+ async ensureRunningAndRetry(resident, tenantId2, pluginType, endpoint, payload, timeoutMs, firstOutcome) {
15535
+ try {
15536
+ await resident.ensureRunning?.(tenantId2, pluginType);
15537
+ } catch (error) {
15538
+ this.deps.logger?.info(
15539
+ {
15540
+ event: "sandbox-plugin.resident.ensure.failed",
15541
+ pluginType,
15542
+ tenantId: tenantId2,
15543
+ error: error instanceof Error ? error.message : String(error)
15544
+ },
15545
+ "resident service resume failed"
15546
+ );
15547
+ return firstOutcome;
15548
+ }
15549
+ const refreshed = await this.lookupDeployment(resident, tenantId2, pluginType);
15550
+ return this.callResident(resident, refreshed?.endpoint ?? endpoint, payload, timeoutMs);
15551
+ }
15552
+ /**
15553
+ * Invoke the resident host over the sandbox-service proxy path.
15554
+ *
15555
+ * @param resident - Resident routing dependencies
15556
+ * @param endpointPath - Relative proxy path
15557
+ * @param payload - Invocation payload matching the host contract
15558
+ * @param timeoutMs - Clamped per-call timeout
15559
+ * @returns The normalized resident invocation result
15560
+ */
15561
+ callResident(resident, endpointPath, payload, timeoutMs) {
15562
+ return invokeResident({
15563
+ baseUrl: resident.baseUrl,
15564
+ endpointPath,
15565
+ serviceKey: resident.serviceKey,
15566
+ payload,
15567
+ timeoutMs,
15568
+ fetchImpl: resident.fetchImpl
15569
+ });
15570
+ }
15571
+ /**
15572
+ * Invoke a plugin handler as a short `node <handler>` process.
15149
15573
  *
15150
15574
  * Resolves the sandbox, injects small input as a base64 env var (or writes a
15151
15575
  * temp `input.json` for large input), writes connection credentials to a temp
@@ -15155,12 +15579,10 @@ var SandboxPluginExecutor = class {
15155
15579
  * in all cases, including setup failures.
15156
15580
  *
15157
15581
  * @param args - Invocation arguments including manifest, handler, and input
15582
+ * @param started - Invocation start timestamp used for `durationMs`
15158
15583
  * @returns The normalized handler result, exit code, timing, and raw stdout
15159
- * @throws When `args.handler` is not a safe relative path
15160
15584
  */
15161
- async invoke(args) {
15162
- const started = Date.now();
15163
- validateRelativePath(args.handler, "handler");
15585
+ async invokeShortProcess(args, started) {
15164
15586
  const pluginDir = `${PLUGINS_ROOT}/${args.manifest.type}`;
15165
15587
  const handlerAbs = `${pluginDir}/${args.handler}`;
15166
15588
  const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
@@ -15248,11 +15670,9 @@ var SandboxPluginExecutor = class {
15248
15670
  }
15249
15671
  const parsed = parseEnvelope(stdout);
15250
15672
  if (parsed) {
15251
- const result = parsed.ok ? parsed.result : void 0;
15252
- const serialized = typeof result === "string" ? result : JSON.stringify(result ?? null);
15253
15673
  return {
15254
15674
  ok: parsed.ok,
15255
- result: Buffer.byteLength(serialized, "utf8") <= cap ? result : truncate(serialized, cap),
15675
+ result: capResult(parsed.ok ? parsed.result : void 0, cap),
15256
15676
  error: typeof parsed.error === "string" ? parsed.error : void 0,
15257
15677
  exitCode,
15258
15678
  durationMs,
@@ -15263,7 +15683,7 @@ var SandboxPluginExecutor = class {
15263
15683
  const text = stdout.trim();
15264
15684
  return {
15265
15685
  ok: true,
15266
- result: truncate(text, cap),
15686
+ result: capResult(text, cap),
15267
15687
  exitCode,
15268
15688
  durationMs,
15269
15689
  stdout,
@@ -15280,24 +15700,33 @@ var SandboxPluginExecutor = class {
15280
15700
  }
15281
15701
  };
15282
15702
 
15703
+ // src/sandbox_plugin/pluginDeploymentServiceRuntime.ts
15704
+ var service2 = null;
15705
+ function setPluginDeploymentService(instance) {
15706
+ service2 = instance ?? null;
15707
+ }
15708
+ function getPluginDeploymentService() {
15709
+ return service2;
15710
+ }
15711
+
15283
15712
  // src/sandbox_plugin/TenantPluginCatalog.ts
15284
15713
  var import_node_crypto3 = require("crypto");
15285
15714
 
15286
15715
  // src/sandbox_plugin/jsonSchemaToZod.ts
15287
15716
  var import_zod45 = require("zod");
15288
- function isRecord4(value) {
15717
+ function isRecord5(value) {
15289
15718
  return typeof value === "object" && value !== null && !Array.isArray(value);
15290
15719
  }
15291
15720
  function withDescription(schema6, raw) {
15292
- const description = isRecord4(raw) ? raw.description : void 0;
15721
+ const description = isRecord5(raw) ? raw.description : void 0;
15293
15722
  return typeof description === "string" ? schema6.describe(description) : schema6;
15294
15723
  }
15295
15724
  function jsonSchemaToZod(schema6) {
15296
- if (!isRecord4(schema6)) throw new Error("schema must be an object");
15725
+ if (!isRecord5(schema6)) throw new Error("schema must be an object");
15297
15726
  const type = schema6.type;
15298
15727
  switch (type) {
15299
15728
  case "object": {
15300
- const props = isRecord4(schema6.properties) ? schema6.properties : {};
15729
+ const props = isRecord5(schema6.properties) ? schema6.properties : {};
15301
15730
  const required = Array.isArray(schema6.required) ? schema6.required.filter((x) => typeof x === "string") : [];
15302
15731
  const shape = {};
15303
15732
  for (const [key5, sub] of Object.entries(props)) {
@@ -15337,7 +15766,7 @@ var DISPLAY_MODES = ["inline", "fullscreen", "pip"];
15337
15766
  var MAX_MANIFEST_BYTES = 262144;
15338
15767
  var MAX_TOOLS = 32;
15339
15768
  var MAX_PLUGINS_PER_TENANT = 50;
15340
- function isRecord5(value) {
15769
+ function isRecord6(value) {
15341
15770
  return typeof value === "object" && value !== null && !Array.isArray(value);
15342
15771
  }
15343
15772
  function isSafeHandler(path8) {
@@ -15362,7 +15791,7 @@ function validateSandboxPluginManifest(args) {
15362
15791
  const diagnostics = [];
15363
15792
  const error = (code, message, pluginType, tool68) => diagnostics.push({ level: "error", code, message, pluginType, tool: tool68 });
15364
15793
  const raw = args.raw;
15365
- if (!isRecord5(raw)) {
15794
+ if (!isRecord6(raw)) {
15366
15795
  error("MANIFEST_NOT_OBJECT", "manifest must be a JSON object", args.dirName);
15367
15796
  return { tools: [], diagnostics };
15368
15797
  }
@@ -15415,7 +15844,7 @@ function validateSandboxPluginManifest(args) {
15415
15844
  const tools = [];
15416
15845
  const seenToolNames = /* @__PURE__ */ new Set();
15417
15846
  for (const candidate of toolCandidates.slice(0, MAX_TOOLS)) {
15418
- if (!isRecord5(candidate)) {
15847
+ if (!isRecord6(candidate)) {
15419
15848
  error("TOOL_INVALID", "tool must be an object", type);
15420
15849
  continue;
15421
15850
  }
@@ -15432,7 +15861,7 @@ function validateSandboxPluginManifest(args) {
15432
15861
  error("HANDLER_MISSING", `handler "${String(candidate.handler)}" not found`, type, name);
15433
15862
  continue;
15434
15863
  }
15435
- if (!isRecord5(candidate.schema)) {
15864
+ if (!isRecord6(candidate.schema)) {
15436
15865
  error("TOOL_SCHEMA", `tool "${name}" schema must be an object`, type, name);
15437
15866
  continue;
15438
15867
  }
@@ -15446,17 +15875,17 @@ function validateSandboxPluginManifest(args) {
15446
15875
  let ui;
15447
15876
  if (candidate.ui !== void 0) {
15448
15877
  const rawUi = candidate.ui;
15449
- if (!isRecord5(rawUi) || !isSafeUiPath(rawUi.resource) || !args.handlerExists(rawUi.resource)) {
15878
+ if (!isRecord6(rawUi) || !isSafeUiPath(rawUi.resource) || !args.handlerExists(rawUi.resource)) {
15450
15879
  error("TOOL_UI", `tool "${name}" ui.resource must be an existing .html file within the plugin dir`, type, name);
15451
15880
  } else {
15452
- const csp = isRecord5(rawUi.csp) ? {
15881
+ const csp = isRecord6(rawUi.csp) ? {
15453
15882
  connectDomains: Array.isArray(rawUi.csp.connectDomains) ? rawUi.csp.connectDomains.filter((d) => typeof d === "string") : void 0,
15454
15883
  resourceDomains: Array.isArray(rawUi.csp.resourceDomains) ? rawUi.csp.resourceDomains.filter((d) => typeof d === "string") : void 0,
15455
15884
  frameDomains: Array.isArray(rawUi.csp.frameDomains) ? rawUi.csp.frameDomains.filter((d) => typeof d === "string") : void 0,
15456
15885
  baseUriDomains: Array.isArray(rawUi.csp.baseUriDomains) ? rawUi.csp.baseUriDomains.filter((d) => typeof d === "string") : void 0
15457
15886
  } : void 0;
15458
15887
  const displayMode = DISPLAY_MODES.includes(rawUi.displayMode) ? rawUi.displayMode : void 0;
15459
- const permissions = isRecord5(rawUi.permissions) ? {
15888
+ const permissions = isRecord6(rawUi.permissions) ? {
15460
15889
  camera: rawUi.permissions.camera === true ? true : void 0,
15461
15890
  microphone: rawUi.permissions.microphone === true ? true : void 0,
15462
15891
  geolocation: rawUi.permissions.geolocation === true ? true : void 0,
@@ -15486,7 +15915,7 @@ function validateSandboxPluginManifest(args) {
15486
15915
  }
15487
15916
  const skills = {};
15488
15917
  if (raw.skills !== void 0) {
15489
- if (!isRecord5(raw.skills)) {
15918
+ if (!isRecord6(raw.skills)) {
15490
15919
  error("SKILLS_INVALID", "skills must be an object", type);
15491
15920
  } else {
15492
15921
  const prefix = `${type}-`;
@@ -15501,12 +15930,12 @@ function validateSandboxPluginManifest(args) {
15501
15930
  error("SKILL_NAME", `skill "${name}" must be a safe relative path prefixed with "${prefix}"`, type);
15502
15931
  continue;
15503
15932
  }
15504
- if (!isRecord5(definition) || typeof definition.version !== "string" || typeof definition.content !== "string") {
15933
+ if (!isRecord6(definition) || typeof definition.version !== "string" || typeof definition.content !== "string") {
15505
15934
  error("SKILL_INVALID", `skill "${name}" requires string version and content`, type);
15506
15935
  continue;
15507
15936
  }
15508
15937
  if (definition.resources !== void 0) {
15509
- if (!isRecord5(definition.resources)) {
15938
+ if (!isRecord6(definition.resources)) {
15510
15939
  error("SKILL_RESOURCES", `skill "${name}" resources must be an object`, type);
15511
15940
  continue;
15512
15941
  }
@@ -15518,7 +15947,7 @@ function validateSandboxPluginManifest(args) {
15518
15947
  resourcesOk = false;
15519
15948
  break;
15520
15949
  }
15521
- if (!isRecord5(resource) || typeof resource.content !== "string") {
15950
+ if (!isRecord6(resource) || typeof resource.content !== "string") {
15522
15951
  resourcesOk = false;
15523
15952
  break;
15524
15953
  }
@@ -15534,11 +15963,11 @@ function validateSandboxPluginManifest(args) {
15534
15963
  }
15535
15964
  const agents = {};
15536
15965
  if (raw.agents !== void 0) {
15537
- if (!isRecord5(raw.agents)) {
15966
+ if (!isRecord6(raw.agents)) {
15538
15967
  error("AGENTS_INVALID", "agents must be an object", type);
15539
15968
  } else {
15540
15969
  for (const [key5, config] of Object.entries(raw.agents)) {
15541
- if (!isRecord5(config)) {
15970
+ if (!isRecord6(config)) {
15542
15971
  error("AGENT_INVALID", `agent "${key5}" must be an object`, type);
15543
15972
  continue;
15544
15973
  }
@@ -15559,7 +15988,7 @@ function validateSandboxPluginManifest(args) {
15559
15988
  }
15560
15989
  }
15561
15990
  let connection;
15562
- if (isRecord5(raw.connection)) {
15991
+ if (isRecord6(raw.connection)) {
15563
15992
  const fields = raw.connection.fields;
15564
15993
  if (!Array.isArray(fields)) {
15565
15994
  error("CONNECTION_INVALID", "connection.fields must be an array", type);
@@ -15573,7 +16002,7 @@ function validateSandboxPluginManifest(args) {
15573
16002
  for (const key5 of ["test", "discover"]) {
15574
16003
  const entry = raw.connection[key5];
15575
16004
  if (entry === void 0) continue;
15576
- const handler = isRecord5(entry) ? entry.handler : void 0;
16005
+ const handler = isRecord6(entry) ? entry.handler : void 0;
15577
16006
  if (!isSafeHandler(handler) || !args.handlerExists(handler)) {
15578
16007
  error("CONNECTION_HANDLER", `connection.${key5} handler "${String(handler)}" not found`, type);
15579
16008
  continue;
@@ -15591,8 +16020,8 @@ function validateSandboxPluginManifest(args) {
15591
16020
  version: raw.version,
15592
16021
  icon: typeof raw.icon === "string" ? raw.icon : void 0,
15593
16022
  category: typeof raw.category === "string" ? raw.category : void 0,
15594
- configSchema: isRecord5(raw.configSchema) ? raw.configSchema : void 0,
15595
- defaultConfig: isRecord5(raw.defaultConfig) ? raw.defaultConfig : void 0,
16023
+ configSchema: isRecord6(raw.configSchema) ? raw.configSchema : void 0,
16024
+ defaultConfig: isRecord6(raw.defaultConfig) ? raw.defaultConfig : void 0,
15596
16025
  connection,
15597
16026
  tools,
15598
16027
  skills: Object.keys(skills).length > 0 ? skills : void 0,
@@ -15772,7 +16201,7 @@ var STANDARD_CONNECTION_DEFAULT_CONFIG = Object.freeze({
15772
16201
  connectAll: false
15773
16202
  });
15774
16203
  var EMPTY_DEFAULT_CONFIG = Object.freeze({});
15775
- function isRecord6(value) {
16204
+ function isRecord7(value) {
15776
16205
  return typeof value === "object" && value !== null && !Array.isArray(value);
15777
16206
  }
15778
16207
  function runConfigFrom(exeConfig) {
@@ -15862,11 +16291,11 @@ async function runConnectionHandler(executor, manifest, handler, config, tenantI
15862
16291
  return { ok: false, message: result.error ?? "connection test failed" };
15863
16292
  }
15864
16293
  const value = result.result;
15865
- if (isRecord6(value) && typeof value.ok === "boolean" && typeof value.message === "string") {
16294
+ if (isRecord7(value) && typeof value.ok === "boolean" && typeof value.message === "string") {
15866
16295
  return {
15867
16296
  ok: value.ok,
15868
16297
  message: value.message,
15869
- details: isRecord6(value.details) ? value.details : void 0
16298
+ details: isRecord7(value.details) ? value.details : void 0
15870
16299
  };
15871
16300
  }
15872
16301
  return { ok: false, message: result.error ?? "connection test returned an invalid result" };
@@ -16219,13 +16648,29 @@ var TenantPluginCatalog = class {
16219
16648
 
16220
16649
  // src/sandbox_plugin/sandboxPluginRuntime.ts
16221
16650
  var catalog = null;
16651
+ function buildResidentRouting() {
16652
+ const baseUrl = process.env.MICROSANDBOX_SERVICE_URL;
16653
+ if (!baseUrl) return void 0;
16654
+ return {
16655
+ getDeployment: async (tenantId2, pluginType) => await getPluginDeploymentService()?.getDeployment(tenantId2, pluginType) ?? void 0,
16656
+ ensureRunning: async (tenantId2, pluginType) => {
16657
+ await getPluginDeploymentService()?.ensureRunning(tenantId2, pluginType);
16658
+ },
16659
+ baseUrl,
16660
+ serviceKey: process.env.MICROSANDBOX_API_KEY
16661
+ };
16662
+ }
16663
+ function buildExecutor(resolveSandbox) {
16664
+ const resident = buildResidentRouting();
16665
+ return new SandboxPluginExecutor({ resolveSandbox, ...resident ? { resident } : {} });
16666
+ }
16222
16667
  function emptyCatalog() {
16223
16668
  const resolveSandbox = async () => {
16224
16669
  throw new Error("sandbox manager not configured");
16225
16670
  };
16226
16671
  return new TenantPluginCatalog({
16227
16672
  resolveSandbox,
16228
- executor: new SandboxPluginExecutor({ resolveSandbox }),
16673
+ executor: buildExecutor(resolveSandbox),
16229
16674
  reservedTypes: () => new Set(PluginRegistry.list())
16230
16675
  });
16231
16676
  }
@@ -16242,7 +16687,7 @@ function getSandboxPluginCatalog() {
16242
16687
  const resolveSandbox = createSandboxResolver((config) => manager.getSandboxFromConfig(config));
16243
16688
  catalog = new TenantPluginCatalog({
16244
16689
  resolveSandbox,
16245
- executor: new SandboxPluginExecutor({ resolveSandbox }),
16690
+ executor: buildExecutor(resolveSandbox),
16246
16691
  reservedTypes: () => new Set(PluginRegistry.list())
16247
16692
  });
16248
16693
  return catalog;
@@ -16251,6 +16696,22 @@ function setSandboxPluginCatalog(instance) {
16251
16696
  catalog = instance;
16252
16697
  }
16253
16698
 
16699
+ // src/plugin/listTenantPlugins.ts
16700
+ async function collectPluginSources(tenantId2) {
16701
+ const globalPlugins = PluginRegistry.list().map((type) => PluginRegistry.get(type)).filter((plugin) => Boolean(plugin));
16702
+ let tenantPlugins = [];
16703
+ try {
16704
+ tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2)).plugins;
16705
+ } catch {
16706
+ tenantPlugins = [];
16707
+ }
16708
+ return { globalPlugins, tenantPlugins };
16709
+ }
16710
+ async function listTenantPlugins(tenantId2) {
16711
+ const { globalPlugins, tenantPlugins } = await collectPluginSources(tenantId2);
16712
+ return mergePluginSources(globalPlugins, tenantPlugins);
16713
+ }
16714
+
16254
16715
  // src/agent_lattice/builders/taskMiddlewareValidation.ts
16255
16716
  var REQUIRED_TASK_TOOLS = ["manage_task", "task"];
16256
16717
  function validateTaskReviewMode(reviewMode) {
@@ -16373,23 +16834,23 @@ function toolName(tool68) {
16373
16834
  return typeof tool68.name === "string" ? tool68.name : void 0;
16374
16835
  }
16375
16836
  function schemaHasConnectionKey(schema6) {
16376
- const definition = isRecord7(schema6._def) ? schema6._def : void 0;
16837
+ const definition = isRecord8(schema6._def) ? schema6._def : void 0;
16377
16838
  const shape = definition?.shape;
16378
16839
  const fields = typeof shape === "function" ? shape() : shape;
16379
- return isRecord7(fields) && Object.prototype.hasOwnProperty.call(fields, "connectionKey");
16840
+ return isRecord8(fields) && Object.prototype.hasOwnProperty.call(fields, "connectionKey");
16380
16841
  }
16381
16842
  function hasConnectionKeySchema(tool68) {
16382
- if (!isRecord7(tool68) || !isRecord7(tool68.schema)) return false;
16843
+ if (!isRecord8(tool68) || !isRecord8(tool68.schema)) return false;
16383
16844
  if (schemaHasConnectionKey(tool68.schema)) return true;
16384
- const definition = isRecord7(tool68.schema._def) ? tool68.schema._def : void 0;
16845
+ const definition = isRecord8(tool68.schema._def) ? tool68.schema._def : void 0;
16385
16846
  if (Array.isArray(definition?.options) && definition.options.length > 0) {
16386
16847
  return definition.options.every(
16387
- (option) => isRecord7(option) && schemaHasConnectionKey(option)
16848
+ (option) => isRecord8(option) && schemaHasConnectionKey(option)
16388
16849
  );
16389
16850
  }
16390
16851
  return false;
16391
16852
  }
16392
- function isRecord7(value) {
16853
+ function isRecord8(value) {
16393
16854
  return typeof value === "object" && value !== null && !Array.isArray(value);
16394
16855
  }
16395
16856
  var CapabilityToolRegistry = class {
@@ -16481,17 +16942,17 @@ var CapabilityToolRegistry = class {
16481
16942
  // src/middlewares/capabilityGuardMiddleware.ts
16482
16943
  var import_messages4 = require("@langchain/core/messages");
16483
16944
  var import_langchain49 = require("langchain");
16484
- function isRecord8(value) {
16945
+ function isRecord9(value) {
16485
16946
  return typeof value === "object" && value !== null && !Array.isArray(value);
16486
16947
  }
16487
16948
  function capabilityRuntimeFromRequest(request) {
16488
- if (!isRecord8(request) || !isRecord8(request.runtime)) return void 0;
16949
+ if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
16489
16950
  const configurable = request.runtime.configurable;
16490
16951
  const context = request.runtime.context;
16491
- const runConfig = isRecord8(configurable) && isRecord8(configurable.runConfig) ? configurable.runConfig : isRecord8(context) && isRecord8(context.runConfig) ? context.runConfig : void 0;
16952
+ const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
16492
16953
  if (!runConfig) return void 0;
16493
16954
  const value = runConfig.capabilityRuntime;
16494
- if (!isRecord8(value) || typeof value.revision !== "string" || !isRecord8(value.middleware)) {
16955
+ if (!isRecord9(value) || typeof value.revision !== "string" || !isRecord9(value.middleware)) {
16495
16956
  return void 0;
16496
16957
  }
16497
16958
  return value;
@@ -16503,10 +16964,10 @@ function hasActiveRuntimePolicy(runtime) {
16503
16964
  return Object.keys(runtime.middleware).length > 0;
16504
16965
  }
16505
16966
  function tenantIdFromRequest(request) {
16506
- if (!isRecord8(request) || !isRecord8(request.runtime)) return void 0;
16967
+ if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
16507
16968
  const configurable = request.runtime.configurable;
16508
16969
  const context = request.runtime.context;
16509
- const runConfig = isRecord8(configurable) && isRecord8(configurable.runConfig) ? configurable.runConfig : isRecord8(context) && isRecord8(context.runConfig) ? context.runConfig : void 0;
16970
+ const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
16510
16971
  return runConfig && typeof runConfig.tenantId === "string" ? runConfig.tenantId : void 0;
16511
16972
  }
16512
16973
  async function standardConnectionAllowed(request, capabilityType, config) {
@@ -16514,8 +16975,8 @@ async function standardConnectionAllowed(request, capabilityType, config) {
16514
16975
  const hasConnectAll = Object.prototype.hasOwnProperty.call(config, "connectAll");
16515
16976
  if (!hasConnections && (!hasConnectAll || config.connectAll !== true)) return false;
16516
16977
  const tenantId2 = tenantIdFromRequest(request);
16517
- const resource = isRecord8(request.toolCall) && isRecord8(request.toolCall.args) ? request.toolCall.args.connectionKey : void 0;
16518
- const hasConnectionKey = isRecord8(request.toolCall) && isRecord8(request.toolCall.args) && Object.prototype.hasOwnProperty.call(
16978
+ const resource = isRecord9(request.toolCall) && isRecord9(request.toolCall.args) ? request.toolCall.args.connectionKey : void 0;
16979
+ const hasConnectionKey = isRecord9(request.toolCall) && isRecord9(request.toolCall.args) && Object.prototype.hasOwnProperty.call(
16519
16980
  request.toolCall.args,
16520
16981
  "connectionKey"
16521
16982
  );
@@ -16581,10 +17042,10 @@ function createCapabilityGuardMiddleware(registry4) {
16581
17042
  toolName: toolName2
16582
17043
  });
16583
17044
  }
16584
- const standardConfig = isRecord8(policy.config) ? policy.config : {};
17045
+ const standardConfig = isRecord9(policy.config) ? policy.config : {};
16585
17046
  if (registry4.isStandardConnectionTool(toolName2) && !await standardConnectionAllowed(request, metadata.capabilityType, standardConfig)) {
16586
17047
  const args2 = request.toolCall.args;
16587
- const resource = isRecord8(args2) && typeof args2.connectionKey === "string" ? args2.connectionKey : void 0;
17048
+ const resource = isRecord9(args2) && typeof args2.connectionKey === "string" ? args2.connectionKey : void 0;
16588
17049
  return denialMessage(request.toolCall, {
16589
17050
  code: "RESOURCE_NOT_ALLOWED",
16590
17051
  message: typeof resource === "string" ? `Resource '${resource}' is not allowed by the capability policy` : "Resource selector 'connectionKey' must be a permitted string",
@@ -16597,8 +17058,8 @@ function createCapabilityGuardMiddleware(registry4) {
16597
17058
  const selector = metadata.resourceSelector;
16598
17059
  const args = request.toolCall.args;
16599
17060
  if (selector) {
16600
- const resource = isRecord8(args) ? args[selector.argumentField] : void 0;
16601
- const config = isRecord8(policy.config) ? policy.config : {};
17061
+ const resource = isRecord9(args) ? args[selector.argumentField] : void 0;
17062
+ const config = isRecord9(policy.config) ? policy.config : {};
16602
17063
  const hasExplicitResources = Object.prototype.hasOwnProperty.call(config, selector.configField);
16603
17064
  const allowsAll = !hasExplicitResources && selector.allowAllField !== void 0 && config[selector.allowAllField] === true;
16604
17065
  const allowedResources = config[selector.configField];
@@ -16815,13 +17276,7 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
16815
17276
  ));
16816
17277
  }
16817
17278
  }
16818
- let tenantPlugins = [];
16819
- try {
16820
- tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2 ?? "default")).plugins;
16821
- } catch {
16822
- tenantPlugins = [];
16823
- }
16824
- const globalPlugins = PluginRegistry.list().map((type) => PluginRegistry.get(type)).filter((p) => Boolean(p));
17279
+ const { globalPlugins, tenantPlugins } = await collectPluginSources(tenantId2 ?? "default");
16825
17280
  const mergedPlugins = mergePluginSources(globalPlugins, tenantPlugins);
16826
17281
  const mergedByType = new Map(mergedPlugins.map((p) => [p.meta.type, p]));
16827
17282
  const enabledPluginTypes = new Set(
@@ -31317,10 +31772,8 @@ function getAvailableTools(tenantId2) {
31317
31772
  function getAvailableModels() {
31318
31773
  return modelLatticeManager.getAllByTenant("default");
31319
31774
  }
31320
- function getAvailableMiddlewareTypes() {
31321
- return PluginRegistry.list().filter(
31322
- (type) => typeof PluginRegistry.get(type)?.middleware === "function"
31323
- );
31775
+ async function getAvailableMiddlewareTypes(tenantId2) {
31776
+ return (await listTenantPlugins(tenantId2)).filter((plugin) => typeof plugin.middleware === "function").map((plugin) => plugin.meta.type);
31324
31777
  }
31325
31778
  function normalizeProjectScope(projectId) {
31326
31779
  return !projectId || projectId === "default" ? "default" : projectId;
@@ -31359,7 +31812,7 @@ function requireSkillDeclaration(skillLoaded) {
31359
31812
  }
31360
31813
  async function validateAgentReferences(input, tenantId2) {
31361
31814
  const availableTools = getAvailableTools(tenantId2).map((tool68) => tool68.key);
31362
- const availableMiddlewareTypes = getAvailableMiddlewareTypes();
31815
+ const availableMiddlewareTypes = await getAvailableMiddlewareTypes(tenantId2);
31363
31816
  const availableCustomMiddlewareKeys = CustomMiddlewareRegistry.list().filter(
31364
31817
  (key5) => CustomMiddlewareRegistry.get(key5) !== void 0
31365
31818
  );
@@ -32039,9 +32492,10 @@ registerToolLattice(
32039
32492
  description: "\u5217\u51FA\u5F53\u524D\u7CFB\u7EDF\u4E2D\u6240\u6709\u53EF\u7528\u7684\u4E2D\u95F4\u4EF6\u7C7B\u578B\uFF08Middlewares\uFF09\uFF0C\u5305\u62EC\u5185\u7F6E\u548C\u81EA\u5B9A\u4E49\u63D2\u4EF6\u3002\u8FD4\u56DE\u6BCF\u4E2A\u4E2D\u95F4\u4EF6\u7684 type\u3001name\u3001description\u3001tools \u6E05\u5355\uFF08\u652F\u6301 allowedTools \u8FC7\u6EE4\uFF09\u3001configSchema\uFF08\u914D\u7F6E\u9762\u677F\u9700\u8981\u54EA\u4E9B\u5B57\u6BB5\uFF09\u548C connectionSchema\uFF08\u662F\u5426\u652F\u6301\u8FDE\u63A5\u6D4B\u8BD5\u548C\u8D44\u6E90\u53D1\u73B0\uFF09\u3002\n\n\u4F7F\u7528\u573A\u666F\uFF1A\n1. \u5728\u521B\u5EFA agent \u524D\uFF0C\u5148\u8C03\u6B64\u5DE5\u5177\u4E86\u89E3\u6709\u54EA\u4E9B\u4E2D\u95F4\u4EF6\u53EF\u914D\u7F6E\n2. \u6839\u636E configSchema \u51B3\u5B9A\u9700\u8981\u63D0\u4F9B\u54EA\u4E9B\u914D\u7F6E\u5B57\u6BB5\uFF1B\u65B0\u8FDE\u63A5\u578B\u63D2\u4EF6\u4F7F\u7528 connections \u548C connectAll\uFF0C\u5185\u7F6E\u65E7\u63D2\u4EF6\u7684 selector \u5B57\u6BB5\u7531 core \u7684 legacy compatibility map \u5B9A\u4E49\n3. \u5982\u679C\u67D0\u4E2A\u4E2D\u95F4\u4EF6\u7684 connectionSchema \u5B58\u5728\uFF0C\u8BF4\u660E\u5B83\u662F\u8FDE\u63A5\u578B\u4E2D\u95F4\u4EF6\uFF0C\u9700\u8981\u518D\u8C03 list_connections \u83B7\u53D6\u53EF\u7528\u8FDE\u63A5\n4. \u7528\u8FD4\u56DE\u7684 type \u5B57\u6BB5\u6784\u5EFA middleware \u6570\u7EC4\u4F20\u7ED9 create_agent / update_agent",
32040
32493
  schema: import_zod50.default.object({})
32041
32494
  },
32042
- async () => {
32043
- const availableMiddlewareTypes = getAvailableMiddlewareTypes();
32044
- const metas = PluginRegistry.listMeta().filter((meta) => availableMiddlewareTypes.includes(meta.type));
32495
+ async (_input, exeConfig) => {
32496
+ const tenantId2 = getTenantId2(exeConfig);
32497
+ const plugins = await listTenantPlugins(tenantId2);
32498
+ const metas = plugins.filter((plugin) => typeof plugin.middleware === "function").map(serializePluginMeta);
32045
32499
  return JSON.stringify(metas);
32046
32500
  }
32047
32501
  );
@@ -32352,14 +32806,7 @@ function ensureBuiltinAgentsForTenant(tenantId2) {
32352
32806
 
32353
32807
  // src/agent_lattice/pluginAgents.ts
32354
32808
  async function ensurePluginAgentsForTenant(tenantId2) {
32355
- const globalPlugins = PluginRegistry.list().map((type) => PluginRegistry.get(type)).filter((p) => Boolean(p));
32356
- let tenantPlugins = [];
32357
- try {
32358
- tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2)).plugins;
32359
- } catch {
32360
- tenantPlugins = [];
32361
- }
32362
- for (const plugin of mergePluginSources(globalPlugins, tenantPlugins)) {
32809
+ for (const plugin of await listTenantPlugins(tenantId2)) {
32363
32810
  if (!plugin.agents) continue;
32364
32811
  for (const [key5, config] of Object.entries(plugin.agents)) {
32365
32812
  try {
@@ -32387,6 +32834,7 @@ function assistantToConfig(assistant) {
32387
32834
  prompt: typeof assistant.graphDefinition === "string" ? assistant.graphDefinition : JSON.stringify(assistant.graphDefinition)
32388
32835
  };
32389
32836
  Object.assign(config, graphDef);
32837
+ if (assistant.ownerUserId !== void 0) config.ownerUserId = assistant.ownerUserId;
32390
32838
  return config;
32391
32839
  }
32392
32840
  var _AgentLatticeManager = class _AgentLatticeManager extends BaseLatticeManager {
@@ -34784,6 +35232,9 @@ var MicrosandboxRemoteInstance = class {
34784
35232
  }
34785
35233
  return "unknown";
34786
35234
  }
35235
+ async getEndpoint(port) {
35236
+ return this.client.getEndpoint(this.name, port);
35237
+ }
34787
35238
  /**
34788
35239
  * Pack a sandbox folder into a sibling ZIP through the microsandbox shell.
34789
35240
  *
@@ -34850,6 +35301,19 @@ var MicrosandboxServiceClient = class {
34850
35301
  method: "GET"
34851
35302
  });
34852
35303
  }
35304
+ /**
35305
+ * Resolve the endpoint for a published guest port of a sandbox.
35306
+ *
35307
+ * @param name - Sandbox name
35308
+ * @param port - Guest port published via `ensureSandbox({ exposePorts })`
35309
+ * @returns The scheme-less endpoint string and optional headers
35310
+ */
35311
+ async getEndpoint(name, port) {
35312
+ return this.request(
35313
+ `/api/sandboxes/${encodeURIComponent(name)}/endpoints/${port}`,
35314
+ { method: "GET" }
35315
+ );
35316
+ }
34853
35317
  async readFile(sandboxName, path8) {
34854
35318
  return this.request("/api/files/read", {
34855
35319
  method: "POST",
@@ -35114,9 +35578,9 @@ var MicrosandboxRemoteProvider = class {
35114
35578
  }
35115
35579
  buildEnsureInput(config) {
35116
35580
  const defaultMicrosandboxRemoteConfig = getDefaultMicrosandboxRemoteConfig();
35117
- const image = this.config.image ?? defaultMicrosandboxRemoteConfig.image;
35581
+ const image = config.image ?? this.config.image ?? defaultMicrosandboxRemoteConfig.image;
35118
35582
  console.log(
35119
- `[MicrosandboxRemote] buildEnsureInput image=${image} from=${this.config.image ? "providerConfig" : "defaultConfig"}`
35583
+ `[MicrosandboxRemote] buildEnsureInput image=${image} from=${config.image ? "runConfig" : this.config.image ? "providerConfig" : "defaultConfig"}`
35120
35584
  );
35121
35585
  return {
35122
35586
  image,
@@ -35126,7 +35590,10 @@ var MicrosandboxRemoteProvider = class {
35126
35590
  ...this.config.env,
35127
35591
  ...buildSandboxMetadataEnv(config)
35128
35592
  },
35129
- volumes: config?.volumes ?? this.buildDefaultVolumes(config)
35593
+ volumes: config?.volumes ?? this.buildDefaultVolumes(config),
35594
+ entrypoint: config.entrypoint,
35595
+ timeoutSeconds: config.timeoutSeconds,
35596
+ exposePorts: config.exposePorts
35130
35597
  };
35131
35598
  }
35132
35599
  buildDefaultVolumes(config) {
@@ -36852,6 +37319,157 @@ async function connectAllChannels(getAdapter, options = {}) {
36852
37319
  }
36853
37320
  }
36854
37321
  }
37322
+ async function disconnectAllChannels(getAdapter) {
37323
+ const store = getStoreLattice("default", "channelInstallation").store;
37324
+ const installations = await store.getAllInstallations();
37325
+ console.log(`[disconnectAllChannels] total=${installations.length} enabled=${installations.filter((i) => i.enabled).length}`);
37326
+ for (const inst of installations) {
37327
+ if (!inst.enabled) continue;
37328
+ const adapter = getAdapter(inst.channel);
37329
+ if (!adapter?.disconnect) continue;
37330
+ try {
37331
+ await adapter.disconnect(inst.id);
37332
+ } catch (error) {
37333
+ console.error(`[disconnectAllChannels] disconnect failed: channel=${inst.channel} tenant=${inst.tenantId} id=${inst.id}`, error);
37334
+ }
37335
+ }
37336
+ }
37337
+
37338
+ // src/channel/ChannelConnectionLeader.ts
37339
+ var import_node_crypto8 = require("crypto");
37340
+ var LEASE_KEY = "leader:channel-connections";
37341
+ var ChannelConnectionLeader = class {
37342
+ constructor(deps) {
37343
+ this.holder = null;
37344
+ this.connected = false;
37345
+ this.attempted = false;
37346
+ this.inFlight = null;
37347
+ this.timer = null;
37348
+ this.running = false;
37349
+ this.stopped = false;
37350
+ this.deps = deps;
37351
+ this.ttlMs = deps.ttlMs ?? 6e4;
37352
+ this.retryMs = deps.retryMs ?? Math.max(5e3, Math.floor(this.ttlMs / 3));
37353
+ }
37354
+ /** Starts the ownership loop (immediate tick, then every `retryMs`). */
37355
+ start() {
37356
+ if (this.timer || this.stopped) return;
37357
+ void this.tickOnce();
37358
+ this.timer = setInterval(() => {
37359
+ void this.tickOnce();
37360
+ }, this.retryMs);
37361
+ this.timer.unref?.();
37362
+ }
37363
+ /**
37364
+ * Releases ownership (if held) and disconnects.
37365
+ *
37366
+ * @returns A promise that settles after teardown completes.
37367
+ * @remarks Terminal: a stopped leader does not restart.
37368
+ */
37369
+ async stop() {
37370
+ this.stopped = true;
37371
+ if (this.timer) {
37372
+ clearInterval(this.timer);
37373
+ this.timer = null;
37374
+ }
37375
+ if (this.inFlight) await this.inFlight.catch(() => void 0);
37376
+ if (this.holder) {
37377
+ const holder = this.holder;
37378
+ this.holder = null;
37379
+ await this.deps.leaseStore.release(LEASE_KEY, holder).catch((error) => {
37380
+ this.deps.log?.warn("Channel leader lease release failed", {
37381
+ error: error instanceof Error ? error.message : String(error)
37382
+ });
37383
+ });
37384
+ }
37385
+ await this.disconnectChannels();
37386
+ }
37387
+ /**
37388
+ * Runs one ownership/health step. Public for deterministic tests.
37389
+ *
37390
+ * @returns A promise that settles when this step completes.
37391
+ */
37392
+ async tickOnce() {
37393
+ if (this.running || this.stopped) return;
37394
+ this.running = true;
37395
+ const run = this.runTick();
37396
+ this.inFlight = run;
37397
+ try {
37398
+ await run;
37399
+ } finally {
37400
+ if (this.inFlight === run) this.inFlight = null;
37401
+ this.running = false;
37402
+ }
37403
+ }
37404
+ /**
37405
+ * Performs one acquire/renew/step decision.
37406
+ *
37407
+ * @remarks A transient lease-store error while renewing keeps the current
37408
+ * holder and connections intact (retry next tick); only a confirmed
37409
+ * `renewed === false` triggers teardown.
37410
+ */
37411
+ async runTick() {
37412
+ if (this.holder) {
37413
+ let renewed = false;
37414
+ let renewFailed = false;
37415
+ try {
37416
+ renewed = await this.deps.leaseStore.renew(LEASE_KEY, this.holder, this.ttlMs);
37417
+ } catch (error) {
37418
+ renewFailed = true;
37419
+ this.deps.log?.warn("Channel leader lease renew failed", {
37420
+ error: error instanceof Error ? error.message : String(error)
37421
+ });
37422
+ }
37423
+ if (renewFailed) return;
37424
+ if (renewed) {
37425
+ if (!this.connected) await this.connectChannels();
37426
+ return;
37427
+ }
37428
+ await this.loseLeadership();
37429
+ }
37430
+ await this.tryAcquire();
37431
+ }
37432
+ async tryAcquire() {
37433
+ const holder = `${this.deps.owner}#${(0, import_node_crypto8.randomUUID)()}`;
37434
+ const acquired = await this.deps.leaseStore.tryAcquire(LEASE_KEY, holder, this.ttlMs).catch((error) => {
37435
+ this.deps.log?.warn("Channel leader lease acquire failed", {
37436
+ error: error instanceof Error ? error.message : String(error)
37437
+ });
37438
+ return false;
37439
+ });
37440
+ if (!acquired) return;
37441
+ this.holder = holder;
37442
+ await this.connectChannels();
37443
+ }
37444
+ async loseLeadership() {
37445
+ this.holder = null;
37446
+ await this.disconnectChannels();
37447
+ }
37448
+ async connectChannels() {
37449
+ this.attempted = true;
37450
+ try {
37451
+ await this.deps.connect();
37452
+ this.connected = true;
37453
+ } catch (error) {
37454
+ this.connected = false;
37455
+ this.deps.log?.warn("Channel leader connect failed", {
37456
+ error: error instanceof Error ? error.message : String(error)
37457
+ });
37458
+ }
37459
+ }
37460
+ async disconnectChannels() {
37461
+ if (!this.attempted) return;
37462
+ this.attempted = false;
37463
+ this.connected = false;
37464
+ try {
37465
+ await this.deps.disconnect();
37466
+ } catch (error) {
37467
+ this.deps.log?.warn("Channel leader disconnect failed", {
37468
+ error: error instanceof Error ? error.message : String(error)
37469
+ });
37470
+ }
37471
+ }
37472
+ };
36855
37473
 
36856
37474
  // src/eval_lattice/LatticeEval.ts
36857
37475
  var import_messages7 = require("@langchain/core/messages");
@@ -38312,7 +38930,7 @@ function getCoordinationLeaseStore() {
38312
38930
  }
38313
38931
 
38314
38932
  // src/services/withLease.ts
38315
- var import_node_crypto8 = require("crypto");
38933
+ var import_node_crypto9 = require("crypto");
38316
38934
  var LeaseAcquisitionTimeoutError = class extends Error {
38317
38935
  /**
38318
38936
  * @param key Lease key that could not be acquired.
@@ -38337,7 +38955,7 @@ async function withLease(store, key5, ownerLabel, fn, options = {}) {
38337
38955
  const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
38338
38956
  const retryDelayMs = options.retryDelayMs ?? 50;
38339
38957
  const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
38340
- const holder = `${ownerLabel}#${(0, import_node_crypto8.randomUUID)()}`;
38958
+ const holder = `${ownerLabel}#${(0, import_node_crypto9.randomUUID)()}`;
38341
38959
  const deadline = Date.now() + acquireTimeoutMs;
38342
38960
  for (; ; ) {
38343
38961
  if (await store.tryAcquire(key5, holder, ttlMs)) break;
@@ -38815,11 +39433,11 @@ function snapshotDate(value) {
38815
39433
  }
38816
39434
 
38817
39435
  // src/services/TaskLifecycleService.ts
38818
- var import_node_crypto10 = require("crypto");
39436
+ var import_node_crypto11 = require("crypto");
38819
39437
  var import_protocols31 = require("@axiom-lattice/protocols");
38820
39438
 
38821
39439
  // src/middlewares/taskBelief.ts
38822
- var import_node_crypto9 = require("crypto");
39440
+ var import_node_crypto10 = require("crypto");
38823
39441
  function buildBeliefCompletionEventKey(taskId, result, impacts) {
38824
39442
  const canonical = JSON.stringify({
38825
39443
  beliefImpact: impacts.map((impact) => ({
@@ -38830,7 +39448,7 @@ function buildBeliefCompletionEventKey(taskId, result, impacts) {
38830
39448
  })),
38831
39449
  result: result.trim()
38832
39450
  });
38833
- return (0, import_node_crypto9.createHash)("sha256").update(canonical).digest("hex");
39451
+ return (0, import_node_crypto10.createHash)("sha256").update(canonical).digest("hex");
38834
39452
  }
38835
39453
  function buildCompletionEvidenceDetail(result, impacts, eventKey) {
38836
39454
  return { result, beliefImpact: impacts, eventKey };
@@ -39033,7 +39651,7 @@ function canonicalJson(value) {
39033
39651
  return JSON.stringify(value);
39034
39652
  }
39035
39653
  function sha256(value) {
39036
- return (0, import_node_crypto10.createHash)("sha256").update(canonicalJson(value)).digest("hex");
39654
+ return (0, import_node_crypto11.createHash)("sha256").update(canonicalJson(value)).digest("hex");
39037
39655
  }
39038
39656
  function sameEvidenceDetail(detail, taskId, result, impacts, eventKey, beliefOwner) {
39039
39657
  const storedOwner = detail.beliefOwner;
@@ -41111,7 +41729,7 @@ function createTaskLifecycleService(deps) {
41111
41729
  }
41112
41730
 
41113
41731
  // src/services/TaskExecutionService.ts
41114
- var import_node_crypto12 = require("crypto");
41732
+ var import_node_crypto13 = require("crypto");
41115
41733
  var import_node_util3 = require("util");
41116
41734
  var import_protocols32 = require("@axiom-lattice/protocols");
41117
41735
 
@@ -41434,13 +42052,13 @@ async function reconcileAllTenantTaskRuns(input) {
41434
42052
  }
41435
42053
 
41436
42054
  // src/services/taskRunLease.ts
41437
- var import_node_crypto11 = require("crypto");
42055
+ var import_node_crypto12 = require("crypto");
41438
42056
  function taskRunLeaseKey(taskId) {
41439
42057
  return `task-run:${taskId}`;
41440
42058
  }
41441
42059
  async function tryAcquireTaskRun(input) {
41442
42060
  const key5 = taskRunLeaseKey(input.taskId);
41443
- const holder = `${input.owner}#${(0, import_node_crypto11.randomUUID)()}`;
42061
+ const holder = `${input.owner}#${(0, import_node_crypto12.randomUUID)()}`;
41444
42062
  if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
41445
42063
  const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
41446
42064
  let released = false;
@@ -41807,11 +42425,11 @@ function createTaskExecutionService(deps) {
41807
42425
  let threadId;
41808
42426
  let runId;
41809
42427
  try {
41810
- threadId = storedThreadId ?? (deps.createThreadId ?? import_node_crypto12.randomUUID)();
42428
+ threadId = storedThreadId ?? (deps.createThreadId ?? import_node_crypto13.randomUUID)();
41811
42429
  if (task.status === "interrupted" && reuseCurrentRun) {
41812
42430
  runId = storedRunId;
41813
42431
  } else if (task.status === "interrupted" || task.status === "failed") {
41814
- runId = (deps.createRunId ?? import_node_crypto12.randomUUID)();
42432
+ runId = (deps.createRunId ?? import_node_crypto13.randomUUID)();
41815
42433
  }
41816
42434
  } catch (error) {
41817
42435
  return {
@@ -42070,11 +42688,11 @@ function createTaskExecutionService(deps) {
42070
42688
  }
42071
42689
 
42072
42690
  // src/services/leadership.ts
42073
- var import_node_crypto13 = require("crypto");
42691
+ var import_node_crypto14 = require("crypto");
42074
42692
  async function runAsLeader(store, name, fn, options = {}) {
42075
42693
  const key5 = `leader:${name}`;
42076
42694
  const owner2 = options.owner ?? getReplicaId();
42077
- const holder = `${owner2}#${(0, import_node_crypto13.randomUUID)()}`;
42695
+ const holder = `${owner2}#${(0, import_node_crypto14.randomUUID)()}`;
42078
42696
  const ttlMs = options.ttlMs ?? 6e4;
42079
42697
  if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
42080
42698
  const heartbeat = setInterval(() => {
@@ -43307,16 +43925,16 @@ function createAskUserToClarifyTool() {
43307
43925
  }
43308
43926
 
43309
43927
  // src/middlewares/askUserClarifyMiddleware.ts
43310
- function isRecord9(value) {
43928
+ function isRecord10(value) {
43311
43929
  return typeof value === "object" && value !== null && !Array.isArray(value);
43312
43930
  }
43313
43931
  function isProjectRoomRun(request) {
43314
- if (!isRecord9(request) || !isRecord9(request.runtime)) return false;
43932
+ if (!isRecord10(request) || !isRecord10(request.runtime)) return false;
43315
43933
  const runtime = request.runtime;
43316
- const configurable = isRecord9(runtime.configurable) ? runtime.configurable.runConfig : void 0;
43317
- const context = isRecord9(runtime.context) ? runtime.context.runConfig : void 0;
43318
- const runConfig = isRecord9(configurable) ? configurable : isRecord9(context) ? context : void 0;
43319
- return isRecord9(runConfig?.projectRoom);
43934
+ const configurable = isRecord10(runtime.configurable) ? runtime.configurable.runConfig : void 0;
43935
+ const context = isRecord10(runtime.context) ? runtime.context.runConfig : void 0;
43936
+ const runConfig = isRecord10(configurable) ? configurable : isRecord10(context) ? context : void 0;
43937
+ return isRecord10(runConfig?.projectRoom);
43320
43938
  }
43321
43939
  function createAskUserClarifyMiddleware() {
43322
43940
  return (0, import_langchain79.createMiddleware)({
@@ -44756,15 +45374,16 @@ function executorString(value) {
44756
45374
  }
44757
45375
  async function renderAvailableExecutors(rc, tenantId2) {
44758
45376
  const authority = classifyTaskAuthority(rc);
44759
- const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
44760
- const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
44761
- const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
45377
+ const trusted = authority.kind === "projectRoom" || authority.kind === "projectTask" ? authority.trusted : void 0;
45378
+ const useRoster = trusted !== void 0 && !trustedRosterExempt(trusted);
45379
+ const projectId = trusted ? (trusted.projectTask ?? trusted.projectRoom)?.projectId : void 0;
45380
+ const cacheKey = `${tenantId2}:${useRoster ? `project:${projectId ?? ""}` : "tenant"}`;
44762
45381
  const cached2 = executorCache.get(cacheKey);
44763
45382
  const now = Date.now();
44764
45383
  if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
44765
45384
  const lines = [];
44766
45385
  try {
44767
- if (governed && projectId) {
45386
+ if (useRoster && projectId) {
44768
45387
  const store = getStoreLattice("default", "projectBotMembership").store;
44769
45388
  if (typeof store.list === "function") {
44770
45389
  const bots = await store.list(tenantId2, projectId);
@@ -44779,11 +45398,15 @@ async function renderAvailableExecutors(rc, tenantId2) {
44779
45398
  }
44780
45399
  }
44781
45400
  } else {
45401
+ const exempt = trusted !== void 0 && trustedRosterExempt(trusted) ? trusted : void 0;
45402
+ const excluded = exempt ? await excludedRosterExecutors(exempt) : /* @__PURE__ */ new Set();
44782
45403
  const configs = await agentLatticeManager.getAllAgentConfigsByTenant(tenantId2);
44783
45404
  for (const config of configs.slice(0, 40)) {
44784
45405
  const row = config;
44785
45406
  const id = executorString(row.key);
44786
45407
  if (!id) continue;
45408
+ if (row.ownerUserId !== void 0) continue;
45409
+ if (excluded.has(id)) continue;
44787
45410
  const name = executorString(row.name);
44788
45411
  const description = executorString(row.description);
44789
45412
  lines.push(`- ${id}${name ? ` \u2014 ${name}` : ""}${description ? ` (${description.slice(0, 120)})` : ""}`);
@@ -44795,7 +45418,7 @@ async function renderAvailableExecutors(rc, tenantId2) {
44795
45418
  executorCache.set(cacheKey, { at: now, text: "" });
44796
45419
  return "";
44797
45420
  }
44798
- const header = governed ? "## Available task executors (Project roster)" : "## Available task executors";
45421
+ const header = useRoster ? "## Available task executors (Project roster)" : "## Available task executors";
44799
45422
  const text = `
44800
45423
 
44801
45424
  ${header}
@@ -44807,6 +45430,22 @@ ${lines.join("\n")}
44807
45430
  executorCache.set(cacheKey, { at: now, text });
44808
45431
  return text;
44809
45432
  }
45433
+ async function excludedRosterExecutors(trusted) {
45434
+ const excluded = /* @__PURE__ */ new Set();
45435
+ const context = trusted.projectTask ?? trusted.projectRoom;
45436
+ if (!context) return excluded;
45437
+ try {
45438
+ const store = getStoreLattice("default", "projectBotMembership").store;
45439
+ if (typeof store.list !== "function") return excluded;
45440
+ const rows = await store.list(context.tenantId, context.projectId);
45441
+ for (const row of Array.isArray(rows) ? rows : []) {
45442
+ const record = row;
45443
+ if (record.status !== "active" && typeof record.assistantId === "string") excluded.add(record.assistantId);
45444
+ }
45445
+ } catch {
45446
+ }
45447
+ return excluded;
45448
+ }
44810
45449
  function delegatedTaskScopeError(taskId) {
44811
45450
  return JSON.stringify({
44812
45451
  success: false,
@@ -44856,7 +45495,7 @@ function trustedRosterScope(trusted) {
44856
45495
  };
44857
45496
  }
44858
45497
  function trustedRosterExempt(trusted) {
44859
- return trusted.projectTask?.rosterExempt === true;
45498
+ return trusted.projectTask?.rosterExempt === true || trusted.projectRoom?.rosterExempt === true;
44860
45499
  }
44861
45500
  async function requireTrustedOwner(trusted, targetAssistantId) {
44862
45501
  const context = trusted.projectTask ?? trusted.projectRoom;
@@ -44872,6 +45511,17 @@ async function requireTrustedOwner(trusted, targetAssistantId) {
44872
45511
  targetAssistantId
44873
45512
  });
44874
45513
  }
45514
+ async function rosterExemptOwnerError(tenantId2, targetAssistantId) {
45515
+ try {
45516
+ const lattice = await agentLatticeManager.getOrLoadWithTenant(tenantId2, targetAssistantId);
45517
+ if (lattice && lattice.config.ownerUserId === void 0) return void 0;
45518
+ } catch {
45519
+ }
45520
+ return projectPolicyError(new ProjectTaskOwnerPolicyError(
45521
+ "PROJECT_TASK_OWNER_INACTIVE",
45522
+ "Target Project task owner is not a shared agent of this tenant."
45523
+ ));
45524
+ }
44875
45525
  function trustedTaskContextFailure(task, trusted) {
44876
45526
  if (task.tenantId === trusted.tenantId && task.workspaceId === trusted.workspaceId && task.projectId === trusted.projectId) return void 0;
44877
45527
  return JSON.stringify({
@@ -45476,6 +46126,12 @@ function createTaskMiddleware(options = {}) {
45476
46126
  "Target Project task owner is not active in the exact roster."
45477
46127
  ));
45478
46128
  }
46129
+ } else {
46130
+ const ownerError = await rosterExemptOwnerError(
46131
+ (trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
46132
+ ownerId
46133
+ );
46134
+ if (ownerError) return ownerError;
45479
46135
  }
45480
46136
  }
45481
46137
  if (!delegatedTaskId && (typeof effectiveOwnerId !== "string" || effectiveOwnerId.length === 0)) {
@@ -45537,6 +46193,12 @@ function createTaskMiddleware(options = {}) {
45537
46193
  } catch (error) {
45538
46194
  return projectPolicyError(error);
45539
46195
  }
46196
+ } else {
46197
+ const ownerError = await rosterExemptOwnerError(
46198
+ (trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
46199
+ ownerId
46200
+ );
46201
+ if (ownerError) return ownerError;
45540
46202
  }
45541
46203
  }
45542
46204
  return store.create({
@@ -46584,7 +47246,7 @@ function createTaskMiddleware(options = {}) {
46584
47246
  }
46585
47247
  const callerAssistantId = rc.assistant_id;
46586
47248
  const agentId = input.agentId === "general-purpose" && callerAssistantId ? `${callerAssistantId}-general-purpose` : input.agentId;
46587
- const service2 = createTaskExecutionService({
47249
+ const service3 = createTaskExecutionService({
46588
47250
  taskStore: getTaskStore(),
46589
47251
  workItemStore: getTaskWorkItemStore(),
46590
47252
  resolveAgent: async (targetTenantId, agentId2) => !!await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId2),
@@ -46609,7 +47271,7 @@ function createTaskMiddleware(options = {}) {
46609
47271
  }
46610
47272
  });
46611
47273
  const trustedCaller = authority.kind === "projectTask" || authority.kind === "projectRoom" ? authority.trusted.projectTask ?? authority.trusted.projectRoom : void 0;
46612
- return JSON.stringify(await service2.start({
47274
+ return JSON.stringify(await service3.start({
46613
47275
  tenantId: tenantId2,
46614
47276
  workspaceId: rc.workspaceId,
46615
47277
  projectId: rc.projectId,
@@ -48126,6 +48788,24 @@ function snapshotMembership3(value) {
48126
48788
  function snapshotRows(value) {
48127
48789
  return (0, import_protocols37.snapshotExactArray)(value) ?? [];
48128
48790
  }
48791
+ async function rosterExemptMembershipProjection(scope) {
48792
+ const excluded = /* @__PURE__ */ new Set();
48793
+ const roles = /* @__PURE__ */ new Map();
48794
+ try {
48795
+ const rows = snapshotRows(await getStoreLattice("default", "projectBotMembership").store.list(scope.tenantId, scope.projectId));
48796
+ for (const row of rows) {
48797
+ const member = snapshotMembership3(row);
48798
+ if (!member) continue;
48799
+ if (member.status !== "active") {
48800
+ excluded.add(member.assistantId);
48801
+ continue;
48802
+ }
48803
+ roles.set(member.assistantId, member.role);
48804
+ }
48805
+ } catch {
48806
+ }
48807
+ return { excluded, roles };
48808
+ }
48129
48809
  async function hasActiveCurrentMembership(scope) {
48130
48810
  const memberships = getStoreLattice("default", "projectBotMembership").store;
48131
48811
  const member = snapshotMembership3(await memberships.findById(scope.tenantId, scope.membershipId));
@@ -48142,11 +48822,11 @@ function createProjectRoomMiddleware() {
48142
48822
  if (!scope) return CONTEXT_REQUIRED;
48143
48823
  const digest = (0, import_crypto10.createHash)("sha256").update(text).digest("hex");
48144
48824
  try {
48145
- const service2 = new RoomAgentMessageService({
48825
+ const service3 = new RoomAgentMessageService({
48146
48826
  memberships: getStoreLattice("default", "projectBotMembership").store,
48147
48827
  messages: getStoreLattice("default", "projectRoomMessage").store
48148
48828
  });
48149
- await service2.post({
48829
+ await service3.post({
48150
48830
  tenantId: scope.tenantId,
48151
48831
  workspaceId: scope.workspaceId,
48152
48832
  projectId: scope.projectId,
@@ -48172,6 +48852,25 @@ function createProjectRoomMiddleware() {
48172
48852
  if (!scope) return CONTEXT_REQUIRED;
48173
48853
  try {
48174
48854
  if (!await hasActiveCurrentMembership(scope)) return CONTEXT_REQUIRED;
48855
+ if (scope.rosterExempt === true) {
48856
+ const { excluded, roles } = await rosterExemptMembershipProjection(scope);
48857
+ const configs = await agentLatticeManager.getAllAgentConfigsByTenant(scope.tenantId);
48858
+ const members2 = configs.filter((entry) => entry.ownerUserId === void 0).filter((entry) => {
48859
+ const key5 = entry.key;
48860
+ return typeof key5 === "string" && !excluded.has(key5);
48861
+ }).map((entry) => {
48862
+ const row = entry;
48863
+ const name = typeof row.name === "string" ? row.name : "";
48864
+ const assistantId = typeof row.key === "string" ? row.key : "";
48865
+ return {
48866
+ assistantId,
48867
+ mentionName: name,
48868
+ title: name,
48869
+ role: roles.get(assistantId) ?? "specialist"
48870
+ };
48871
+ }).filter((member) => member.assistantId !== "" && member.mentionName !== "").sort((left, right) => left.assistantId < right.assistantId ? -1 : left.assistantId > right.assistantId ? 1 : 0);
48872
+ return JSON.stringify({ success: true, members: members2 });
48873
+ }
48175
48874
  const rows = await getStoreLattice("default", "projectBotMembership").store.list(scope.tenantId, scope.projectId);
48176
48875
  const members = snapshotRows(rows).map(snapshotMembership3).filter((member) => member !== void 0 && member.status === "active" && member.tenantId === scope.tenantId && member.workspaceId === scope.workspaceId && member.projectId === scope.projectId && member.roomId === scope.roomId).sort((left, right) => left.joinedAt.getTime() - right.joinedAt.getTime() || (left.id < right.id ? -1 : left.id > right.id ? 1 : 0)).map((member) => ({
48177
48876
  assistantId: member.assistantId,
@@ -48274,13 +48973,13 @@ var TEST_TEMPLATE = `const data = process.env.PLUGIN_CONNECTION_FILE
48274
48973
  : {};
48275
48974
  const conn = data.connection ?? data.connections?.[0]?.config ?? {};
48276
48975
  const message = conn.apiKey ? "ok" : "missing apiKey";
48277
- process.stdout.write(JSON.stringify({ ok: Boolean(conn.apiKey), message }));
48976
+ process.stdout.write(JSON.stringify({ ok: true, result: { ok: Boolean(conn.apiKey), message } }));
48278
48977
  `;
48279
48978
  var DISCOVER_TEMPLATE = `const data = process.env.PLUGIN_CONNECTION_FILE
48280
48979
  ? JSON.parse(require("fs").readFileSync(process.env.PLUGIN_CONNECTION_FILE, "utf8"))
48281
48980
  : {};
48282
48981
  const conn = data.connection ?? data.connections?.[0]?.config ?? {};
48283
- process.stdout.write(JSON.stringify([{ id: "sample", name: "Sample resource" }]));
48982
+ process.stdout.write(JSON.stringify({ ok: true, result: [{ id: "sample", name: "Sample resource" }] }));
48284
48983
  `;
48285
48984
  var UI_TEMPLATE = `<!doctype html>
48286
48985
  <html>
@@ -48833,7 +49532,7 @@ captured).
48833
49532
  }
48834
49533
  },
48835
49534
  "sandbox-plugins-connection": {
48836
- version: "1.0.0",
49535
+ version: "1.1.0",
48837
49536
  content: `---
48838
49537
  name: sandbox-plugins-connection
48839
49538
  description: Define connection fields and test/discover handlers for a tenant sandbox plugin. Trigger: plugin needs credentials or resource discovery.
@@ -48849,17 +49548,20 @@ The resolved connection is injected at execution time as an
48849
49548
  \`PLUGIN_CONNECTION_FILE\` (never inline; credentials stay out of argv). It is never hardcoded.
48850
49549
 
48851
49550
  ## test handler
48852
- Print \`{ ok, message }\`:
49551
+ Print the two-level result envelope \`{ ok, result: { ok, message } }\`: the outer
49552
+ \`ok\` means the handler ran, the inner \`ok\`/\`message\` is the connection verdict.
49553
+ Put extra diagnostics under \`result.details\`. A single-level \`{ ok, message }\`
49554
+ is rejected as an invalid result.
48853
49555
  \`\`\`js
48854
49556
  const data = JSON.parse(require("fs").readFileSync(process.env.PLUGIN_CONNECTION_FILE, "utf8"));
48855
49557
  const conn = data.connection ?? data.connections?.[0]?.config ?? {};
48856
- process.stdout.write(JSON.stringify({ ok: Boolean(conn.apiKey), message: conn.apiKey ? "ok" : "missing apiKey" }));
49558
+ process.stdout.write(JSON.stringify({ ok: true, result: { ok: Boolean(conn.apiKey), message: conn.apiKey ? "ok" : "missing apiKey" } }));
48857
49559
  \`\`\`
48858
49560
 
48859
49561
  ## discover handler
48860
- Print an array of resources:
49562
+ Print \`{ ok: true, result: [ ...resources ] }\`:
48861
49563
  \`\`\`js
48862
- process.stdout.write(JSON.stringify([{ id: "a", name: "A", group: "G" }]));
49564
+ process.stdout.write(JSON.stringify({ ok: true, result: [{ id: "a", name: "A", group: "G" }] }));
48863
49565
  \`\`\`
48864
49566
 
48865
49567
  Set \`connection.resourceLabel\` for the selection panel title.
@@ -48916,6 +49618,70 @@ Fall back to \`execute_code\`/\`shell\` to run \`node handlers/x.js\` manually w
48916
49618
  \`PLUGIN_INPUT\` and a synthetic \`PLUGIN_CONNECTION_FILE\` to reproduce logic bugs.
48917
49619
  A synthetic fixture only verifies plumbing \u2014 a real upstream call requires a real
48918
49620
  \`connectionKey\` configured by a human.
49621
+ `,
49622
+ resources: {}
49623
+ },
49624
+ "sandbox-plugins-deploy": {
49625
+ version: "1.0.0",
49626
+ content: `---
49627
+ name: sandbox-plugins-deploy
49628
+ description: Deploy a validated tenant plugin as a resident service (alwaysOn vs onDemand), check its status, and undeploy it. Trigger: after a plugin's tests pass and it should run as a long-lived service, or when inspecting/removing a deployment.
49629
+ ---
49630
+
49631
+ # Deploying a Plugin as a Resident Service
49632
+
49633
+ By default a plugin handler runs as a short-lived process on each tool call.
49634
+ Optionally, you can **deploy** the plugin as a **resident service**: one
49635
+ long-lived sandbox per plugin that serves every call. This is faster for
49636
+ frequent or heavy calls, but costs a running sandbox.
49637
+
49638
+ ## Precondition: tests must pass first
49639
+ Deploy ONLY after the plugin is validated AND its tests pass:
49640
+ 1. \`manage_plugin validate\` \u2014 no diagnostics.
49641
+ 2. \`manage_plugin create\`/\`update\` \u2014 written and reloaded.
49642
+ 3. \`manage_plugin test\` \u2014 every tool/connection test returns \`ok: true\`
49643
+ (a real \`connectionKey\` test for credentialed tools).
49644
+ Never deploy a plugin you have not tested: a resident service wraps the same
49645
+ handlers, so an untested handler fails identically \u2014 just in a service you now
49646
+ have to undeploy.
49647
+
49648
+ ## Choose a mode
49649
+ - \`"onDemand"\` (default) \u2014 the service is paused when idle and resumed on the
49650
+ next call. Best for plugins used occasionally. Lower steady cost.
49651
+ - \`"alwaysOn"\` \u2014 the service stays resident and is restarted if it becomes
49652
+ unhealthy. Best for plugins on a hot path or with expensive startup.
49653
+
49654
+ ## Deploy
49655
+ \`\`\`jsonc
49656
+ { "action": "deploy", "type": "my-plugin", "mode": "alwaysOn" }
49657
+ \`\`\`
49658
+ Returns \`{ deployed, status, endpoint, mode }\`. \`status\` is \`"running"\` when
49659
+ the service answered its health check. On failure you get
49660
+ \`{ deployed: false, error }\` \u2014 report it, do not treat it as success.
49661
+ Deploying again is idempotent: it reuses the same service.
49662
+
49663
+ ## Check status
49664
+ \`\`\`jsonc
49665
+ { "action": "status", "type": "my-plugin" }
49666
+ \`\`\`
49667
+ Returns \`{ status, endpoint?, mode?, lastHealthAt?, lastError? }\`. \`"absent"\`
49668
+ means no deployment exists; \`"failed"\` (with \`lastError\`) means the service is
49669
+ down. Inspect this before reporting the plugin as serving.
49670
+
49671
+ ## Undeploy
49672
+ \`\`\`jsonc
49673
+ { "action": "undeploy", "type": "my-plugin" }
49674
+ \`\`\`
49675
+ Returns \`{ undeployed }\`. Undeploy removes the resident sandbox and its
49676
+ deployment record; the plugin files and manifest are untouched, so tool calls
49677
+ fall back to the short-process path. Use it to stop paying for a service you no
49678
+ longer need.
49679
+
49680
+ ## When resident services are unavailable
49681
+ If the environment has no deployment service registered, deploy/undeploy/status
49682
+ return a diagnostic (\`{ deployed: false, error }\` etc.) instead of throwing.
49683
+ That is not a plugin defect: the plugin still works through the default
49684
+ short-process path, so report it as not-deployed rather than broken.
48919
49685
  `,
48920
49686
  resources: {}
48921
49687
  }
@@ -48952,9 +49718,197 @@ the connector and give you its key. Only after a real \`connectionKey\` test
48952
49718
  passes may you call the tool verified; without it, report functionality as
48953
49719
  unverified (missing credentials), not as passing. Use \`shell_exec\` (the tool
48954
49720
  provided by the \`code_eval\` middleware) to run \`node handlers/x.js\` for manual
48955
- checks when reproducing a failure.`;
49721
+ checks when reproducing a failure.
49722
+
49723
+ After the plugin passes \`validate\` and its tests, deploy it as a resident
49724
+ service: load "sandbox-plugins-deploy" and follow it. Choose \`onDemand\`
49725
+ (default) or \`alwaysOn\` for hot paths, call \`manage_plugin deploy\` with the
49726
+ chosen \`mode\`, then confirm with \`manage_plugin status\` (\`running\`). Only claim
49727
+ the plugin is deployed and serving once \`status\` reports \`running\`; if the
49728
+ environment reports no deployment service, say so and treat deployment as
49729
+ unavailable \u2014 do NOT claim success. \`undeploy\` removes the service without
49730
+ touching the plugin files.`;
49731
+
49732
+ // src/middlewares/sandboxPluginsUi.ts
49733
+ var SANDBOX_PLUGINS_UI_RESOURCE = "ui://sandbox-plugins/app";
49734
+ var SANDBOX_PLUGINS_APP_HTML = `<!doctype html>
49735
+ <html lang="en">
49736
+ <head>
49737
+ <meta charset="utf-8" />
49738
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
49739
+ <title>Plugin Deployments</title>
49740
+ <style>
49741
+ :root { color-scheme: light dark; }
49742
+ * { box-sizing: border-box; }
49743
+ body {
49744
+ margin: 0;
49745
+ padding: 14px 16px;
49746
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
49747
+ font-size: 13px;
49748
+ color: #1f2430;
49749
+ background: transparent;
49750
+ }
49751
+ h1 { font-size: 14px; font-weight: 600; margin: 0 0 2px; }
49752
+ .subtitle { color: #6b7280; font-size: 12px; margin: 0 0 12px; }
49753
+ table { width: 100%; border-collapse: collapse; }
49754
+ th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eceef2; vertical-align: top; }
49755
+ th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; font-weight: 600; }
49756
+ td.type { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600; white-space: nowrap; }
49757
+ td.mode { white-space: nowrap; color: #4b5563; }
49758
+ td.detail { color: #4b5563; word-break: break-word; max-width: 280px; }
49759
+ .muted { color: #9ca3af; }
49760
+ .status { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
49761
+ .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: #9ca3af; }
49762
+ .dot.running { background: #22c55e; }
49763
+ .dot.failed { background: #ef4444; }
49764
+ .dot.requested, .dot.provisioning, .dot.stopping { background: #f59e0b; }
49765
+ .dot.paused, .dot.stopped { background: #94a3b8; }
49766
+ .state { color: #6b7280; padding: 14px 2px; }
49767
+ .error { margin: 0 0 10px; padding: 8px 10px; border-radius: 8px; background: #fef2f2; color: #b91c1c; }
49768
+ </style>
49769
+ </head>
49770
+ <body>
49771
+ <h1>Plugin Deployments</h1>
49772
+ <p class="subtitle">Resident services deployed for this tenant. Read-only.</p>
49773
+ <div id="root"><p class="state">Loading\u2026</p></div>
49774
+ <script>
49775
+ (function () {
49776
+ var root = document.getElementById("root");
49777
+
49778
+ function post(message) {
49779
+ window.parent.postMessage(message, "*");
49780
+ }
49781
+ function notify(method, params) {
49782
+ post({ jsonrpc: "2.0", method: method, params: params });
49783
+ }
49784
+ var nextId = 1;
49785
+ function rpc(method, params) {
49786
+ var id = nextId++;
49787
+ return new Promise(function (resolve, reject) {
49788
+ function onMessage(event) {
49789
+ var data = event.data;
49790
+ if (!data || data.id !== id) return;
49791
+ window.removeEventListener("message", onMessage);
49792
+ if (data.error) reject(new Error((data.error && data.error.message) || "rpc error"));
49793
+ else resolve(data.result);
49794
+ }
49795
+ window.addEventListener("message", onMessage);
49796
+ post({ jsonrpc: "2.0", id: id, method: method, params: params });
49797
+ });
49798
+ }
49799
+
49800
+ function sendSize() {
49801
+ var width = Math.ceil(document.documentElement.scrollWidth || document.body.scrollWidth);
49802
+ var height = Math.ceil(document.documentElement.scrollHeight || document.body.scrollHeight);
49803
+ notify("ui/notifications/size-changed", { width: width, height: height });
49804
+ }
49805
+
49806
+ function esc(value) {
49807
+ return String(value === null || value === undefined ? "" : value).replace(/[&<>"']/g, function (ch) {
49808
+ return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[ch];
49809
+ });
49810
+ }
49811
+
49812
+ function statusClass(status) {
49813
+ switch (status) {
49814
+ case "running": return "running";
49815
+ case "failed": return "failed";
49816
+ case "requested":
49817
+ case "provisioning":
49818
+ case "stopping": return "requested";
49819
+ case "paused":
49820
+ case "stopped": return "paused";
49821
+ default: return "";
49822
+ }
49823
+ }
49824
+
49825
+ function healthCell(item) {
49826
+ if (item.lastError) return '<span style="color:#b91c1c">' + esc(item.lastError) + "</span>";
49827
+ if (item.lastHealthAt) return '<span class="muted">' + esc(item.lastHealthAt) + "</span>";
49828
+ return '<span class="muted">\u2014</span>';
49829
+ }
49830
+
49831
+ function render(payload) {
49832
+ var deployments = payload && Array.isArray(payload.deployments) ? payload.deployments : [];
49833
+ var error = payload && payload.error ? String(payload.error) : "";
49834
+ var html = "";
49835
+ if (error) html += '<div class="error">' + esc(error) + "</div>";
49836
+ if (deployments.length === 0) {
49837
+ html += '<p class="state">' + (error ? "No deployments available." : "No resident plugin deployments.") + "</p>";
49838
+ root.innerHTML = html;
49839
+ sendSize();
49840
+ return;
49841
+ }
49842
+ html += "<table><thead><tr><th>Plugin</th><th>Status</th><th>Mode</th><th>Endpoint</th><th>Health</th></tr></thead><tbody>";
49843
+ deployments.forEach(function (item) {
49844
+ var status = item && item.status ? String(item.status) : "unknown";
49845
+ html += "<tr>";
49846
+ html += '<td class="type">' + esc(item && item.pluginType) + "</td>";
49847
+ html += '<td><span class="status"><span class="dot ' + statusClass(status) + '"></span>' + esc(status) + "</span></td>";
49848
+ html += '<td class="mode">' + esc(item && item.mode) + "</td>";
49849
+ html += '<td class="detail">' + (item && item.endpoint ? esc(item.endpoint) : '<span class="muted">\u2014</span>') + "</td>";
49850
+ html += '<td class="detail">' + healthCell(item || {}) + "</td>";
49851
+ html += "</tr>";
49852
+ });
49853
+ html += "</tbody></table>";
49854
+ root.innerHTML = html;
49855
+ sendSize();
49856
+ }
49857
+
49858
+ function readResult(params) {
49859
+ if (!params || typeof params !== "object") return null;
49860
+ if (params.structuredContent && typeof params.structuredContent === "object") {
49861
+ return params.structuredContent;
49862
+ }
49863
+ var content = params.content;
49864
+ var text = content && content[0] && typeof content[0].text === "string" ? content[0].text : "";
49865
+ if (!text) return null;
49866
+ try { return JSON.parse(text); } catch (err) { return null; }
49867
+ }
49868
+
49869
+ // 1) Register the tool-result listener BEFORE initializing so the data
49870
+ // the host pushes right after the handshake is never missed.
49871
+ window.addEventListener("message", function (event) {
49872
+ var data = event.data;
49873
+ if (!data || typeof data !== "object" || typeof data.method !== "string") return;
49874
+ if (data.method === "ui/notifications/tool-result") {
49875
+ render(readResult(data.params));
49876
+ }
49877
+ });
49878
+
49879
+ if (typeof ResizeObserver !== "undefined") {
49880
+ new ResizeObserver(sendSize).observe(document.documentElement);
49881
+ } else {
49882
+ window.addEventListener("resize", sendSize);
49883
+ }
49884
+
49885
+ // 2) MCP Apps lifecycle: initialize, then signal readiness. The host
49886
+ // pushes tool data only after this completes.
49887
+ rpc("ui/initialize", {
49888
+ protocolVersion: "2025-06-18",
49889
+ capabilities: {},
49890
+ clientInfo: { name: "sandbox-plugins-ui", version: "1.0.0" },
49891
+ appCapabilities: { availableDisplayModes: ["inline"] }
49892
+ }).then(function () {
49893
+ notify("ui/notifications/initialized", {});
49894
+ sendSize();
49895
+ }).catch(function (err) {
49896
+ root.innerHTML = '<p class="state">Failed to initialize: ' + esc((err && err.message) || err) + "</p>";
49897
+ sendSize();
49898
+ });
49899
+ })();
49900
+ </script>
49901
+ </body>
49902
+ </html>
49903
+ `;
48956
49904
 
48957
49905
  // src/middlewares/sandboxPluginsMiddleware.ts
49906
+ var SANDBOX_PLUGINS_UI_REF = {
49907
+ kind: "plugin",
49908
+ pluginType: "sandbox-plugins",
49909
+ resource: SANDBOX_PLUGINS_UI_RESOURCE,
49910
+ displayMode: "inline"
49911
+ };
48958
49912
  var PLUGIN_BUILDER_SKILLS = [...Object.keys(SANDBOX_PLUGIN_SKILLS), "task-tracking"];
48959
49913
  function tenantIdFrom(exeConfig) {
48960
49914
  const c = exeConfig;
@@ -48972,6 +49926,15 @@ function collectSecretValues(connection, fields) {
48972
49926
  function errorText(err) {
48973
49927
  return err instanceof Error ? err.message : String(err);
48974
49928
  }
49929
+ function deploymentUnavailable(action) {
49930
+ const error = `plugin deployment service is not configured; cannot ${action}. Resident plugin services are unavailable in this environment.`;
49931
+ if (action === "undeploy") return JSON.stringify({ undeployed: false, error });
49932
+ if (action === "status") return JSON.stringify({ status: "unavailable", error });
49933
+ return JSON.stringify({ deployed: false, error });
49934
+ }
49935
+ function deploymentsResult(payload) {
49936
+ return appendUiFence(JSON.stringify(payload), SANDBOX_PLUGINS_UI_REF);
49937
+ }
48975
49938
  function redactString(value, secrets) {
48976
49939
  let out = value;
48977
49940
  for (const secret of secrets) out = out.split(secret).join("[redacted]");
@@ -48992,10 +49955,24 @@ function redactSecretValues(payload, secrets) {
48992
49955
  return walk(payload);
48993
49956
  }
48994
49957
  var managePluginSchema = import_zod71.z.object({
48995
- action: import_zod71.z.enum(["list", "get", "validate", "create", "update", "delete", "reload", "test"]),
49958
+ action: import_zod71.z.enum([
49959
+ "list",
49960
+ "get",
49961
+ "validate",
49962
+ "create",
49963
+ "update",
49964
+ "delete",
49965
+ "reload",
49966
+ "test",
49967
+ "deploy",
49968
+ "undeploy",
49969
+ "status",
49970
+ "deployments"
49971
+ ]),
48996
49972
  type: import_zod71.z.string().optional(),
48997
49973
  manifest: import_zod71.z.record(import_zod71.z.unknown()).optional(),
48998
49974
  handlers: import_zod71.z.record(import_zod71.z.string()).optional(),
49975
+ mode: import_zod71.z.enum(["onDemand", "alwaysOn"]).optional(),
48999
49976
  target: import_zod71.z.object({
49000
49977
  kind: import_zod71.z.enum(["tool", "connection-test", "connection-discover"]),
49001
49978
  name: import_zod71.z.string().optional(),
@@ -49008,9 +49985,18 @@ var sandboxPluginsPlugin = {
49008
49985
  type: "sandbox-plugins",
49009
49986
  category: "execution",
49010
49987
  name: "Sandbox Plugins",
49011
- description: "Author and manage tenant plugins stored in the sandbox: list/validate/create/update/delete/reload/test. Tools are agent-callable JS handlers and may declare an optional interactive HTML UI (rendered as a sandboxed MCP App). Pairs with the Skill plugin for authoring guidance.",
49988
+ description: "Author and manage tenant plugins stored in the sandbox: list/validate/create/update/delete/reload/test, and deploy/undeploy/status/deployments to run a plugin as a resident service. Tools are agent-callable JS handlers and may declare an optional interactive HTML UI (rendered as a sandboxed MCP App). Pairs with the Skill plugin for authoring guidance.",
49012
49989
  recommends: ["skill"],
49013
- tools: [{ name: "manage_plugin", description: "Author and manage tenant sandbox plugins (tools, optional per-tool HTML UI, connections, and Open/MCP exposure)" }],
49990
+ tools: [
49991
+ {
49992
+ name: "manage_plugin",
49993
+ description: "Author and manage tenant sandbox plugins (tools, optional per-tool HTML UI, connections, Open/MCP exposure, and resident deployment). Actions: list/get/validate/create/update/delete/reload/test, deploy/undeploy/status, and `deployments` to list every resident deployment for the tenant.",
49994
+ ui: { resource: SANDBOX_PLUGINS_UI_RESOURCE, displayMode: "inline" }
49995
+ }
49996
+ ],
49997
+ uiResources: {
49998
+ [SANDBOX_PLUGINS_UI_RESOURCE]: { html: SANDBOX_PLUGINS_APP_HTML }
49999
+ },
49014
50000
  configSchema: { type: "object", properties: {} },
49015
50001
  defaultConfig: {}
49016
50002
  },
@@ -49031,7 +50017,7 @@ var sandboxPluginsPlugin = {
49031
50017
  id: "sandbox-plugins",
49032
50018
  type: "sandbox-plugins",
49033
50019
  name: "Sandbox Plugins",
49034
- description: "Author and manage tenant plugins (list/validate/create/update/delete/reload/test)",
50020
+ description: "Author and manage tenant plugins (list/validate/create/update/delete/reload/test/deploy/undeploy/status)",
49035
50021
  enabled: true,
49036
50022
  config: {}
49037
50023
  },
@@ -49227,13 +50213,74 @@ var sandboxPluginsPlugin = {
49227
50213
  return JSON.stringify(redactSecretValues({ ok: false, error: errorText(err) }, secrets));
49228
50214
  }
49229
50215
  }
50216
+ case "deploy": {
50217
+ if (!input.type) return "type is required";
50218
+ const service3 = getPluginDeploymentService();
50219
+ if (!service3) return deploymentUnavailable("deploy");
50220
+ try {
50221
+ const result = await service3.deploy(tenantId2, input.type, {
50222
+ ...input.mode === void 0 ? {} : { mode: input.mode }
50223
+ });
50224
+ return JSON.stringify({
50225
+ deployed: true,
50226
+ status: result.status,
50227
+ endpoint: result.endpoint,
50228
+ mode: result.mode
50229
+ });
50230
+ } catch (err) {
50231
+ return JSON.stringify({ deployed: false, error: errorText(err) });
50232
+ }
50233
+ }
50234
+ case "undeploy": {
50235
+ if (!input.type) return "type is required";
50236
+ const service3 = getPluginDeploymentService();
50237
+ if (!service3) return deploymentUnavailable("undeploy");
50238
+ try {
50239
+ const result = await service3.undeploy(tenantId2, input.type);
50240
+ return JSON.stringify({ undeployed: result.undeployed });
50241
+ } catch (err) {
50242
+ return JSON.stringify({ undeployed: false, error: errorText(err) });
50243
+ }
50244
+ }
50245
+ case "status": {
50246
+ if (!input.type) return "type is required";
50247
+ const service3 = getPluginDeploymentService();
50248
+ if (!service3) return deploymentUnavailable("status");
50249
+ try {
50250
+ const result = await service3.status(tenantId2, input.type);
50251
+ return JSON.stringify({
50252
+ status: result.status,
50253
+ endpoint: result.endpoint,
50254
+ mode: result.mode,
50255
+ lastHealthAt: result.lastHealthAt,
50256
+ lastError: result.lastError
50257
+ });
50258
+ } catch (err) {
50259
+ return JSON.stringify({ status: "unavailable", error: errorText(err) });
50260
+ }
50261
+ }
50262
+ case "deployments": {
50263
+ const service3 = getPluginDeploymentService();
50264
+ if (!service3) {
50265
+ return deploymentsResult({
50266
+ deployments: [],
50267
+ error: "plugin deployment service is not configured; cannot list deployments. Resident plugin services are unavailable in this environment."
50268
+ });
50269
+ }
50270
+ try {
50271
+ const deployments = await service3.list(tenantId2);
50272
+ return deploymentsResult({ deployments });
50273
+ } catch (err) {
50274
+ return deploymentsResult({ deployments: [], error: errorText(err) });
50275
+ }
50276
+ }
49230
50277
  default:
49231
50278
  return `unsupported action: ${input.action}`;
49232
50279
  }
49233
50280
  },
49234
50281
  {
49235
50282
  name: "manage_plugin",
49236
- description: "Author and manage tenant sandbox plugins (list/get/validate/create/update/delete/reload/test).",
50283
+ description: "Author and manage tenant sandbox plugins (list/get/validate/create/update/delete/reload/test) and deploy/undeploy/status a plugin as a resident service.",
49237
50284
  schema: managePluginSchema
49238
50285
  }
49239
50286
  )
@@ -49288,6 +50335,526 @@ function buildBundleFromDist(distDir, version) {
49288
50335
  };
49289
50336
  }
49290
50337
 
50338
+ // src/sandbox_plugin/PluginDeploymentRuntimeRegistry.ts
50339
+ var PluginDeploymentRuntimeRegistry = class {
50340
+ /**
50341
+ * @param options - Optional injectable clock for deterministic health timestamps.
50342
+ */
50343
+ constructor(options = {}) {
50344
+ this.entries = /* @__PURE__ */ new Map();
50345
+ this.inFlight = /* @__PURE__ */ new Map();
50346
+ this.now = options.now ?? Date.now;
50347
+ }
50348
+ /**
50349
+ * Cache the resolved proxy endpoint for a deployment.
50350
+ *
50351
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50352
+ * @param endpoint - Relative proxy path, e.g. `/api/sandboxes/plugin-abc/proxy/8080`.
50353
+ */
50354
+ setEndpoint(id, endpoint) {
50355
+ this.entry(id).endpoint = endpoint;
50356
+ }
50357
+ /**
50358
+ * Read the cached endpoint for a deployment.
50359
+ *
50360
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50361
+ * @returns The cached endpoint, or `undefined` when none is known.
50362
+ */
50363
+ getEndpoint(id) {
50364
+ return this.entries.get(id)?.endpoint;
50365
+ }
50366
+ /**
50367
+ * Record a successful health observation and clear any prior error.
50368
+ *
50369
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50370
+ */
50371
+ markHealthy(id) {
50372
+ const entry = this.entry(id);
50373
+ entry.lastHealthAt = this.now();
50374
+ delete entry.lastError;
50375
+ }
50376
+ /**
50377
+ * Record a failed health observation.
50378
+ *
50379
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50380
+ * @param error - Human-readable failure description.
50381
+ */
50382
+ markUnhealthy(id, error) {
50383
+ const entry = this.entry(id);
50384
+ entry.lastHealthAt = this.now();
50385
+ entry.lastError = error;
50386
+ }
50387
+ /**
50388
+ * Read the last observed health for a deployment.
50389
+ *
50390
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50391
+ * @returns The cached health, or `undefined` when the id is unknown.
50392
+ */
50393
+ getHealth(id) {
50394
+ const entry = this.entries.get(id);
50395
+ if (!entry) return void 0;
50396
+ const health = {};
50397
+ if (entry.lastHealthAt !== void 0) health.lastHealthAt = entry.lastHealthAt;
50398
+ if (entry.lastError !== void 0) health.lastError = entry.lastError;
50399
+ return health;
50400
+ }
50401
+ /**
50402
+ * Run a cold start/resume factory at most once per id at a time.
50403
+ *
50404
+ * Concurrent calls for the same id return the same in-flight promise and run
50405
+ * `factory` exactly once. The in-flight entry is cleared when the factory
50406
+ * settles, so a later call runs it again (retry after failure is allowed).
50407
+ *
50408
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50409
+ * @param factory - Idempotent start/resume operation for the resident service.
50410
+ * @returns A promise that settles with the factory outcome.
50411
+ */
50412
+ ensureRunning(id, factory) {
50413
+ const existing = this.inFlight.get(id);
50414
+ if (existing) return existing;
50415
+ let started;
50416
+ try {
50417
+ started = factory();
50418
+ } catch (error) {
50419
+ started = Promise.reject(error);
50420
+ }
50421
+ const tracked = started.finally(() => {
50422
+ if (this.inFlight.get(id) === tracked) {
50423
+ this.inFlight.delete(id);
50424
+ }
50425
+ });
50426
+ this.inFlight.set(id, tracked);
50427
+ return tracked;
50428
+ }
50429
+ /**
50430
+ * Drop cached endpoint and health for one deployment.
50431
+ *
50432
+ * @param id - Composed key `${tenantId}:${pluginType}`.
50433
+ */
50434
+ clear(id) {
50435
+ this.entries.delete(id);
50436
+ }
50437
+ /** Drop all cached endpoints, health, and in-flight coordination entries. */
50438
+ reset() {
50439
+ this.entries.clear();
50440
+ this.inFlight.clear();
50441
+ }
50442
+ entry(id) {
50443
+ const existing = this.entries.get(id);
50444
+ if (existing) return existing;
50445
+ const created = {};
50446
+ this.entries.set(id, created);
50447
+ return created;
50448
+ }
50449
+ };
50450
+
50451
+ // src/sandbox_plugin/host/pluginHostProtocol.ts
50452
+ var INLINE_INPUT_LIMIT_BYTES2 = 48 * 1024;
50453
+ var DEFAULT_HANDLER_TIMEOUT_MS = 6e4;
50454
+ var MIN_HANDLER_TIMEOUT_MS = 1e3;
50455
+ var MAX_HANDLER_TIMEOUT_MS = 3e5;
50456
+ function isRecord11(value) {
50457
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50458
+ }
50459
+ function isEnvelope3(value) {
50460
+ return isRecord11(value) && typeof value.ok === "boolean";
50461
+ }
50462
+ function parseHostArgs(argv) {
50463
+ let type;
50464
+ let port;
50465
+ for (let index = 0; index < argv.length; index += 1) {
50466
+ const token = argv[index];
50467
+ if (token === "--type" && index + 1 < argv.length) {
50468
+ type = argv[index + 1];
50469
+ index += 1;
50470
+ } else if (token.startsWith("--type=")) {
50471
+ type = token.slice("--type=".length);
50472
+ } else if (token === "--port" && index + 1 < argv.length) {
50473
+ port = argv[index + 1];
50474
+ index += 1;
50475
+ } else if (token.startsWith("--port=")) {
50476
+ port = token.slice("--port=".length);
50477
+ }
50478
+ }
50479
+ const parsedPort = Number(port);
50480
+ if (typeof type !== "string" || type.length === 0) {
50481
+ throw new Error("parseHostArgs: missing or empty --type");
50482
+ }
50483
+ if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
50484
+ throw new Error("parseHostArgs: invalid --port");
50485
+ }
50486
+ return { type, port: parsedPort };
50487
+ }
50488
+ function resolveHandlerTimeoutMs(value) {
50489
+ if (value === null || value === void 0 || value === "") return DEFAULT_HANDLER_TIMEOUT_MS;
50490
+ const numeric = typeof value === "number" ? value : Number(value);
50491
+ if (!Number.isFinite(numeric)) return DEFAULT_HANDLER_TIMEOUT_MS;
50492
+ return Math.min(Math.max(Math.round(numeric), MIN_HANDLER_TIMEOUT_MS), MAX_HANDLER_TIMEOUT_MS);
50493
+ }
50494
+ function parseHandlerEnvelope(stdout) {
50495
+ const trimmed = stdout.trim();
50496
+ if (!trimmed) return { ok: false, error: "empty handler output" };
50497
+ const whole = tryParse(trimmed);
50498
+ if (isEnvelope3(whole)) return normalizeEnvelope(whole);
50499
+ const lines = stdout.split("\n");
50500
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
50501
+ const line = lines[index]?.trim();
50502
+ if (!line) continue;
50503
+ const candidate = tryParse(line);
50504
+ if (isEnvelope3(candidate)) return normalizeEnvelope(candidate);
50505
+ }
50506
+ return { ok: true, result: trimmed };
50507
+ }
50508
+ function buildHandlerCommand(handlerPath) {
50509
+ return `node ${quotePosixShellArg2(handlerPath)}`;
50510
+ }
50511
+ function encodeInput(input) {
50512
+ return Buffer.from(JSON.stringify(input ?? null), "utf8").toString("base64");
50513
+ }
50514
+ function buildConnectionPayload2(args) {
50515
+ if (Array.isArray(args.connections) && args.connections.length > 0) {
50516
+ return {
50517
+ connection: args.connection ?? args.connections[0]?.config,
50518
+ connections: args.connections
50519
+ };
50520
+ }
50521
+ if (args.connection) {
50522
+ return { connection: args.connection };
50523
+ }
50524
+ return null;
50525
+ }
50526
+ function quotePosixShellArg2(value) {
50527
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
50528
+ }
50529
+ function tryParse(text) {
50530
+ try {
50531
+ return JSON.parse(text);
50532
+ } catch {
50533
+ return void 0;
50534
+ }
50535
+ }
50536
+ function normalizeEnvelope(parsed) {
50537
+ if (parsed.ok) return { ok: true, result: parsed.result };
50538
+ return { ok: false, error: typeof parsed.error === "string" ? parsed.error : void 0 };
50539
+ }
50540
+
50541
+ // src/sandbox_plugin/host/pluginHostSource.ts
50542
+ var import_node_crypto15 = require("crypto");
50543
+ var PLUGIN_HOST_VERSION = "1.1.0";
50544
+ var PLUGIN_HOST_SERVER_SOURCE = String.raw`#!/usr/bin/env node
50545
+ "use strict";
50546
+
50547
+ var http = require("node:http");
50548
+ var fs = require("node:fs");
50549
+ var os = require("node:os");
50550
+ var path = require("node:path");
50551
+ var crypto = require("node:crypto");
50552
+ var execFile = require("node:child_process").execFile;
50553
+
50554
+ var VERSION = "${PLUGIN_HOST_VERSION}";
50555
+ var PLUGINS_ROOT = "/root/.agents/plugins";
50556
+ var INLINE_INPUT_LIMIT_BYTES = 48 * 1024;
50557
+ var MAX_BODY_BYTES = 8 * 1024 * 1024;
50558
+ var MAX_RESULT_BYTES = 16 * 1024 * 1024;
50559
+ var DEFAULT_HANDLER_TIMEOUT_MS = 60000;
50560
+ var MIN_HANDLER_TIMEOUT_MS = 1000;
50561
+ var MAX_HANDLER_TIMEOUT_MS = 300000;
50562
+
50563
+ function resolveHandlerTimeoutMs(value) {
50564
+ if (value === null || value === undefined || value === "") {
50565
+ return DEFAULT_HANDLER_TIMEOUT_MS;
50566
+ }
50567
+ var numeric = typeof value === "number" ? value : Number(value);
50568
+ if (!isFinite(numeric)) {
50569
+ return DEFAULT_HANDLER_TIMEOUT_MS;
50570
+ }
50571
+ return Math.min(Math.max(Math.round(numeric), MIN_HANDLER_TIMEOUT_MS), MAX_HANDLER_TIMEOUT_MS);
50572
+ }
50573
+
50574
+ function isRecord(value) {
50575
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50576
+ }
50577
+
50578
+ function isEnvelope(value) {
50579
+ return isRecord(value) && typeof value.ok === "boolean";
50580
+ }
50581
+
50582
+ function tryParse(text) {
50583
+ try {
50584
+ return JSON.parse(text);
50585
+ } catch (err) {
50586
+ return undefined;
50587
+ }
50588
+ }
50589
+
50590
+ function normalizeEnvelope(parsed) {
50591
+ if (parsed.ok) {
50592
+ return { ok: true, result: parsed.result };
50593
+ }
50594
+ return { ok: false, error: typeof parsed.error === "string" ? parsed.error : undefined };
50595
+ }
50596
+
50597
+ function parseHandlerEnvelope(stdout) {
50598
+ var text = String(stdout == null ? "" : stdout);
50599
+ var trimmed = text.trim();
50600
+ if (!trimmed) {
50601
+ // Deliberate divergence from SandboxPluginExecutor, which returns
50602
+ // { ok: true, result: "" } for empty stdout; the resident host treats empty
50603
+ // output as a handler failure so a crashed/no-op handler is not a "success".
50604
+ return { ok: false, error: "empty handler output" };
50605
+ }
50606
+ var whole = tryParse(trimmed);
50607
+ if (isEnvelope(whole)) {
50608
+ return normalizeEnvelope(whole);
50609
+ }
50610
+ var lines = text.split("\n");
50611
+ for (var index = lines.length - 1; index >= 0; index -= 1) {
50612
+ var line = lines[index].trim();
50613
+ if (!line) {
50614
+ continue;
50615
+ }
50616
+ var candidate = tryParse(line);
50617
+ if (isEnvelope(candidate)) {
50618
+ return normalizeEnvelope(candidate);
50619
+ }
50620
+ }
50621
+ return { ok: true, result: trimmed };
50622
+ }
50623
+
50624
+ function quotePosixShellArg(value) {
50625
+ return "'" + String(value).replace(/'/g, "'\"'\"'") + "'";
50626
+ }
50627
+
50628
+ function buildHandlerCommand(handlerPath) {
50629
+ return "node " + quotePosixShellArg(handlerPath);
50630
+ }
50631
+
50632
+ function parseHostArgs(argv) {
50633
+ var type;
50634
+ var port;
50635
+ for (var index = 0; index < argv.length; index += 1) {
50636
+ var token = argv[index];
50637
+ if (token === "--type" && index + 1 < argv.length) {
50638
+ type = argv[index + 1];
50639
+ index += 1;
50640
+ } else if (token.indexOf("--type=") === 0) {
50641
+ type = token.slice("--type=".length);
50642
+ } else if (token === "--port" && index + 1 < argv.length) {
50643
+ port = argv[index + 1];
50644
+ index += 1;
50645
+ } else if (token.indexOf("--port=") === 0) {
50646
+ port = token.slice("--port=".length);
50647
+ }
50648
+ }
50649
+ var parsedPort = Number(port);
50650
+ if (typeof type !== "string" || type.length === 0) {
50651
+ throw new Error("parseHostArgs: missing or empty --type");
50652
+ }
50653
+ if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
50654
+ throw new Error("parseHostArgs: invalid --port");
50655
+ }
50656
+ return { type: type, port: parsedPort };
50657
+ }
50658
+
50659
+ function encodeInput(input) {
50660
+ return Buffer.from(JSON.stringify(input === undefined ? null : input), "utf8").toString("base64");
50661
+ }
50662
+
50663
+ function buildConnectionPayload(body) {
50664
+ if (Array.isArray(body.connections) && body.connections.length > 0) {
50665
+ return {
50666
+ connection: body.connection !== undefined ? body.connection : body.connections[0].config,
50667
+ connections: body.connections,
50668
+ };
50669
+ }
50670
+ if (body.connection) {
50671
+ return { connection: body.connection };
50672
+ }
50673
+ return null;
50674
+ }
50675
+
50676
+ function resolveHandlerPath(type, handler) {
50677
+ if (typeof handler !== "string" || handler.length === 0) {
50678
+ throw new Error("missing handler");
50679
+ }
50680
+ if (handler.charAt(0) === "/" || handler.split("/").indexOf("..") !== -1) {
50681
+ throw new Error("invalid handler path");
50682
+ }
50683
+ return path.join(PLUGINS_ROOT, type, handler);
50684
+ }
50685
+
50686
+ function readJsonBody(req) {
50687
+ return new Promise(function (resolve, reject) {
50688
+ var chunks = [];
50689
+ var size = 0;
50690
+ req.on("data", function (chunk) {
50691
+ size += chunk.length;
50692
+ if (size > MAX_BODY_BYTES) {
50693
+ reject(new Error("request body too large"));
50694
+ req.destroy();
50695
+ return;
50696
+ }
50697
+ chunks.push(chunk);
50698
+ });
50699
+ req.on("end", function () {
50700
+ try {
50701
+ var text = Buffer.concat(chunks).toString("utf8");
50702
+ resolve(text ? JSON.parse(text) : {});
50703
+ } catch (err) {
50704
+ reject(new Error("invalid JSON body"));
50705
+ }
50706
+ });
50707
+ req.on("error", reject);
50708
+ });
50709
+ }
50710
+
50711
+ function runNode(handlerPath, pluginDir, env, timeoutMs) {
50712
+ return new Promise(function (resolve) {
50713
+ execFile(
50714
+ process.execPath,
50715
+ [handlerPath],
50716
+ { cwd: pluginDir, env: env, timeout: timeoutMs, maxBuffer: MAX_RESULT_BYTES },
50717
+ function (error, stdout, stderr) {
50718
+ resolve({ error: error, stdout: stdout || "", stderr: stderr || "" });
50719
+ }
50720
+ );
50721
+ });
50722
+ }
50723
+
50724
+ function failureFromExec(exec, command) {
50725
+ var parsed = parseHandlerEnvelope(exec.stdout);
50726
+ if (!parsed.ok) {
50727
+ return parsed;
50728
+ }
50729
+ var code = exec.error && typeof exec.error.code === "number" ? exec.error.code : "n/a";
50730
+ var detail = exec.stdout ? exec.stdout.slice(0, 4096) : exec.error ? String(exec.error.message) : "";
50731
+ return { ok: false, error: "handler failed (exit " + code + "): " + detail + " [" + command + "]" };
50732
+ }
50733
+
50734
+ async function runHandler(body) {
50735
+ var pluginDir = path.join(PLUGINS_ROOT, config.type);
50736
+ var handlerPath = resolveHandlerPath(config.type, body.handler);
50737
+ var command = buildHandlerCommand(handlerPath);
50738
+ var inputJson = JSON.stringify(body.input === undefined ? null : body.input);
50739
+ var connectionPayload = buildConnectionPayload(body);
50740
+ var inputIsLarge = Buffer.byteLength(inputJson, "utf8") > INLINE_INPUT_LIMIT_BYTES;
50741
+ var needsTempDir = inputIsLarge || connectionPayload !== null;
50742
+ var tempDir = null;
50743
+ var env = Object.assign({}, process.env);
50744
+ try {
50745
+ if (needsTempDir) {
50746
+ tempDir = path.join(os.tmpdir(), "plugin-run-" + Date.now() + "-" + Math.random().toString(36).slice(2));
50747
+ fs.mkdirSync(tempDir, { recursive: true });
50748
+ }
50749
+ if (inputIsLarge) {
50750
+ var inputFile = path.join(tempDir, "input.json");
50751
+ fs.writeFileSync(inputFile, inputJson);
50752
+ env.PLUGIN_INPUT_FILE = inputFile;
50753
+ } else {
50754
+ env.PLUGIN_INPUT = encodeInput(body.input);
50755
+ }
50756
+ if (connectionPayload !== null) {
50757
+ var connectionFile = path.join(tempDir, "connection.json");
50758
+ fs.writeFileSync(connectionFile, JSON.stringify(connectionPayload));
50759
+ env.PLUGIN_CONNECTION_FILE = connectionFile;
50760
+ }
50761
+ env.PLUGIN_CONTEXT = encodeInput(body.context || {});
50762
+ var handlerTimeoutMs = resolveHandlerTimeoutMs(body.timeoutMs);
50763
+ var exec = await runNode(handlerPath, pluginDir, env, handlerTimeoutMs);
50764
+ if (exec.error) {
50765
+ return failureFromExec(exec, command);
50766
+ }
50767
+ return parseHandlerEnvelope(exec.stdout);
50768
+ } finally {
50769
+ if (tempDir) {
50770
+ try {
50771
+ fs.rmSync(tempDir, { recursive: true, force: true });
50772
+ } catch (err) {
50773
+ // best-effort cleanup
50774
+ }
50775
+ }
50776
+ }
50777
+ }
50778
+
50779
+ function manifestHash() {
50780
+ try {
50781
+ var content = fs.readFileSync(path.join(PLUGINS_ROOT, config.type, "plugin.json"), "utf8");
50782
+ return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
50783
+ } catch (err) {
50784
+ return "";
50785
+ }
50786
+ }
50787
+
50788
+ function sendJson(res, status, payload) {
50789
+ var body = JSON.stringify(payload);
50790
+ res.writeHead(status, {
50791
+ "content-type": "application/json",
50792
+ "content-length": Buffer.byteLength(body),
50793
+ });
50794
+ res.end(body);
50795
+ }
50796
+
50797
+ function handleRequest(req, res) {
50798
+ var url = String(req.url || "").split("?")[0];
50799
+ if (req.method === "GET" && url === "/health") {
50800
+ sendJson(res, 200, { ok: true, version: VERSION });
50801
+ return;
50802
+ }
50803
+ if (req.method === "GET" && url === "/meta") {
50804
+ sendJson(res, 200, { type: config.type, manifestHash: manifestHash() });
50805
+ return;
50806
+ }
50807
+ if (req.method === "POST" && url === "/invoke") {
50808
+ readJsonBody(req)
50809
+ .then(function (body) {
50810
+ if (!isRecord(body)) {
50811
+ sendJson(res, 400, { ok: false, error: "invalid body" });
50812
+ return;
50813
+ }
50814
+ return runHandler(body).then(function (result) {
50815
+ sendJson(res, 200, result);
50816
+ });
50817
+ })
50818
+ .catch(function (err) {
50819
+ sendJson(res, 400, { ok: false, error: String(err && err.message ? err.message : err) });
50820
+ });
50821
+ return;
50822
+ }
50823
+ sendJson(res, 404, { ok: false, error: "not found" });
50824
+ }
50825
+
50826
+ var config = parseHostArgs(process.argv);
50827
+ var server = http.createServer(handleRequest);
50828
+ server.listen(config.port, "0.0.0.0", function () {
50829
+ process.stdout.write(
50830
+ "[plugin-host] listening type=" + config.type + " port=" + config.port + " version=" + VERSION + "\n"
50831
+ );
50832
+ });
50833
+ `;
50834
+ function pluginHostContentHash() {
50835
+ return (0, import_node_crypto15.createHash)("sha256").update(`${PLUGIN_HOST_VERSION}:${PLUGIN_HOST_SERVER_SOURCE}`).digest("hex").slice(0, 16);
50836
+ }
50837
+
50838
+ // src/sandbox_plugin/host/pluginHostInstaller.ts
50839
+ var PLUGIN_HOST_DIR = "/root/.agents/.plugin-host";
50840
+ var SERVER_PATH = `${PLUGIN_HOST_DIR}/server.js`;
50841
+ var VERSION_PATH = `${PLUGIN_HOST_DIR}/version.json`;
50842
+ async function installPluginHost(sandbox) {
50843
+ const hash = pluginHostContentHash();
50844
+ try {
50845
+ const existing = await sandbox.file.readFile(VERSION_PATH);
50846
+ const parsed = JSON.parse(existing.content);
50847
+ if (parsed.hash === hash) {
50848
+ return { path: SERVER_PATH, version: PLUGIN_HOST_VERSION, hash, changed: false };
50849
+ }
50850
+ } catch {
50851
+ }
50852
+ await sandbox.file.createDirectory(PLUGIN_HOST_DIR).catch(() => void 0);
50853
+ await sandbox.file.writeFile(SERVER_PATH, PLUGIN_HOST_SERVER_SOURCE);
50854
+ await sandbox.file.writeFile(VERSION_PATH, JSON.stringify({ version: PLUGIN_HOST_VERSION, hash }, null, 2));
50855
+ return { path: SERVER_PATH, version: PLUGIN_HOST_VERSION, hash, changed: true };
50856
+ }
50857
+
49291
50858
  // src/workflow/index.ts
49292
50859
  init_compile();
49293
50860
  init_parse_yaml();
@@ -49679,16 +51246,16 @@ var IdRemapper = class {
49679
51246
  };
49680
51247
 
49681
51248
  // src/util/directRoomId.ts
49682
- var import_node_crypto14 = require("crypto");
51249
+ var import_node_crypto16 = require("crypto");
49683
51250
  function generateDirectHumanProjectId(tenantId2, workspaceId, userId1, userId2) {
49684
51251
  const sortedUserIds = [userId1, userId2].sort();
49685
51252
  const parts = [tenantId2, workspaceId, ...sortedUserIds];
49686
- const hash = (0, import_node_crypto14.createHash)("sha256").update(parts.join(":")).digest("hex");
51253
+ const hash = (0, import_node_crypto16.createHash)("sha256").update(parts.join(":")).digest("hex");
49687
51254
  return `direct_human_${hash}`;
49688
51255
  }
49689
51256
  function generateDirectBotProjectId(tenantId2, workspaceId, userId, assistantId) {
49690
51257
  const parts = [tenantId2, workspaceId, userId, assistantId];
49691
- const hash = (0, import_node_crypto14.createHash)("sha256").update(parts.join(":")).digest("hex");
51258
+ const hash = (0, import_node_crypto16.createHash)("sha256").update(parts.join(":")).digest("hex");
49692
51259
  return `direct_bot_${hash}`;
49693
51260
  }
49694
51261
  function parseDirectRoomType(projectId) {
@@ -49718,6 +51285,7 @@ registerBuiltinPlugins();
49718
51285
  CapabilityBundleValidationError,
49719
51286
  CapabilityToolNameConflictError,
49720
51287
  CapabilityToolRegistry,
51288
+ ChannelConnectionLeader,
49721
51289
  ChunkBuffer,
49722
51290
  ChunkBufferLatticeManager,
49723
51291
  CollectionLatticeManager,
@@ -49729,7 +51297,9 @@ registerBuiltinPlugins();
49729
51297
  DEFAULT_CALIBRATION_PROBES,
49730
51298
  DEFAULT_CONNECT_TIMEOUT_MS,
49731
51299
  DEFAULT_DELAY_MS,
51300
+ DEFAULT_HANDLER_TIMEOUT_MS,
49732
51301
  DEFAULT_MAX_ATTEMPTS,
51302
+ DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS,
49733
51303
  DaytonaInstance,
49734
51304
  DaytonaProvider,
49735
51305
  DefaultScheduleClient,
@@ -49747,6 +51317,7 @@ registerBuiltinPlugins();
49747
51317
  HEALTH_CHECK_INTERVAL_MS,
49748
51318
  HEALTH_CHECK_TIMEOUT_MS,
49749
51319
  HumanMessage,
51320
+ INLINE_INPUT_LIMIT_BYTES,
49750
51321
  IdRemapper,
49751
51322
  InMemoryA2AApiKeyStore,
49752
51323
  InMemoryAgentWebAppStore,
@@ -49787,10 +51358,12 @@ registerBuiltinPlugins();
49787
51358
  LocalSandboxProvider,
49788
51359
  LoggerLatticeManager,
49789
51360
  MAX_BACKOFF_MS,
51361
+ MAX_HANDLER_TIMEOUT_MS,
49790
51362
  MAX_LINE_LENGTH,
49791
51363
  MAX_PACK_ENTRIES,
49792
51364
  MAX_PACK_FILE_BYTES,
49793
51365
  MAX_PACK_TOTAL_BYTES,
51366
+ MIN_HANDLER_TIMEOUT_MS,
49794
51367
  ManagedTaskStartError,
49795
51368
  McpLatticeManager,
49796
51369
  MemoryBackend,
@@ -49809,8 +51382,12 @@ registerBuiltinPlugins();
49809
51382
  PACK_DIRECTORY_TIMEOUT_SECONDS,
49810
51383
  PLATFORM_SECRET_ENV_VARS,
49811
51384
  PLUGIN_CATALOG_CHANGED_TOPIC,
51385
+ PLUGIN_HOST_DIR,
51386
+ PLUGIN_HOST_SERVER_SOURCE,
51387
+ PLUGIN_HOST_VERSION,
49812
51388
  PersonalAssistantConfig,
49813
51389
  PinoLoggerClient,
51390
+ PluginDeploymentRuntimeRegistry,
49814
51391
  PluginRegistry,
49815
51392
  PostgresDatabase,
49816
51393
  ProjectBotMembershipIdConflictError,
@@ -49872,7 +51449,9 @@ registerBuiltinPlugins();
49872
51449
  appendUiFence,
49873
51450
  assertSafeUpstreamUrl,
49874
51451
  buildBundleFromDist,
51452
+ buildConnectionPayload,
49875
51453
  buildGrepResultsDict,
51454
+ buildHandlerCommand,
49876
51455
  buildInput,
49877
51456
  buildNamedVolumeName,
49878
51457
  buildRemotePackDirectoryCommand,
@@ -49887,6 +51466,7 @@ registerBuiltinPlugins();
49887
51466
  clearEncryptionKeyCache,
49888
51467
  clearEvalRunService,
49889
51468
  clearSandboxOpenCredentialProviderForTests,
51469
+ collectPluginSources,
49890
51470
  collectionLatticeManager,
49891
51471
  compileInternal,
49892
51472
  compileSandboxPlugin,
@@ -49939,9 +51519,11 @@ registerBuiltinPlugins();
49939
51519
  decrypt,
49940
51520
  decryptConfigFields,
49941
51521
  describeCronExpression,
51522
+ disconnectAllChannels,
49942
51523
  documentLearningPlugin,
49943
51524
  documentParserPlugin,
49944
51525
  embeddingsLatticeManager,
51526
+ encodeInput,
49945
51527
  encrypt,
49946
51528
  encryptConfigFields,
49947
51529
  ensureBuiltinAgentsForTenant,
@@ -49985,6 +51567,7 @@ registerBuiltinPlugins();
49985
51567
  getModelLattice,
49986
51568
  getNextCronTime,
49987
51569
  getOrCreateCollectionVectorStore,
51570
+ getPluginDeploymentService,
49988
51571
  getPluginSkillManifestVersion,
49989
51572
  getQueueLattice,
49990
51573
  getReplicaId,
@@ -50013,6 +51596,8 @@ registerBuiltinPlugins();
50013
51596
  hasChunkBuffer,
50014
51597
  hasDelegatedWorkerToken,
50015
51598
  hasRuntimeCapabilityPolicy,
51599
+ installPluginHost,
51600
+ invokeResident,
50016
51601
  invokeWithRetry,
50017
51602
  isBuiltInSkill,
50018
51603
  isDirectRoom,
@@ -50026,6 +51611,7 @@ registerBuiltinPlugins();
50026
51611
  jsonSchemaToZod,
50027
51612
  listCollectionEntries,
50028
51613
  listSandboxProviderTypes,
51614
+ listTenantPlugins,
50029
51615
  loggerLatticeManager,
50030
51616
  mcpManager,
50031
51617
  mergeCapabilityBundles,
@@ -50039,6 +51625,8 @@ registerBuiltinPlugins();
50039
51625
  parallelLimit,
50040
51626
  parseCronExpression,
50041
51627
  parseDirectRoomType,
51628
+ parseHandlerEnvelope,
51629
+ parseHostArgs,
50042
51630
  parseJudgeVerdict,
50043
51631
  parsePackSize,
50044
51632
  parseQueuedExecutionMode,
@@ -50047,6 +51635,7 @@ registerBuiltinPlugins();
50047
51635
  parseTrustedRunContext,
50048
51636
  parseYaml,
50049
51637
  performStringReplacement,
51638
+ pluginHostContentHash,
50050
51639
  postProcessTaskMutationResponse,
50051
51640
  prepareTaskDispatch,
50052
51641
  projectMutationMutex,
@@ -50085,6 +51674,7 @@ registerBuiltinPlugins();
50085
51674
  renderTemplate,
50086
51675
  resetReplicaIdForTests,
50087
51676
  resolveEnabledPluginSkill,
51677
+ resolveHandlerTimeoutMs,
50088
51678
  resolveJudgeModelKey,
50089
51679
  resolvePath,
50090
51680
  resolvePluginConnections,
@@ -50104,6 +51694,7 @@ registerBuiltinPlugins();
50104
51694
  setBindingRegistry,
50105
51695
  setEvalRunService,
50106
51696
  setMenuRegistry,
51697
+ setPluginDeploymentService,
50107
51698
  setSandboxPluginCatalog,
50108
51699
  skillLatticeManager,
50109
51700
  sqlDatabaseManager,