@axiom-lattice/core 6.3.3 → 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.mjs CHANGED
@@ -1688,6 +1688,9 @@ var InMemoryProjectStore = class {
1688
1688
  workspaceId,
1689
1689
  name: data.name,
1690
1690
  description: data.description,
1691
+ // Persist `kind` like the Local/PostgreSQL stores so `public`/`personal`/
1692
+ // `training` project semantics survive in in-memory deployments.
1693
+ ...data.kind === void 0 ? {} : { kind: data.kind },
1691
1694
  config: data.config === void 0 && Object.keys(capabilityBundleIds).length === 0 ? void 0 : { ...data.config ?? {}, ...capabilityBundleIds },
1692
1695
  createdAt: existing?.createdAt ?? now,
1693
1696
  updatedAt: now
@@ -3307,6 +3310,93 @@ var InMemoryOutboxStore = class {
3307
3310
  }
3308
3311
  };
3309
3312
 
3313
+ // src/store_lattice/InMemoryPluginDeploymentStore.ts
3314
+ var InMemoryPluginDeploymentStore = class {
3315
+ constructor() {
3316
+ this.entries = /* @__PURE__ */ new Map();
3317
+ }
3318
+ /**
3319
+ * Lists every deployment for a tenant.
3320
+ *
3321
+ * @param tenantId - Tenant whose deployments are listed
3322
+ * @returns Defensive copies of the tenant's deployments
3323
+ */
3324
+ async listByTenant(tenantId2) {
3325
+ return [...this.entries.values()].filter((entry) => entry.tenantId === tenantId2).map((entry) => structuredClone(entry));
3326
+ }
3327
+ /**
3328
+ * Returns the deployment for a tenant + plugin type.
3329
+ *
3330
+ * @param tenantId - Owning tenant
3331
+ * @param pluginType - Plugin type key
3332
+ * @returns Defensive copy of the deployment, or undefined when absent
3333
+ */
3334
+ async get(tenantId2, pluginType) {
3335
+ const entry = this.entries.get(entryKey2(tenantId2, pluginType));
3336
+ return entry ? structuredClone(entry) : void 0;
3337
+ }
3338
+ /**
3339
+ * Inserts or replaces a deployment keyed by tenant + plugin type.
3340
+ *
3341
+ * On conflict the existing `id` and `createdAt` are preserved while every
3342
+ * other field (including `updatedAt`, when supplied) is updated — mirroring
3343
+ * the PostgreSQL store's `ON CONFLICT` semantics.
3344
+ *
3345
+ * @param deployment - Deployment record to persist
3346
+ * @returns Defensive copy of the stored deployment
3347
+ */
3348
+ async upsert(deployment) {
3349
+ const key5 = entryKey2(deployment.tenantId, deployment.pluginType);
3350
+ const existing = this.entries.get(key5);
3351
+ const next = {
3352
+ ...structuredClone(deployment),
3353
+ id: existing?.id ?? deployment.id,
3354
+ createdAt: existing?.createdAt ?? deployment.createdAt,
3355
+ updatedAt: deployment.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
3356
+ };
3357
+ this.entries.set(key5, next);
3358
+ return structuredClone(next);
3359
+ }
3360
+ /**
3361
+ * Applies a partial update to a deployment.
3362
+ *
3363
+ * `patch.createdAt` is ignored (immutable) and `updatedAt` is taken from the
3364
+ * patch when supplied, otherwise set to the current time — matching the
3365
+ * PostgreSQL store.
3366
+ *
3367
+ * @param tenantId - Owning tenant
3368
+ * @param pluginType - Plugin type key
3369
+ * @param patch - Mutable fields to apply
3370
+ * @returns Defensive copy of the updated deployment, or undefined when absent
3371
+ */
3372
+ async updateStatus(tenantId2, pluginType, patch) {
3373
+ const key5 = entryKey2(tenantId2, pluginType);
3374
+ const existing = this.entries.get(key5);
3375
+ if (!existing) return void 0;
3376
+ const next = {
3377
+ ...existing,
3378
+ ...patch,
3379
+ createdAt: existing.createdAt,
3380
+ updatedAt: patch.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
3381
+ };
3382
+ this.entries.set(key5, next);
3383
+ return structuredClone(next);
3384
+ }
3385
+ /**
3386
+ * Deletes a deployment by tenant + plugin type.
3387
+ *
3388
+ * @param tenantId - Owning tenant
3389
+ * @param pluginType - Plugin type key
3390
+ * @returns Whether a record was removed
3391
+ */
3392
+ async delete(tenantId2, pluginType) {
3393
+ return this.entries.delete(entryKey2(tenantId2, pluginType));
3394
+ }
3395
+ };
3396
+ function entryKey2(tenantId2, pluginType) {
3397
+ return JSON.stringify([tenantId2, pluginType]);
3398
+ }
3399
+
3310
3400
  // src/store_lattice/InMemoryUserStore.ts
3311
3401
  var InMemoryUserStore = class {
3312
3402
  constructor() {
@@ -4977,6 +5067,7 @@ var defaultProjectRoomReadStateStore = new InMemoryProjectRoomReadStateStore();
4977
5067
  var defaultUserPushSubscriptionStore = new InMemoryUserPushSubscriptionStore();
4978
5068
  var defaultLeaseStore = new InMemoryLeaseStore();
4979
5069
  var defaultOutboxStore = new InMemoryOutboxStore();
5070
+ var defaultPluginDeploymentStore = new InMemoryPluginDeploymentStore();
4980
5071
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
4981
5072
  storeLatticeManager.registerLattice(
4982
5073
  "default",
@@ -5000,6 +5091,7 @@ storeLatticeManager.registerLattice("default", "projectRoomReadState", defaultPr
5000
5091
  storeLatticeManager.registerLattice("default", "userPushSubscription", defaultUserPushSubscriptionStore);
5001
5092
  storeLatticeManager.registerLattice("default", "lease", defaultLeaseStore);
5002
5093
  storeLatticeManager.registerLattice("default", "outbox", defaultOutboxStore);
5094
+ storeLatticeManager.registerLattice("default", "pluginDeployment", defaultPluginDeploymentStore);
5003
5095
  var defaultUserStore = new InMemoryUserStore();
5004
5096
  var defaultTenantStore = new InMemoryTenantStore();
5005
5097
  var defaultUserTenantLinkStore = new InMemoryUserTenantLinkStore();
@@ -13062,6 +13154,82 @@ var evalPlugin = {
13062
13154
  })
13063
13155
  };
13064
13156
 
13157
+ // src/sandbox_plugin/residentTransport.ts
13158
+ var DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS = 6e4;
13159
+ function isRecord3(value) {
13160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13161
+ }
13162
+ function isEnvelope(value) {
13163
+ return isRecord3(value) && typeof value.ok === "boolean";
13164
+ }
13165
+ function errorFromBody(value) {
13166
+ if (isRecord3(value) && typeof value.error === "string" && value.error.length > 0) {
13167
+ return value.error;
13168
+ }
13169
+ return void 0;
13170
+ }
13171
+ async function readJson(response) {
13172
+ try {
13173
+ return await response.json();
13174
+ } catch {
13175
+ return void 0;
13176
+ }
13177
+ }
13178
+ async function invokeResident(args) {
13179
+ const {
13180
+ baseUrl,
13181
+ endpointPath,
13182
+ serviceKey,
13183
+ payload,
13184
+ timeoutMs = DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS
13185
+ } = args;
13186
+ const fetchImpl = args.fetchImpl ?? fetch;
13187
+ const url = `${baseUrl.replace(/\/$/, "")}${endpointPath}/invoke`;
13188
+ const headers = {
13189
+ "Content-Type": "application/json"
13190
+ };
13191
+ if (serviceKey) {
13192
+ headers["Authorization"] = `Bearer ${serviceKey}`;
13193
+ }
13194
+ const controller = new AbortController();
13195
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
13196
+ try {
13197
+ const response = await fetchImpl(url, {
13198
+ method: "POST",
13199
+ headers,
13200
+ body: JSON.stringify({ ...payload, timeoutMs: payload.timeoutMs ?? timeoutMs }),
13201
+ signal: controller.signal
13202
+ });
13203
+ const parsed = await readJson(response);
13204
+ if (!response.ok) {
13205
+ return {
13206
+ ok: false,
13207
+ error: errorFromBody(parsed) ?? `resident service responded with HTTP ${response.status}`,
13208
+ status: response.status
13209
+ };
13210
+ }
13211
+ if (!isEnvelope(parsed)) {
13212
+ return {
13213
+ ok: false,
13214
+ error: "resident service returned an invalid envelope",
13215
+ status: response.status
13216
+ };
13217
+ }
13218
+ if (!parsed.ok) {
13219
+ return {
13220
+ ok: false,
13221
+ error: errorFromBody(parsed) ?? "resident handler reported failure",
13222
+ status: response.status
13223
+ };
13224
+ }
13225
+ return { ok: true, result: parsed.result, status: response.status };
13226
+ } catch (error) {
13227
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
13228
+ } finally {
13229
+ clearTimeout(timer);
13230
+ }
13231
+ }
13232
+
13065
13233
  // src/sandbox_plugin/SandboxPluginExecutor.ts
13066
13234
  var PLUGINS_ROOT = "/root/.agents/plugins";
13067
13235
  var INLINE_INPUT_LIMIT_BYTES = 48 * 1024;
@@ -13074,11 +13242,11 @@ var MAX_MAX_RESULT_BYTES = 1048576;
13074
13242
  function encode(value) {
13075
13243
  return Buffer.from(JSON.stringify(value ?? null), "utf8").toString("base64");
13076
13244
  }
13077
- function isRecord3(value) {
13245
+ function isRecord4(value) {
13078
13246
  return typeof value === "object" && value !== null && !Array.isArray(value);
13079
13247
  }
13080
- function isEnvelope(value) {
13081
- return isRecord3(value) && typeof value.ok === "boolean";
13248
+ function isEnvelope2(value) {
13249
+ return isRecord4(value) && typeof value.ok === "boolean";
13082
13250
  }
13083
13251
  function clamp(value, min, max) {
13084
13252
  return Math.min(Math.max(value, min), max);
@@ -13099,7 +13267,7 @@ function parseEnvelope(stdout) {
13099
13267
  const trimmed = stdout.trim();
13100
13268
  try {
13101
13269
  const parsed = JSON.parse(trimmed || "null");
13102
- if (isEnvelope(parsed)) return parsed;
13270
+ if (isEnvelope2(parsed)) return parsed;
13103
13271
  } catch {
13104
13272
  }
13105
13273
  const lines = stdout.split("\n");
@@ -13108,7 +13276,7 @@ function parseEnvelope(stdout) {
13108
13276
  if (!line) continue;
13109
13277
  try {
13110
13278
  const parsed = JSON.parse(line);
13111
- if (isEnvelope(parsed)) return parsed;
13279
+ if (isEnvelope2(parsed)) return parsed;
13112
13280
  } catch {
13113
13281
  }
13114
13282
  }
@@ -13121,26 +13289,258 @@ function truncate(value, cap) {
13121
13289
  while (end > 0 && (buf[end] & 192) === 128) end -= 1;
13122
13290
  return buf.subarray(0, end).toString("utf8") + "\n...[truncated]";
13123
13291
  }
13292
+ function capResult(result, cap) {
13293
+ const serialized = typeof result === "string" ? result : JSON.stringify(result ?? null);
13294
+ return Buffer.byteLength(serialized, "utf8") <= cap ? result : truncate(serialized, cap);
13295
+ }
13124
13296
  function errorFromStdout(stdout) {
13125
13297
  let text = stdout;
13126
13298
  try {
13127
13299
  const parsed = JSON.parse(stdout);
13128
- if (isRecord3(parsed)) {
13300
+ if (isRecord4(parsed)) {
13129
13301
  text = JSON.stringify(sanitize(parsed)) ?? stdout;
13130
13302
  }
13131
13303
  } catch {
13132
13304
  }
13133
13305
  return text.slice(0, 4096);
13134
13306
  }
13307
+ function buildResidentPayload(args, timeoutMs) {
13308
+ return {
13309
+ handler: args.handler,
13310
+ input: args.input,
13311
+ ...args.connection === void 0 ? {} : { connection: args.connection },
13312
+ ...args.connections === void 0 ? {} : { connections: args.connections },
13313
+ context: args.context,
13314
+ timeoutMs
13315
+ };
13316
+ }
13135
13317
  var SandboxPluginExecutor = class {
13136
13318
  /**
13137
- * @param deps - Sandbox resolver and optional logger
13319
+ * @param deps - Sandbox resolver, optional logger, and optional resident routing
13138
13320
  */
13139
13321
  constructor(deps) {
13140
13322
  this.deps = deps;
13141
13323
  }
13142
13324
  /**
13143
- * Invoke a plugin handler inside the tenant sandbox shell.
13325
+ * Invoke a plugin handler.
13326
+ *
13327
+ * Routes to the published plugin's resident service when {@link
13328
+ * SandboxPluginExecutorDeps.resident} is configured and a deployment is
13329
+ * routable (`running` with an endpoint, or a resumed `onDemand` deployment).
13330
+ * Otherwise, or when the resident call fails and degradation is enabled, runs
13331
+ * the handler as a short `node <handler>` process inside the tenant sandbox.
13332
+ *
13333
+ * @param args - Invocation arguments including manifest, handler, and input
13334
+ * @returns The normalized handler result, exit code, timing, and raw stdout
13335
+ * @throws When `args.handler` is not a safe relative path
13336
+ */
13337
+ async invoke(args) {
13338
+ const started = Date.now();
13339
+ validateRelativePath(args.handler, "handler");
13340
+ if (this.deps.resident) {
13341
+ const resident = await this.tryResident(args, this.deps.resident, started);
13342
+ if (resident) return resident;
13343
+ }
13344
+ return this.invokeShortProcess(args, started);
13345
+ }
13346
+ /**
13347
+ * Attempt to route an invocation to a published plugin's resident service.
13348
+ *
13349
+ * @param args - Invocation arguments
13350
+ * @param resident - Resident routing dependencies
13351
+ * @param started - Invocation start timestamp used for `durationMs`
13352
+ * @returns A terminal result (success, or failure with degradation disabled),
13353
+ * or `undefined` when the caller should fall back to the short-process path
13354
+ */
13355
+ async tryResident(args, resident, started) {
13356
+ const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
13357
+ const pluginType = args.manifest.type;
13358
+ if (!tenantId2) return void 0;
13359
+ const deployment = await this.lookupDeployment(resident, tenantId2, pluginType);
13360
+ if (!deployment) return void 0;
13361
+ const timeoutMs = clamp(args.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
13362
+ const payload = buildResidentPayload(args, timeoutMs);
13363
+ if (deployment.status === "running" && deployment.endpoint) {
13364
+ let outcome = await this.callResident(resident, deployment.endpoint, payload, timeoutMs);
13365
+ if (!outcome.ok && deployment.mode === "onDemand" && resident.ensureRunning) {
13366
+ outcome = await this.ensureRunningAndRetry(
13367
+ resident,
13368
+ tenantId2,
13369
+ pluginType,
13370
+ deployment.endpoint,
13371
+ payload,
13372
+ timeoutMs,
13373
+ outcome
13374
+ );
13375
+ }
13376
+ return this.finalizeResident(outcome, args, resident, pluginType, tenantId2, started);
13377
+ }
13378
+ if (deployment.mode === "onDemand" && resident.ensureRunning) {
13379
+ const outcome = await this.wakeOnDemand(resident, tenantId2, pluginType, payload, timeoutMs);
13380
+ if (outcome) {
13381
+ return this.finalizeResident(outcome, args, resident, pluginType, tenantId2, started);
13382
+ }
13383
+ }
13384
+ return void 0;
13385
+ }
13386
+ /**
13387
+ * Normalize a resident invocation outcome into the caller-facing result.
13388
+ *
13389
+ * @param outcome - Normalized resident transport result
13390
+ * @param args - Invocation arguments (for the result-size cap)
13391
+ * @param resident - Resident routing dependencies (degradation policy)
13392
+ * @param pluginType - Plugin type, for diagnostics
13393
+ * @param tenantId - Tenant owning the plugin, for diagnostics
13394
+ * @param started - Invocation start timestamp used for `durationMs`
13395
+ * @returns The normalized result, or `undefined` when the caller should fall
13396
+ * back to the short-process path
13397
+ */
13398
+ finalizeResident(outcome, args, resident, pluginType, tenantId2, started) {
13399
+ if (outcome.ok) {
13400
+ const cap = clamp(
13401
+ args.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES,
13402
+ MIN_MAX_RESULT_BYTES,
13403
+ MAX_MAX_RESULT_BYTES
13404
+ );
13405
+ return {
13406
+ ok: true,
13407
+ result: capResult(outcome.result, cap),
13408
+ exitCode: 0,
13409
+ httpStatus: outcome.status,
13410
+ durationMs: Date.now() - started,
13411
+ stdout: "",
13412
+ stderr: ""
13413
+ };
13414
+ }
13415
+ const error = outcome.error ?? "resident plugin invocation failed";
13416
+ if (resident.degradeToShortProcess ?? true) {
13417
+ this.deps.logger?.info(
13418
+ { event: "sandbox-plugin.resident.degrade", pluginType, tenantId: tenantId2, error },
13419
+ "resident plugin invocation failed; falling back to short process"
13420
+ );
13421
+ return void 0;
13422
+ }
13423
+ return {
13424
+ ok: false,
13425
+ error,
13426
+ exitCode: 1,
13427
+ httpStatus: outcome.status,
13428
+ durationMs: Date.now() - started,
13429
+ stdout: "",
13430
+ stderr: ""
13431
+ };
13432
+ }
13433
+ /**
13434
+ * Look up a deployment, degrading to `undefined` when the lookup throws.
13435
+ *
13436
+ * @param resident - Resident routing dependencies
13437
+ * @param tenantId - Tenant owning the plugin
13438
+ * @param pluginType - Plugin type addressed by the invocation
13439
+ * @returns The deployment, or `undefined` when absent or lookup failed
13440
+ */
13441
+ async lookupDeployment(resident, tenantId2, pluginType) {
13442
+ try {
13443
+ return await resident.getDeployment(tenantId2, pluginType);
13444
+ } catch (error) {
13445
+ this.deps.logger?.info(
13446
+ {
13447
+ event: "sandbox-plugin.resident.lookup.failed",
13448
+ pluginType,
13449
+ tenantId: tenantId2,
13450
+ error: error instanceof Error ? error.message : String(error)
13451
+ },
13452
+ "resident deployment lookup failed; falling back to short process"
13453
+ );
13454
+ return void 0;
13455
+ }
13456
+ }
13457
+ /**
13458
+ * Resume a non-running `onDemand` deployment once, then invoke it.
13459
+ *
13460
+ * Re-reads the deployment after the resume so a cold-started endpoint is
13461
+ * picked up. Never throws: a failed resume or a deployment that is still not
13462
+ * `running` with an endpoint resolves to `undefined`, letting the caller use
13463
+ * the short-process path.
13464
+ *
13465
+ * @param resident - Resident routing dependencies
13466
+ * @param tenantId - Tenant owning the plugin
13467
+ * @param pluginType - Plugin type addressed by the invocation
13468
+ * @param payload - Invocation payload
13469
+ * @param timeoutMs - Clamped per-call timeout
13470
+ * @returns The resident outcome when routed, or `undefined` to short-process
13471
+ */
13472
+ async wakeOnDemand(resident, tenantId2, pluginType, payload, timeoutMs) {
13473
+ try {
13474
+ await resident.ensureRunning?.(tenantId2, pluginType);
13475
+ } catch (error) {
13476
+ this.deps.logger?.info(
13477
+ {
13478
+ event: "sandbox-plugin.resident.ensure.failed",
13479
+ pluginType,
13480
+ tenantId: tenantId2,
13481
+ error: error instanceof Error ? error.message : String(error)
13482
+ },
13483
+ "resident service resume failed; falling back to short process"
13484
+ );
13485
+ return void 0;
13486
+ }
13487
+ const refreshed = await this.lookupDeployment(resident, tenantId2, pluginType);
13488
+ if (!refreshed || refreshed.status !== "running" || !refreshed.endpoint) {
13489
+ return void 0;
13490
+ }
13491
+ return this.callResident(resident, refreshed.endpoint, payload, timeoutMs);
13492
+ }
13493
+ /**
13494
+ * Resume an `onDemand` deployment once and retry the resident call once.
13495
+ *
13496
+ * @param resident - Resident routing dependencies
13497
+ * @param tenantId - Tenant owning the plugin
13498
+ * @param pluginType - Plugin type addressed by the invocation
13499
+ * @param endpoint - Endpoint used by the first attempt
13500
+ * @param payload - Invocation payload
13501
+ * @param timeoutMs - Clamped per-call timeout
13502
+ * @param firstOutcome - Failed first attempt, returned when resume fails
13503
+ * @returns The retry outcome, or `firstOutcome` when resume itself failed
13504
+ */
13505
+ async ensureRunningAndRetry(resident, tenantId2, pluginType, endpoint, payload, timeoutMs, firstOutcome) {
13506
+ try {
13507
+ await resident.ensureRunning?.(tenantId2, pluginType);
13508
+ } catch (error) {
13509
+ this.deps.logger?.info(
13510
+ {
13511
+ event: "sandbox-plugin.resident.ensure.failed",
13512
+ pluginType,
13513
+ tenantId: tenantId2,
13514
+ error: error instanceof Error ? error.message : String(error)
13515
+ },
13516
+ "resident service resume failed"
13517
+ );
13518
+ return firstOutcome;
13519
+ }
13520
+ const refreshed = await this.lookupDeployment(resident, tenantId2, pluginType);
13521
+ return this.callResident(resident, refreshed?.endpoint ?? endpoint, payload, timeoutMs);
13522
+ }
13523
+ /**
13524
+ * Invoke the resident host over the sandbox-service proxy path.
13525
+ *
13526
+ * @param resident - Resident routing dependencies
13527
+ * @param endpointPath - Relative proxy path
13528
+ * @param payload - Invocation payload matching the host contract
13529
+ * @param timeoutMs - Clamped per-call timeout
13530
+ * @returns The normalized resident invocation result
13531
+ */
13532
+ callResident(resident, endpointPath, payload, timeoutMs) {
13533
+ return invokeResident({
13534
+ baseUrl: resident.baseUrl,
13535
+ endpointPath,
13536
+ serviceKey: resident.serviceKey,
13537
+ payload,
13538
+ timeoutMs,
13539
+ fetchImpl: resident.fetchImpl
13540
+ });
13541
+ }
13542
+ /**
13543
+ * Invoke a plugin handler as a short `node <handler>` process.
13144
13544
  *
13145
13545
  * Resolves the sandbox, injects small input as a base64 env var (or writes a
13146
13546
  * temp `input.json` for large input), writes connection credentials to a temp
@@ -13150,12 +13550,10 @@ var SandboxPluginExecutor = class {
13150
13550
  * in all cases, including setup failures.
13151
13551
  *
13152
13552
  * @param args - Invocation arguments including manifest, handler, and input
13553
+ * @param started - Invocation start timestamp used for `durationMs`
13153
13554
  * @returns The normalized handler result, exit code, timing, and raw stdout
13154
- * @throws When `args.handler` is not a safe relative path
13155
13555
  */
13156
- async invoke(args) {
13157
- const started = Date.now();
13158
- validateRelativePath(args.handler, "handler");
13556
+ async invokeShortProcess(args, started) {
13159
13557
  const pluginDir = `${PLUGINS_ROOT}/${args.manifest.type}`;
13160
13558
  const handlerAbs = `${pluginDir}/${args.handler}`;
13161
13559
  const tenantId2 = args.runConfig?.tenantId ?? args.context.tenantId;
@@ -13243,11 +13641,9 @@ var SandboxPluginExecutor = class {
13243
13641
  }
13244
13642
  const parsed = parseEnvelope(stdout);
13245
13643
  if (parsed) {
13246
- const result = parsed.ok ? parsed.result : void 0;
13247
- const serialized = typeof result === "string" ? result : JSON.stringify(result ?? null);
13248
13644
  return {
13249
13645
  ok: parsed.ok,
13250
- result: Buffer.byteLength(serialized, "utf8") <= cap ? result : truncate(serialized, cap),
13646
+ result: capResult(parsed.ok ? parsed.result : void 0, cap),
13251
13647
  error: typeof parsed.error === "string" ? parsed.error : void 0,
13252
13648
  exitCode,
13253
13649
  durationMs,
@@ -13258,7 +13654,7 @@ var SandboxPluginExecutor = class {
13258
13654
  const text = stdout.trim();
13259
13655
  return {
13260
13656
  ok: true,
13261
- result: truncate(text, cap),
13657
+ result: capResult(text, cap),
13262
13658
  exitCode,
13263
13659
  durationMs,
13264
13660
  stdout,
@@ -13275,24 +13671,33 @@ var SandboxPluginExecutor = class {
13275
13671
  }
13276
13672
  };
13277
13673
 
13674
+ // src/sandbox_plugin/pluginDeploymentServiceRuntime.ts
13675
+ var service2 = null;
13676
+ function setPluginDeploymentService(instance) {
13677
+ service2 = instance ?? null;
13678
+ }
13679
+ function getPluginDeploymentService() {
13680
+ return service2;
13681
+ }
13682
+
13278
13683
  // src/sandbox_plugin/TenantPluginCatalog.ts
13279
13684
  import { createHash as createHash3 } from "crypto";
13280
13685
 
13281
13686
  // src/sandbox_plugin/jsonSchemaToZod.ts
13282
13687
  import { z as z43 } from "zod";
13283
- function isRecord4(value) {
13688
+ function isRecord5(value) {
13284
13689
  return typeof value === "object" && value !== null && !Array.isArray(value);
13285
13690
  }
13286
13691
  function withDescription(schema6, raw) {
13287
- const description = isRecord4(raw) ? raw.description : void 0;
13692
+ const description = isRecord5(raw) ? raw.description : void 0;
13288
13693
  return typeof description === "string" ? schema6.describe(description) : schema6;
13289
13694
  }
13290
13695
  function jsonSchemaToZod(schema6) {
13291
- if (!isRecord4(schema6)) throw new Error("schema must be an object");
13696
+ if (!isRecord5(schema6)) throw new Error("schema must be an object");
13292
13697
  const type = schema6.type;
13293
13698
  switch (type) {
13294
13699
  case "object": {
13295
- const props = isRecord4(schema6.properties) ? schema6.properties : {};
13700
+ const props = isRecord5(schema6.properties) ? schema6.properties : {};
13296
13701
  const required = Array.isArray(schema6.required) ? schema6.required.filter((x) => typeof x === "string") : [];
13297
13702
  const shape = {};
13298
13703
  for (const [key5, sub] of Object.entries(props)) {
@@ -13332,7 +13737,7 @@ var DISPLAY_MODES = ["inline", "fullscreen", "pip"];
13332
13737
  var MAX_MANIFEST_BYTES = 262144;
13333
13738
  var MAX_TOOLS = 32;
13334
13739
  var MAX_PLUGINS_PER_TENANT = 50;
13335
- function isRecord5(value) {
13740
+ function isRecord6(value) {
13336
13741
  return typeof value === "object" && value !== null && !Array.isArray(value);
13337
13742
  }
13338
13743
  function isSafeHandler(path8) {
@@ -13357,7 +13762,7 @@ function validateSandboxPluginManifest(args) {
13357
13762
  const diagnostics = [];
13358
13763
  const error = (code, message, pluginType, tool68) => diagnostics.push({ level: "error", code, message, pluginType, tool: tool68 });
13359
13764
  const raw = args.raw;
13360
- if (!isRecord5(raw)) {
13765
+ if (!isRecord6(raw)) {
13361
13766
  error("MANIFEST_NOT_OBJECT", "manifest must be a JSON object", args.dirName);
13362
13767
  return { tools: [], diagnostics };
13363
13768
  }
@@ -13410,7 +13815,7 @@ function validateSandboxPluginManifest(args) {
13410
13815
  const tools = [];
13411
13816
  const seenToolNames = /* @__PURE__ */ new Set();
13412
13817
  for (const candidate of toolCandidates.slice(0, MAX_TOOLS)) {
13413
- if (!isRecord5(candidate)) {
13818
+ if (!isRecord6(candidate)) {
13414
13819
  error("TOOL_INVALID", "tool must be an object", type);
13415
13820
  continue;
13416
13821
  }
@@ -13427,7 +13832,7 @@ function validateSandboxPluginManifest(args) {
13427
13832
  error("HANDLER_MISSING", `handler "${String(candidate.handler)}" not found`, type, name);
13428
13833
  continue;
13429
13834
  }
13430
- if (!isRecord5(candidate.schema)) {
13835
+ if (!isRecord6(candidate.schema)) {
13431
13836
  error("TOOL_SCHEMA", `tool "${name}" schema must be an object`, type, name);
13432
13837
  continue;
13433
13838
  }
@@ -13441,17 +13846,17 @@ function validateSandboxPluginManifest(args) {
13441
13846
  let ui;
13442
13847
  if (candidate.ui !== void 0) {
13443
13848
  const rawUi = candidate.ui;
13444
- if (!isRecord5(rawUi) || !isSafeUiPath(rawUi.resource) || !args.handlerExists(rawUi.resource)) {
13849
+ if (!isRecord6(rawUi) || !isSafeUiPath(rawUi.resource) || !args.handlerExists(rawUi.resource)) {
13445
13850
  error("TOOL_UI", `tool "${name}" ui.resource must be an existing .html file within the plugin dir`, type, name);
13446
13851
  } else {
13447
- const csp = isRecord5(rawUi.csp) ? {
13852
+ const csp = isRecord6(rawUi.csp) ? {
13448
13853
  connectDomains: Array.isArray(rawUi.csp.connectDomains) ? rawUi.csp.connectDomains.filter((d) => typeof d === "string") : void 0,
13449
13854
  resourceDomains: Array.isArray(rawUi.csp.resourceDomains) ? rawUi.csp.resourceDomains.filter((d) => typeof d === "string") : void 0,
13450
13855
  frameDomains: Array.isArray(rawUi.csp.frameDomains) ? rawUi.csp.frameDomains.filter((d) => typeof d === "string") : void 0,
13451
13856
  baseUriDomains: Array.isArray(rawUi.csp.baseUriDomains) ? rawUi.csp.baseUriDomains.filter((d) => typeof d === "string") : void 0
13452
13857
  } : void 0;
13453
13858
  const displayMode = DISPLAY_MODES.includes(rawUi.displayMode) ? rawUi.displayMode : void 0;
13454
- const permissions = isRecord5(rawUi.permissions) ? {
13859
+ const permissions = isRecord6(rawUi.permissions) ? {
13455
13860
  camera: rawUi.permissions.camera === true ? true : void 0,
13456
13861
  microphone: rawUi.permissions.microphone === true ? true : void 0,
13457
13862
  geolocation: rawUi.permissions.geolocation === true ? true : void 0,
@@ -13481,7 +13886,7 @@ function validateSandboxPluginManifest(args) {
13481
13886
  }
13482
13887
  const skills = {};
13483
13888
  if (raw.skills !== void 0) {
13484
- if (!isRecord5(raw.skills)) {
13889
+ if (!isRecord6(raw.skills)) {
13485
13890
  error("SKILLS_INVALID", "skills must be an object", type);
13486
13891
  } else {
13487
13892
  const prefix = `${type}-`;
@@ -13496,12 +13901,12 @@ function validateSandboxPluginManifest(args) {
13496
13901
  error("SKILL_NAME", `skill "${name}" must be a safe relative path prefixed with "${prefix}"`, type);
13497
13902
  continue;
13498
13903
  }
13499
- if (!isRecord5(definition) || typeof definition.version !== "string" || typeof definition.content !== "string") {
13904
+ if (!isRecord6(definition) || typeof definition.version !== "string" || typeof definition.content !== "string") {
13500
13905
  error("SKILL_INVALID", `skill "${name}" requires string version and content`, type);
13501
13906
  continue;
13502
13907
  }
13503
13908
  if (definition.resources !== void 0) {
13504
- if (!isRecord5(definition.resources)) {
13909
+ if (!isRecord6(definition.resources)) {
13505
13910
  error("SKILL_RESOURCES", `skill "${name}" resources must be an object`, type);
13506
13911
  continue;
13507
13912
  }
@@ -13513,7 +13918,7 @@ function validateSandboxPluginManifest(args) {
13513
13918
  resourcesOk = false;
13514
13919
  break;
13515
13920
  }
13516
- if (!isRecord5(resource) || typeof resource.content !== "string") {
13921
+ if (!isRecord6(resource) || typeof resource.content !== "string") {
13517
13922
  resourcesOk = false;
13518
13923
  break;
13519
13924
  }
@@ -13529,11 +13934,11 @@ function validateSandboxPluginManifest(args) {
13529
13934
  }
13530
13935
  const agents = {};
13531
13936
  if (raw.agents !== void 0) {
13532
- if (!isRecord5(raw.agents)) {
13937
+ if (!isRecord6(raw.agents)) {
13533
13938
  error("AGENTS_INVALID", "agents must be an object", type);
13534
13939
  } else {
13535
13940
  for (const [key5, config] of Object.entries(raw.agents)) {
13536
- if (!isRecord5(config)) {
13941
+ if (!isRecord6(config)) {
13537
13942
  error("AGENT_INVALID", `agent "${key5}" must be an object`, type);
13538
13943
  continue;
13539
13944
  }
@@ -13554,7 +13959,7 @@ function validateSandboxPluginManifest(args) {
13554
13959
  }
13555
13960
  }
13556
13961
  let connection;
13557
- if (isRecord5(raw.connection)) {
13962
+ if (isRecord6(raw.connection)) {
13558
13963
  const fields = raw.connection.fields;
13559
13964
  if (!Array.isArray(fields)) {
13560
13965
  error("CONNECTION_INVALID", "connection.fields must be an array", type);
@@ -13568,7 +13973,7 @@ function validateSandboxPluginManifest(args) {
13568
13973
  for (const key5 of ["test", "discover"]) {
13569
13974
  const entry = raw.connection[key5];
13570
13975
  if (entry === void 0) continue;
13571
- const handler = isRecord5(entry) ? entry.handler : void 0;
13976
+ const handler = isRecord6(entry) ? entry.handler : void 0;
13572
13977
  if (!isSafeHandler(handler) || !args.handlerExists(handler)) {
13573
13978
  error("CONNECTION_HANDLER", `connection.${key5} handler "${String(handler)}" not found`, type);
13574
13979
  continue;
@@ -13586,8 +13991,8 @@ function validateSandboxPluginManifest(args) {
13586
13991
  version: raw.version,
13587
13992
  icon: typeof raw.icon === "string" ? raw.icon : void 0,
13588
13993
  category: typeof raw.category === "string" ? raw.category : void 0,
13589
- configSchema: isRecord5(raw.configSchema) ? raw.configSchema : void 0,
13590
- defaultConfig: isRecord5(raw.defaultConfig) ? raw.defaultConfig : void 0,
13994
+ configSchema: isRecord6(raw.configSchema) ? raw.configSchema : void 0,
13995
+ defaultConfig: isRecord6(raw.defaultConfig) ? raw.defaultConfig : void 0,
13591
13996
  connection,
13592
13997
  tools,
13593
13998
  skills: Object.keys(skills).length > 0 ? skills : void 0,
@@ -13767,7 +14172,7 @@ var STANDARD_CONNECTION_DEFAULT_CONFIG = Object.freeze({
13767
14172
  connectAll: false
13768
14173
  });
13769
14174
  var EMPTY_DEFAULT_CONFIG = Object.freeze({});
13770
- function isRecord6(value) {
14175
+ function isRecord7(value) {
13771
14176
  return typeof value === "object" && value !== null && !Array.isArray(value);
13772
14177
  }
13773
14178
  function runConfigFrom(exeConfig) {
@@ -13857,11 +14262,11 @@ async function runConnectionHandler(executor, manifest, handler, config, tenantI
13857
14262
  return { ok: false, message: result.error ?? "connection test failed" };
13858
14263
  }
13859
14264
  const value = result.result;
13860
- if (isRecord6(value) && typeof value.ok === "boolean" && typeof value.message === "string") {
14265
+ if (isRecord7(value) && typeof value.ok === "boolean" && typeof value.message === "string") {
13861
14266
  return {
13862
14267
  ok: value.ok,
13863
14268
  message: value.message,
13864
- details: isRecord6(value.details) ? value.details : void 0
14269
+ details: isRecord7(value.details) ? value.details : void 0
13865
14270
  };
13866
14271
  }
13867
14272
  return { ok: false, message: result.error ?? "connection test returned an invalid result" };
@@ -14214,13 +14619,29 @@ var TenantPluginCatalog = class {
14214
14619
 
14215
14620
  // src/sandbox_plugin/sandboxPluginRuntime.ts
14216
14621
  var catalog = null;
14622
+ function buildResidentRouting() {
14623
+ const baseUrl = process.env.MICROSANDBOX_SERVICE_URL;
14624
+ if (!baseUrl) return void 0;
14625
+ return {
14626
+ getDeployment: async (tenantId2, pluginType) => await getPluginDeploymentService()?.getDeployment(tenantId2, pluginType) ?? void 0,
14627
+ ensureRunning: async (tenantId2, pluginType) => {
14628
+ await getPluginDeploymentService()?.ensureRunning(tenantId2, pluginType);
14629
+ },
14630
+ baseUrl,
14631
+ serviceKey: process.env.MICROSANDBOX_API_KEY
14632
+ };
14633
+ }
14634
+ function buildExecutor(resolveSandbox) {
14635
+ const resident = buildResidentRouting();
14636
+ return new SandboxPluginExecutor({ resolveSandbox, ...resident ? { resident } : {} });
14637
+ }
14217
14638
  function emptyCatalog() {
14218
14639
  const resolveSandbox = async () => {
14219
14640
  throw new Error("sandbox manager not configured");
14220
14641
  };
14221
14642
  return new TenantPluginCatalog({
14222
14643
  resolveSandbox,
14223
- executor: new SandboxPluginExecutor({ resolveSandbox }),
14644
+ executor: buildExecutor(resolveSandbox),
14224
14645
  reservedTypes: () => new Set(PluginRegistry.list())
14225
14646
  });
14226
14647
  }
@@ -14237,7 +14658,7 @@ function getSandboxPluginCatalog() {
14237
14658
  const resolveSandbox = createSandboxResolver((config) => manager.getSandboxFromConfig(config));
14238
14659
  catalog = new TenantPluginCatalog({
14239
14660
  resolveSandbox,
14240
- executor: new SandboxPluginExecutor({ resolveSandbox }),
14661
+ executor: buildExecutor(resolveSandbox),
14241
14662
  reservedTypes: () => new Set(PluginRegistry.list())
14242
14663
  });
14243
14664
  return catalog;
@@ -14384,23 +14805,23 @@ function toolName(tool68) {
14384
14805
  return typeof tool68.name === "string" ? tool68.name : void 0;
14385
14806
  }
14386
14807
  function schemaHasConnectionKey(schema6) {
14387
- const definition = isRecord7(schema6._def) ? schema6._def : void 0;
14808
+ const definition = isRecord8(schema6._def) ? schema6._def : void 0;
14388
14809
  const shape = definition?.shape;
14389
14810
  const fields = typeof shape === "function" ? shape() : shape;
14390
- return isRecord7(fields) && Object.prototype.hasOwnProperty.call(fields, "connectionKey");
14811
+ return isRecord8(fields) && Object.prototype.hasOwnProperty.call(fields, "connectionKey");
14391
14812
  }
14392
14813
  function hasConnectionKeySchema(tool68) {
14393
- if (!isRecord7(tool68) || !isRecord7(tool68.schema)) return false;
14814
+ if (!isRecord8(tool68) || !isRecord8(tool68.schema)) return false;
14394
14815
  if (schemaHasConnectionKey(tool68.schema)) return true;
14395
- const definition = isRecord7(tool68.schema._def) ? tool68.schema._def : void 0;
14816
+ const definition = isRecord8(tool68.schema._def) ? tool68.schema._def : void 0;
14396
14817
  if (Array.isArray(definition?.options) && definition.options.length > 0) {
14397
14818
  return definition.options.every(
14398
- (option) => isRecord7(option) && schemaHasConnectionKey(option)
14819
+ (option) => isRecord8(option) && schemaHasConnectionKey(option)
14399
14820
  );
14400
14821
  }
14401
14822
  return false;
14402
14823
  }
14403
- function isRecord7(value) {
14824
+ function isRecord8(value) {
14404
14825
  return typeof value === "object" && value !== null && !Array.isArray(value);
14405
14826
  }
14406
14827
  var CapabilityToolRegistry = class {
@@ -14492,17 +14913,17 @@ var CapabilityToolRegistry = class {
14492
14913
  // src/middlewares/capabilityGuardMiddleware.ts
14493
14914
  import { ToolMessage as ToolMessage5 } from "@langchain/core/messages";
14494
14915
  import { createMiddleware as createMiddleware13 } from "langchain";
14495
- function isRecord8(value) {
14916
+ function isRecord9(value) {
14496
14917
  return typeof value === "object" && value !== null && !Array.isArray(value);
14497
14918
  }
14498
14919
  function capabilityRuntimeFromRequest(request) {
14499
- if (!isRecord8(request) || !isRecord8(request.runtime)) return void 0;
14920
+ if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
14500
14921
  const configurable = request.runtime.configurable;
14501
14922
  const context = request.runtime.context;
14502
- const runConfig = isRecord8(configurable) && isRecord8(configurable.runConfig) ? configurable.runConfig : isRecord8(context) && isRecord8(context.runConfig) ? context.runConfig : void 0;
14923
+ const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
14503
14924
  if (!runConfig) return void 0;
14504
14925
  const value = runConfig.capabilityRuntime;
14505
- if (!isRecord8(value) || typeof value.revision !== "string" || !isRecord8(value.middleware)) {
14926
+ if (!isRecord9(value) || typeof value.revision !== "string" || !isRecord9(value.middleware)) {
14506
14927
  return void 0;
14507
14928
  }
14508
14929
  return value;
@@ -14514,10 +14935,10 @@ function hasActiveRuntimePolicy(runtime) {
14514
14935
  return Object.keys(runtime.middleware).length > 0;
14515
14936
  }
14516
14937
  function tenantIdFromRequest(request) {
14517
- if (!isRecord8(request) || !isRecord8(request.runtime)) return void 0;
14938
+ if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
14518
14939
  const configurable = request.runtime.configurable;
14519
14940
  const context = request.runtime.context;
14520
- const runConfig = isRecord8(configurable) && isRecord8(configurable.runConfig) ? configurable.runConfig : isRecord8(context) && isRecord8(context.runConfig) ? context.runConfig : void 0;
14941
+ const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
14521
14942
  return runConfig && typeof runConfig.tenantId === "string" ? runConfig.tenantId : void 0;
14522
14943
  }
14523
14944
  async function standardConnectionAllowed(request, capabilityType, config) {
@@ -14525,8 +14946,8 @@ async function standardConnectionAllowed(request, capabilityType, config) {
14525
14946
  const hasConnectAll = Object.prototype.hasOwnProperty.call(config, "connectAll");
14526
14947
  if (!hasConnections && (!hasConnectAll || config.connectAll !== true)) return false;
14527
14948
  const tenantId2 = tenantIdFromRequest(request);
14528
- const resource = isRecord8(request.toolCall) && isRecord8(request.toolCall.args) ? request.toolCall.args.connectionKey : void 0;
14529
- const hasConnectionKey = isRecord8(request.toolCall) && isRecord8(request.toolCall.args) && Object.prototype.hasOwnProperty.call(
14949
+ const resource = isRecord9(request.toolCall) && isRecord9(request.toolCall.args) ? request.toolCall.args.connectionKey : void 0;
14950
+ const hasConnectionKey = isRecord9(request.toolCall) && isRecord9(request.toolCall.args) && Object.prototype.hasOwnProperty.call(
14530
14951
  request.toolCall.args,
14531
14952
  "connectionKey"
14532
14953
  );
@@ -14592,10 +15013,10 @@ function createCapabilityGuardMiddleware(registry4) {
14592
15013
  toolName: toolName2
14593
15014
  });
14594
15015
  }
14595
- const standardConfig = isRecord8(policy.config) ? policy.config : {};
15016
+ const standardConfig = isRecord9(policy.config) ? policy.config : {};
14596
15017
  if (registry4.isStandardConnectionTool(toolName2) && !await standardConnectionAllowed(request, metadata.capabilityType, standardConfig)) {
14597
15018
  const args2 = request.toolCall.args;
14598
- const resource = isRecord8(args2) && typeof args2.connectionKey === "string" ? args2.connectionKey : void 0;
15019
+ const resource = isRecord9(args2) && typeof args2.connectionKey === "string" ? args2.connectionKey : void 0;
14599
15020
  return denialMessage(request.toolCall, {
14600
15021
  code: "RESOURCE_NOT_ALLOWED",
14601
15022
  message: typeof resource === "string" ? `Resource '${resource}' is not allowed by the capability policy` : "Resource selector 'connectionKey' must be a permitted string",
@@ -14608,8 +15029,8 @@ function createCapabilityGuardMiddleware(registry4) {
14608
15029
  const selector = metadata.resourceSelector;
14609
15030
  const args = request.toolCall.args;
14610
15031
  if (selector) {
14611
- const resource = isRecord8(args) ? args[selector.argumentField] : void 0;
14612
- const config = isRecord8(policy.config) ? policy.config : {};
15032
+ const resource = isRecord9(args) ? args[selector.argumentField] : void 0;
15033
+ const config = isRecord9(policy.config) ? policy.config : {};
14613
15034
  const hasExplicitResources = Object.prototype.hasOwnProperty.call(config, selector.configField);
14614
15035
  const allowsAll = !hasExplicitResources && selector.allowAllField !== void 0 && config[selector.allowAllField] === true;
14615
15036
  const allowedResources = config[selector.configField];
@@ -30396,6 +30817,7 @@ function assistantToConfig(assistant) {
30396
30817
  prompt: typeof assistant.graphDefinition === "string" ? assistant.graphDefinition : JSON.stringify(assistant.graphDefinition)
30397
30818
  };
30398
30819
  Object.assign(config, graphDef);
30820
+ if (assistant.ownerUserId !== void 0) config.ownerUserId = assistant.ownerUserId;
30399
30821
  return config;
30400
30822
  }
30401
30823
  var _AgentLatticeManager = class _AgentLatticeManager extends BaseLatticeManager {
@@ -32788,6 +33210,9 @@ var MicrosandboxRemoteInstance = class {
32788
33210
  }
32789
33211
  return "unknown";
32790
33212
  }
33213
+ async getEndpoint(port) {
33214
+ return this.client.getEndpoint(this.name, port);
33215
+ }
32791
33216
  /**
32792
33217
  * Pack a sandbox folder into a sibling ZIP through the microsandbox shell.
32793
33218
  *
@@ -32854,6 +33279,19 @@ var MicrosandboxServiceClient = class {
32854
33279
  method: "GET"
32855
33280
  });
32856
33281
  }
33282
+ /**
33283
+ * Resolve the endpoint for a published guest port of a sandbox.
33284
+ *
33285
+ * @param name - Sandbox name
33286
+ * @param port - Guest port published via `ensureSandbox({ exposePorts })`
33287
+ * @returns The scheme-less endpoint string and optional headers
33288
+ */
33289
+ async getEndpoint(name, port) {
33290
+ return this.request(
33291
+ `/api/sandboxes/${encodeURIComponent(name)}/endpoints/${port}`,
33292
+ { method: "GET" }
33293
+ );
33294
+ }
32857
33295
  async readFile(sandboxName, path8) {
32858
33296
  return this.request("/api/files/read", {
32859
33297
  method: "POST",
@@ -33118,9 +33556,9 @@ var MicrosandboxRemoteProvider = class {
33118
33556
  }
33119
33557
  buildEnsureInput(config) {
33120
33558
  const defaultMicrosandboxRemoteConfig = getDefaultMicrosandboxRemoteConfig();
33121
- const image = this.config.image ?? defaultMicrosandboxRemoteConfig.image;
33559
+ const image = config.image ?? this.config.image ?? defaultMicrosandboxRemoteConfig.image;
33122
33560
  console.log(
33123
- `[MicrosandboxRemote] buildEnsureInput image=${image} from=${this.config.image ? "providerConfig" : "defaultConfig"}`
33561
+ `[MicrosandboxRemote] buildEnsureInput image=${image} from=${config.image ? "runConfig" : this.config.image ? "providerConfig" : "defaultConfig"}`
33124
33562
  );
33125
33563
  return {
33126
33564
  image,
@@ -33130,7 +33568,10 @@ var MicrosandboxRemoteProvider = class {
33130
33568
  ...this.config.env,
33131
33569
  ...buildSandboxMetadataEnv(config)
33132
33570
  },
33133
- volumes: config?.volumes ?? this.buildDefaultVolumes(config)
33571
+ volumes: config?.volumes ?? this.buildDefaultVolumes(config),
33572
+ entrypoint: config.entrypoint,
33573
+ timeoutSeconds: config.timeoutSeconds,
33574
+ exposePorts: config.exposePorts
33134
33575
  };
33135
33576
  }
33136
33577
  buildDefaultVolumes(config) {
@@ -34856,6 +35297,157 @@ async function connectAllChannels(getAdapter, options = {}) {
34856
35297
  }
34857
35298
  }
34858
35299
  }
35300
+ async function disconnectAllChannels(getAdapter) {
35301
+ const store = getStoreLattice("default", "channelInstallation").store;
35302
+ const installations = await store.getAllInstallations();
35303
+ console.log(`[disconnectAllChannels] total=${installations.length} enabled=${installations.filter((i) => i.enabled).length}`);
35304
+ for (const inst of installations) {
35305
+ if (!inst.enabled) continue;
35306
+ const adapter = getAdapter(inst.channel);
35307
+ if (!adapter?.disconnect) continue;
35308
+ try {
35309
+ await adapter.disconnect(inst.id);
35310
+ } catch (error) {
35311
+ console.error(`[disconnectAllChannels] disconnect failed: channel=${inst.channel} tenant=${inst.tenantId} id=${inst.id}`, error);
35312
+ }
35313
+ }
35314
+ }
35315
+
35316
+ // src/channel/ChannelConnectionLeader.ts
35317
+ import { randomUUID as randomUUID8 } from "crypto";
35318
+ var LEASE_KEY = "leader:channel-connections";
35319
+ var ChannelConnectionLeader = class {
35320
+ constructor(deps) {
35321
+ this.holder = null;
35322
+ this.connected = false;
35323
+ this.attempted = false;
35324
+ this.inFlight = null;
35325
+ this.timer = null;
35326
+ this.running = false;
35327
+ this.stopped = false;
35328
+ this.deps = deps;
35329
+ this.ttlMs = deps.ttlMs ?? 6e4;
35330
+ this.retryMs = deps.retryMs ?? Math.max(5e3, Math.floor(this.ttlMs / 3));
35331
+ }
35332
+ /** Starts the ownership loop (immediate tick, then every `retryMs`). */
35333
+ start() {
35334
+ if (this.timer || this.stopped) return;
35335
+ void this.tickOnce();
35336
+ this.timer = setInterval(() => {
35337
+ void this.tickOnce();
35338
+ }, this.retryMs);
35339
+ this.timer.unref?.();
35340
+ }
35341
+ /**
35342
+ * Releases ownership (if held) and disconnects.
35343
+ *
35344
+ * @returns A promise that settles after teardown completes.
35345
+ * @remarks Terminal: a stopped leader does not restart.
35346
+ */
35347
+ async stop() {
35348
+ this.stopped = true;
35349
+ if (this.timer) {
35350
+ clearInterval(this.timer);
35351
+ this.timer = null;
35352
+ }
35353
+ if (this.inFlight) await this.inFlight.catch(() => void 0);
35354
+ if (this.holder) {
35355
+ const holder = this.holder;
35356
+ this.holder = null;
35357
+ await this.deps.leaseStore.release(LEASE_KEY, holder).catch((error) => {
35358
+ this.deps.log?.warn("Channel leader lease release failed", {
35359
+ error: error instanceof Error ? error.message : String(error)
35360
+ });
35361
+ });
35362
+ }
35363
+ await this.disconnectChannels();
35364
+ }
35365
+ /**
35366
+ * Runs one ownership/health step. Public for deterministic tests.
35367
+ *
35368
+ * @returns A promise that settles when this step completes.
35369
+ */
35370
+ async tickOnce() {
35371
+ if (this.running || this.stopped) return;
35372
+ this.running = true;
35373
+ const run = this.runTick();
35374
+ this.inFlight = run;
35375
+ try {
35376
+ await run;
35377
+ } finally {
35378
+ if (this.inFlight === run) this.inFlight = null;
35379
+ this.running = false;
35380
+ }
35381
+ }
35382
+ /**
35383
+ * Performs one acquire/renew/step decision.
35384
+ *
35385
+ * @remarks A transient lease-store error while renewing keeps the current
35386
+ * holder and connections intact (retry next tick); only a confirmed
35387
+ * `renewed === false` triggers teardown.
35388
+ */
35389
+ async runTick() {
35390
+ if (this.holder) {
35391
+ let renewed = false;
35392
+ let renewFailed = false;
35393
+ try {
35394
+ renewed = await this.deps.leaseStore.renew(LEASE_KEY, this.holder, this.ttlMs);
35395
+ } catch (error) {
35396
+ renewFailed = true;
35397
+ this.deps.log?.warn("Channel leader lease renew failed", {
35398
+ error: error instanceof Error ? error.message : String(error)
35399
+ });
35400
+ }
35401
+ if (renewFailed) return;
35402
+ if (renewed) {
35403
+ if (!this.connected) await this.connectChannels();
35404
+ return;
35405
+ }
35406
+ await this.loseLeadership();
35407
+ }
35408
+ await this.tryAcquire();
35409
+ }
35410
+ async tryAcquire() {
35411
+ const holder = `${this.deps.owner}#${randomUUID8()}`;
35412
+ const acquired = await this.deps.leaseStore.tryAcquire(LEASE_KEY, holder, this.ttlMs).catch((error) => {
35413
+ this.deps.log?.warn("Channel leader lease acquire failed", {
35414
+ error: error instanceof Error ? error.message : String(error)
35415
+ });
35416
+ return false;
35417
+ });
35418
+ if (!acquired) return;
35419
+ this.holder = holder;
35420
+ await this.connectChannels();
35421
+ }
35422
+ async loseLeadership() {
35423
+ this.holder = null;
35424
+ await this.disconnectChannels();
35425
+ }
35426
+ async connectChannels() {
35427
+ this.attempted = true;
35428
+ try {
35429
+ await this.deps.connect();
35430
+ this.connected = true;
35431
+ } catch (error) {
35432
+ this.connected = false;
35433
+ this.deps.log?.warn("Channel leader connect failed", {
35434
+ error: error instanceof Error ? error.message : String(error)
35435
+ });
35436
+ }
35437
+ }
35438
+ async disconnectChannels() {
35439
+ if (!this.attempted) return;
35440
+ this.attempted = false;
35441
+ this.connected = false;
35442
+ try {
35443
+ await this.deps.disconnect();
35444
+ } catch (error) {
35445
+ this.deps.log?.warn("Channel leader disconnect failed", {
35446
+ error: error instanceof Error ? error.message : String(error)
35447
+ });
35448
+ }
35449
+ }
35450
+ };
34859
35451
 
34860
35452
  // src/eval_lattice/LatticeEval.ts
34861
35453
  import { HumanMessage as HumanMessage5 } from "@langchain/core/messages";
@@ -36316,7 +36908,7 @@ function getCoordinationLeaseStore() {
36316
36908
  }
36317
36909
 
36318
36910
  // src/services/withLease.ts
36319
- import { randomUUID as randomUUID8 } from "crypto";
36911
+ import { randomUUID as randomUUID9 } from "crypto";
36320
36912
  var LeaseAcquisitionTimeoutError = class extends Error {
36321
36913
  /**
36322
36914
  * @param key Lease key that could not be acquired.
@@ -36341,7 +36933,7 @@ async function withLease(store, key5, ownerLabel, fn, options = {}) {
36341
36933
  const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
36342
36934
  const retryDelayMs = options.retryDelayMs ?? 50;
36343
36935
  const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
36344
- const holder = `${ownerLabel}#${randomUUID8()}`;
36936
+ const holder = `${ownerLabel}#${randomUUID9()}`;
36345
36937
  const deadline = Date.now() + acquireTimeoutMs;
36346
36938
  for (; ; ) {
36347
36939
  if (await store.tryAcquire(key5, holder, ttlMs)) break;
@@ -36440,7 +37032,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
36440
37032
  }
36441
37033
 
36442
37034
  // src/services/RoomAgentMessageService.ts
36443
- import { randomUUID as randomUUID9 } from "crypto";
37035
+ import { randomUUID as randomUUID10 } from "crypto";
36444
37036
  import { snapshotExactArray, snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
36445
37037
  var INPUT_KEYS = [
36446
37038
  "tenantId",
@@ -36528,7 +37120,7 @@ var RoomAgentMessageService = class {
36528
37120
  throw new Error("Invalid project room membership");
36529
37121
  }
36530
37122
  const candidate = {
36531
- id: (this.deps.idFactory ?? randomUUID9)(),
37123
+ id: (this.deps.idFactory ?? randomUUID10)(),
36532
37124
  tenantId: input.tenantId,
36533
37125
  workspaceId: input.workspaceId,
36534
37126
  projectId: input.projectId,
@@ -39115,7 +39707,7 @@ function createTaskLifecycleService(deps) {
39115
39707
  }
39116
39708
 
39117
39709
  // src/services/TaskExecutionService.ts
39118
- import { randomUUID as randomUUID11 } from "crypto";
39710
+ import { randomUUID as randomUUID12 } from "crypto";
39119
39711
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
39120
39712
  import { isExecutionResultEventKey as isExecutionResultEventKey2 } from "@axiom-lattice/protocols";
39121
39713
 
@@ -39438,13 +40030,13 @@ async function reconcileAllTenantTaskRuns(input) {
39438
40030
  }
39439
40031
 
39440
40032
  // src/services/taskRunLease.ts
39441
- import { randomUUID as randomUUID10 } from "crypto";
40033
+ import { randomUUID as randomUUID11 } from "crypto";
39442
40034
  function taskRunLeaseKey(taskId) {
39443
40035
  return `task-run:${taskId}`;
39444
40036
  }
39445
40037
  async function tryAcquireTaskRun(input) {
39446
40038
  const key5 = taskRunLeaseKey(input.taskId);
39447
- const holder = `${input.owner}#${randomUUID10()}`;
40039
+ const holder = `${input.owner}#${randomUUID11()}`;
39448
40040
  if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
39449
40041
  const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
39450
40042
  let released = false;
@@ -39811,11 +40403,11 @@ function createTaskExecutionService(deps) {
39811
40403
  let threadId;
39812
40404
  let runId;
39813
40405
  try {
39814
- threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID11)();
40406
+ threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID12)();
39815
40407
  if (task.status === "interrupted" && reuseCurrentRun) {
39816
40408
  runId = storedRunId;
39817
40409
  } else if (task.status === "interrupted" || task.status === "failed") {
39818
- runId = (deps.createRunId ?? randomUUID11)();
40410
+ runId = (deps.createRunId ?? randomUUID12)();
39819
40411
  }
39820
40412
  } catch (error) {
39821
40413
  return {
@@ -40074,11 +40666,11 @@ function createTaskExecutionService(deps) {
40074
40666
  }
40075
40667
 
40076
40668
  // src/services/leadership.ts
40077
- import { randomUUID as randomUUID12 } from "crypto";
40669
+ import { randomUUID as randomUUID13 } from "crypto";
40078
40670
  async function runAsLeader(store, name, fn, options = {}) {
40079
40671
  const key5 = `leader:${name}`;
40080
40672
  const owner2 = options.owner ?? getReplicaId();
40081
- const holder = `${owner2}#${randomUUID12()}`;
40673
+ const holder = `${owner2}#${randomUUID13()}`;
40082
40674
  const ttlMs = options.ttlMs ?? 6e4;
40083
40675
  if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
40084
40676
  const heartbeat = setInterval(() => {
@@ -41311,16 +41903,16 @@ function createAskUserToClarifyTool() {
41311
41903
  }
41312
41904
 
41313
41905
  // src/middlewares/askUserClarifyMiddleware.ts
41314
- function isRecord9(value) {
41906
+ function isRecord10(value) {
41315
41907
  return typeof value === "object" && value !== null && !Array.isArray(value);
41316
41908
  }
41317
41909
  function isProjectRoomRun(request) {
41318
- if (!isRecord9(request) || !isRecord9(request.runtime)) return false;
41910
+ if (!isRecord10(request) || !isRecord10(request.runtime)) return false;
41319
41911
  const runtime = request.runtime;
41320
- const configurable = isRecord9(runtime.configurable) ? runtime.configurable.runConfig : void 0;
41321
- const context = isRecord9(runtime.context) ? runtime.context.runConfig : void 0;
41322
- const runConfig = isRecord9(configurable) ? configurable : isRecord9(context) ? context : void 0;
41323
- return isRecord9(runConfig?.projectRoom);
41912
+ const configurable = isRecord10(runtime.configurable) ? runtime.configurable.runConfig : void 0;
41913
+ const context = isRecord10(runtime.context) ? runtime.context.runConfig : void 0;
41914
+ const runConfig = isRecord10(configurable) ? configurable : isRecord10(context) ? context : void 0;
41915
+ return isRecord10(runConfig?.projectRoom);
41324
41916
  }
41325
41917
  function createAskUserClarifyMiddleware() {
41326
41918
  return createMiddleware21({
@@ -42765,15 +43357,16 @@ function executorString(value) {
42765
43357
  }
42766
43358
  async function renderAvailableExecutors(rc, tenantId2) {
42767
43359
  const authority = classifyTaskAuthority(rc);
42768
- const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
42769
- const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
42770
- const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
43360
+ const trusted = authority.kind === "projectRoom" || authority.kind === "projectTask" ? authority.trusted : void 0;
43361
+ const useRoster = trusted !== void 0 && !trustedRosterExempt(trusted);
43362
+ const projectId = trusted ? (trusted.projectTask ?? trusted.projectRoom)?.projectId : void 0;
43363
+ const cacheKey = `${tenantId2}:${useRoster ? `project:${projectId ?? ""}` : "tenant"}`;
42771
43364
  const cached2 = executorCache.get(cacheKey);
42772
43365
  const now = Date.now();
42773
43366
  if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
42774
43367
  const lines = [];
42775
43368
  try {
42776
- if (governed && projectId) {
43369
+ if (useRoster && projectId) {
42777
43370
  const store = getStoreLattice("default", "projectBotMembership").store;
42778
43371
  if (typeof store.list === "function") {
42779
43372
  const bots = await store.list(tenantId2, projectId);
@@ -42788,11 +43381,15 @@ async function renderAvailableExecutors(rc, tenantId2) {
42788
43381
  }
42789
43382
  }
42790
43383
  } else {
43384
+ const exempt = trusted !== void 0 && trustedRosterExempt(trusted) ? trusted : void 0;
43385
+ const excluded = exempt ? await excludedRosterExecutors(exempt) : /* @__PURE__ */ new Set();
42791
43386
  const configs = await agentLatticeManager.getAllAgentConfigsByTenant(tenantId2);
42792
43387
  for (const config of configs.slice(0, 40)) {
42793
43388
  const row = config;
42794
43389
  const id = executorString(row.key);
42795
43390
  if (!id) continue;
43391
+ if (row.ownerUserId !== void 0) continue;
43392
+ if (excluded.has(id)) continue;
42796
43393
  const name = executorString(row.name);
42797
43394
  const description = executorString(row.description);
42798
43395
  lines.push(`- ${id}${name ? ` \u2014 ${name}` : ""}${description ? ` (${description.slice(0, 120)})` : ""}`);
@@ -42804,7 +43401,7 @@ async function renderAvailableExecutors(rc, tenantId2) {
42804
43401
  executorCache.set(cacheKey, { at: now, text: "" });
42805
43402
  return "";
42806
43403
  }
42807
- const header = governed ? "## Available task executors (Project roster)" : "## Available task executors";
43404
+ const header = useRoster ? "## Available task executors (Project roster)" : "## Available task executors";
42808
43405
  const text = `
42809
43406
 
42810
43407
  ${header}
@@ -42816,6 +43413,22 @@ ${lines.join("\n")}
42816
43413
  executorCache.set(cacheKey, { at: now, text });
42817
43414
  return text;
42818
43415
  }
43416
+ async function excludedRosterExecutors(trusted) {
43417
+ const excluded = /* @__PURE__ */ new Set();
43418
+ const context = trusted.projectTask ?? trusted.projectRoom;
43419
+ if (!context) return excluded;
43420
+ try {
43421
+ const store = getStoreLattice("default", "projectBotMembership").store;
43422
+ if (typeof store.list !== "function") return excluded;
43423
+ const rows = await store.list(context.tenantId, context.projectId);
43424
+ for (const row of Array.isArray(rows) ? rows : []) {
43425
+ const record = row;
43426
+ if (record.status !== "active" && typeof record.assistantId === "string") excluded.add(record.assistantId);
43427
+ }
43428
+ } catch {
43429
+ }
43430
+ return excluded;
43431
+ }
42819
43432
  function delegatedTaskScopeError(taskId) {
42820
43433
  return JSON.stringify({
42821
43434
  success: false,
@@ -42865,7 +43478,7 @@ function trustedRosterScope(trusted) {
42865
43478
  };
42866
43479
  }
42867
43480
  function trustedRosterExempt(trusted) {
42868
- return trusted.projectTask?.rosterExempt === true;
43481
+ return trusted.projectTask?.rosterExempt === true || trusted.projectRoom?.rosterExempt === true;
42869
43482
  }
42870
43483
  async function requireTrustedOwner(trusted, targetAssistantId) {
42871
43484
  const context = trusted.projectTask ?? trusted.projectRoom;
@@ -42881,6 +43494,17 @@ async function requireTrustedOwner(trusted, targetAssistantId) {
42881
43494
  targetAssistantId
42882
43495
  });
42883
43496
  }
43497
+ async function rosterExemptOwnerError(tenantId2, targetAssistantId) {
43498
+ try {
43499
+ const lattice = await agentLatticeManager.getOrLoadWithTenant(tenantId2, targetAssistantId);
43500
+ if (lattice && lattice.config.ownerUserId === void 0) return void 0;
43501
+ } catch {
43502
+ }
43503
+ return projectPolicyError(new ProjectTaskOwnerPolicyError(
43504
+ "PROJECT_TASK_OWNER_INACTIVE",
43505
+ "Target Project task owner is not a shared agent of this tenant."
43506
+ ));
43507
+ }
42884
43508
  function trustedTaskContextFailure(task, trusted) {
42885
43509
  if (task.tenantId === trusted.tenantId && task.workspaceId === trusted.workspaceId && task.projectId === trusted.projectId) return void 0;
42886
43510
  return JSON.stringify({
@@ -43485,6 +44109,12 @@ function createTaskMiddleware(options = {}) {
43485
44109
  "Target Project task owner is not active in the exact roster."
43486
44110
  ));
43487
44111
  }
44112
+ } else {
44113
+ const ownerError = await rosterExemptOwnerError(
44114
+ (trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
44115
+ ownerId
44116
+ );
44117
+ if (ownerError) return ownerError;
43488
44118
  }
43489
44119
  }
43490
44120
  if (!delegatedTaskId && (typeof effectiveOwnerId !== "string" || effectiveOwnerId.length === 0)) {
@@ -43546,6 +44176,12 @@ function createTaskMiddleware(options = {}) {
43546
44176
  } catch (error) {
43547
44177
  return projectPolicyError(error);
43548
44178
  }
44179
+ } else {
44180
+ const ownerError = await rosterExemptOwnerError(
44181
+ (trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
44182
+ ownerId
44183
+ );
44184
+ if (ownerError) return ownerError;
43549
44185
  }
43550
44186
  }
43551
44187
  return store.create({
@@ -44593,7 +45229,7 @@ function createTaskMiddleware(options = {}) {
44593
45229
  }
44594
45230
  const callerAssistantId = rc.assistant_id;
44595
45231
  const agentId = input.agentId === "general-purpose" && callerAssistantId ? `${callerAssistantId}-general-purpose` : input.agentId;
44596
- const service2 = createTaskExecutionService({
45232
+ const service3 = createTaskExecutionService({
44597
45233
  taskStore: getTaskStore(),
44598
45234
  workItemStore: getTaskWorkItemStore(),
44599
45235
  resolveAgent: async (targetTenantId, agentId2) => !!await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId2),
@@ -44618,7 +45254,7 @@ function createTaskMiddleware(options = {}) {
44618
45254
  }
44619
45255
  });
44620
45256
  const trustedCaller = authority.kind === "projectTask" || authority.kind === "projectRoom" ? authority.trusted.projectTask ?? authority.trusted.projectRoom : void 0;
44621
- return JSON.stringify(await service2.start({
45257
+ return JSON.stringify(await service3.start({
44622
45258
  tenantId: tenantId2,
44623
45259
  workspaceId: rc.workspaceId,
44624
45260
  projectId: rc.projectId,
@@ -46140,6 +46776,24 @@ function snapshotMembership3(value) {
46140
46776
  function snapshotRows(value) {
46141
46777
  return snapshotExactArray2(value) ?? [];
46142
46778
  }
46779
+ async function rosterExemptMembershipProjection(scope) {
46780
+ const excluded = /* @__PURE__ */ new Set();
46781
+ const roles = /* @__PURE__ */ new Map();
46782
+ try {
46783
+ const rows = snapshotRows(await getStoreLattice("default", "projectBotMembership").store.list(scope.tenantId, scope.projectId));
46784
+ for (const row of rows) {
46785
+ const member = snapshotMembership3(row);
46786
+ if (!member) continue;
46787
+ if (member.status !== "active") {
46788
+ excluded.add(member.assistantId);
46789
+ continue;
46790
+ }
46791
+ roles.set(member.assistantId, member.role);
46792
+ }
46793
+ } catch {
46794
+ }
46795
+ return { excluded, roles };
46796
+ }
46143
46797
  async function hasActiveCurrentMembership(scope) {
46144
46798
  const memberships = getStoreLattice("default", "projectBotMembership").store;
46145
46799
  const member = snapshotMembership3(await memberships.findById(scope.tenantId, scope.membershipId));
@@ -46156,11 +46810,11 @@ function createProjectRoomMiddleware() {
46156
46810
  if (!scope) return CONTEXT_REQUIRED;
46157
46811
  const digest = createHash7("sha256").update(text).digest("hex");
46158
46812
  try {
46159
- const service2 = new RoomAgentMessageService({
46813
+ const service3 = new RoomAgentMessageService({
46160
46814
  memberships: getStoreLattice("default", "projectBotMembership").store,
46161
46815
  messages: getStoreLattice("default", "projectRoomMessage").store
46162
46816
  });
46163
- await service2.post({
46817
+ await service3.post({
46164
46818
  tenantId: scope.tenantId,
46165
46819
  workspaceId: scope.workspaceId,
46166
46820
  projectId: scope.projectId,
@@ -46186,6 +46840,25 @@ function createProjectRoomMiddleware() {
46186
46840
  if (!scope) return CONTEXT_REQUIRED;
46187
46841
  try {
46188
46842
  if (!await hasActiveCurrentMembership(scope)) return CONTEXT_REQUIRED;
46843
+ if (scope.rosterExempt === true) {
46844
+ const { excluded, roles } = await rosterExemptMembershipProjection(scope);
46845
+ const configs = await agentLatticeManager.getAllAgentConfigsByTenant(scope.tenantId);
46846
+ const members2 = configs.filter((entry) => entry.ownerUserId === void 0).filter((entry) => {
46847
+ const key5 = entry.key;
46848
+ return typeof key5 === "string" && !excluded.has(key5);
46849
+ }).map((entry) => {
46850
+ const row = entry;
46851
+ const name = typeof row.name === "string" ? row.name : "";
46852
+ const assistantId = typeof row.key === "string" ? row.key : "";
46853
+ return {
46854
+ assistantId,
46855
+ mentionName: name,
46856
+ title: name,
46857
+ role: roles.get(assistantId) ?? "specialist"
46858
+ };
46859
+ }).filter((member) => member.assistantId !== "" && member.mentionName !== "").sort((left, right) => left.assistantId < right.assistantId ? -1 : left.assistantId > right.assistantId ? 1 : 0);
46860
+ return JSON.stringify({ success: true, members: members2 });
46861
+ }
46189
46862
  const rows = await getStoreLattice("default", "projectBotMembership").store.list(scope.tenantId, scope.projectId);
46190
46863
  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) => ({
46191
46864
  assistantId: member.assistantId,
@@ -46933,6 +47606,70 @@ Fall back to \`execute_code\`/\`shell\` to run \`node handlers/x.js\` manually w
46933
47606
  \`PLUGIN_INPUT\` and a synthetic \`PLUGIN_CONNECTION_FILE\` to reproduce logic bugs.
46934
47607
  A synthetic fixture only verifies plumbing \u2014 a real upstream call requires a real
46935
47608
  \`connectionKey\` configured by a human.
47609
+ `,
47610
+ resources: {}
47611
+ },
47612
+ "sandbox-plugins-deploy": {
47613
+ version: "1.0.0",
47614
+ content: `---
47615
+ name: sandbox-plugins-deploy
47616
+ 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.
47617
+ ---
47618
+
47619
+ # Deploying a Plugin as a Resident Service
47620
+
47621
+ By default a plugin handler runs as a short-lived process on each tool call.
47622
+ Optionally, you can **deploy** the plugin as a **resident service**: one
47623
+ long-lived sandbox per plugin that serves every call. This is faster for
47624
+ frequent or heavy calls, but costs a running sandbox.
47625
+
47626
+ ## Precondition: tests must pass first
47627
+ Deploy ONLY after the plugin is validated AND its tests pass:
47628
+ 1. \`manage_plugin validate\` \u2014 no diagnostics.
47629
+ 2. \`manage_plugin create\`/\`update\` \u2014 written and reloaded.
47630
+ 3. \`manage_plugin test\` \u2014 every tool/connection test returns \`ok: true\`
47631
+ (a real \`connectionKey\` test for credentialed tools).
47632
+ Never deploy a plugin you have not tested: a resident service wraps the same
47633
+ handlers, so an untested handler fails identically \u2014 just in a service you now
47634
+ have to undeploy.
47635
+
47636
+ ## Choose a mode
47637
+ - \`"onDemand"\` (default) \u2014 the service is paused when idle and resumed on the
47638
+ next call. Best for plugins used occasionally. Lower steady cost.
47639
+ - \`"alwaysOn"\` \u2014 the service stays resident and is restarted if it becomes
47640
+ unhealthy. Best for plugins on a hot path or with expensive startup.
47641
+
47642
+ ## Deploy
47643
+ \`\`\`jsonc
47644
+ { "action": "deploy", "type": "my-plugin", "mode": "alwaysOn" }
47645
+ \`\`\`
47646
+ Returns \`{ deployed, status, endpoint, mode }\`. \`status\` is \`"running"\` when
47647
+ the service answered its health check. On failure you get
47648
+ \`{ deployed: false, error }\` \u2014 report it, do not treat it as success.
47649
+ Deploying again is idempotent: it reuses the same service.
47650
+
47651
+ ## Check status
47652
+ \`\`\`jsonc
47653
+ { "action": "status", "type": "my-plugin" }
47654
+ \`\`\`
47655
+ Returns \`{ status, endpoint?, mode?, lastHealthAt?, lastError? }\`. \`"absent"\`
47656
+ means no deployment exists; \`"failed"\` (with \`lastError\`) means the service is
47657
+ down. Inspect this before reporting the plugin as serving.
47658
+
47659
+ ## Undeploy
47660
+ \`\`\`jsonc
47661
+ { "action": "undeploy", "type": "my-plugin" }
47662
+ \`\`\`
47663
+ Returns \`{ undeployed }\`. Undeploy removes the resident sandbox and its
47664
+ deployment record; the plugin files and manifest are untouched, so tool calls
47665
+ fall back to the short-process path. Use it to stop paying for a service you no
47666
+ longer need.
47667
+
47668
+ ## When resident services are unavailable
47669
+ If the environment has no deployment service registered, deploy/undeploy/status
47670
+ return a diagnostic (\`{ deployed: false, error }\` etc.) instead of throwing.
47671
+ That is not a plugin defect: the plugin still works through the default
47672
+ short-process path, so report it as not-deployed rather than broken.
46936
47673
  `,
46937
47674
  resources: {}
46938
47675
  }
@@ -46969,9 +47706,197 @@ the connector and give you its key. Only after a real \`connectionKey\` test
46969
47706
  passes may you call the tool verified; without it, report functionality as
46970
47707
  unverified (missing credentials), not as passing. Use \`shell_exec\` (the tool
46971
47708
  provided by the \`code_eval\` middleware) to run \`node handlers/x.js\` for manual
46972
- checks when reproducing a failure.`;
47709
+ checks when reproducing a failure.
47710
+
47711
+ After the plugin passes \`validate\` and its tests, deploy it as a resident
47712
+ service: load "sandbox-plugins-deploy" and follow it. Choose \`onDemand\`
47713
+ (default) or \`alwaysOn\` for hot paths, call \`manage_plugin deploy\` with the
47714
+ chosen \`mode\`, then confirm with \`manage_plugin status\` (\`running\`). Only claim
47715
+ the plugin is deployed and serving once \`status\` reports \`running\`; if the
47716
+ environment reports no deployment service, say so and treat deployment as
47717
+ unavailable \u2014 do NOT claim success. \`undeploy\` removes the service without
47718
+ touching the plugin files.`;
47719
+
47720
+ // src/middlewares/sandboxPluginsUi.ts
47721
+ var SANDBOX_PLUGINS_UI_RESOURCE = "ui://sandbox-plugins/app";
47722
+ var SANDBOX_PLUGINS_APP_HTML = `<!doctype html>
47723
+ <html lang="en">
47724
+ <head>
47725
+ <meta charset="utf-8" />
47726
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
47727
+ <title>Plugin Deployments</title>
47728
+ <style>
47729
+ :root { color-scheme: light dark; }
47730
+ * { box-sizing: border-box; }
47731
+ body {
47732
+ margin: 0;
47733
+ padding: 14px 16px;
47734
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
47735
+ font-size: 13px;
47736
+ color: #1f2430;
47737
+ background: transparent;
47738
+ }
47739
+ h1 { font-size: 14px; font-weight: 600; margin: 0 0 2px; }
47740
+ .subtitle { color: #6b7280; font-size: 12px; margin: 0 0 12px; }
47741
+ table { width: 100%; border-collapse: collapse; }
47742
+ th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eceef2; vertical-align: top; }
47743
+ th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; font-weight: 600; }
47744
+ td.type { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600; white-space: nowrap; }
47745
+ td.mode { white-space: nowrap; color: #4b5563; }
47746
+ td.detail { color: #4b5563; word-break: break-word; max-width: 280px; }
47747
+ .muted { color: #9ca3af; }
47748
+ .status { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
47749
+ .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: #9ca3af; }
47750
+ .dot.running { background: #22c55e; }
47751
+ .dot.failed { background: #ef4444; }
47752
+ .dot.requested, .dot.provisioning, .dot.stopping { background: #f59e0b; }
47753
+ .dot.paused, .dot.stopped { background: #94a3b8; }
47754
+ .state { color: #6b7280; padding: 14px 2px; }
47755
+ .error { margin: 0 0 10px; padding: 8px 10px; border-radius: 8px; background: #fef2f2; color: #b91c1c; }
47756
+ </style>
47757
+ </head>
47758
+ <body>
47759
+ <h1>Plugin Deployments</h1>
47760
+ <p class="subtitle">Resident services deployed for this tenant. Read-only.</p>
47761
+ <div id="root"><p class="state">Loading\u2026</p></div>
47762
+ <script>
47763
+ (function () {
47764
+ var root = document.getElementById("root");
47765
+
47766
+ function post(message) {
47767
+ window.parent.postMessage(message, "*");
47768
+ }
47769
+ function notify(method, params) {
47770
+ post({ jsonrpc: "2.0", method: method, params: params });
47771
+ }
47772
+ var nextId = 1;
47773
+ function rpc(method, params) {
47774
+ var id = nextId++;
47775
+ return new Promise(function (resolve, reject) {
47776
+ function onMessage(event) {
47777
+ var data = event.data;
47778
+ if (!data || data.id !== id) return;
47779
+ window.removeEventListener("message", onMessage);
47780
+ if (data.error) reject(new Error((data.error && data.error.message) || "rpc error"));
47781
+ else resolve(data.result);
47782
+ }
47783
+ window.addEventListener("message", onMessage);
47784
+ post({ jsonrpc: "2.0", id: id, method: method, params: params });
47785
+ });
47786
+ }
47787
+
47788
+ function sendSize() {
47789
+ var width = Math.ceil(document.documentElement.scrollWidth || document.body.scrollWidth);
47790
+ var height = Math.ceil(document.documentElement.scrollHeight || document.body.scrollHeight);
47791
+ notify("ui/notifications/size-changed", { width: width, height: height });
47792
+ }
47793
+
47794
+ function esc(value) {
47795
+ return String(value === null || value === undefined ? "" : value).replace(/[&<>"']/g, function (ch) {
47796
+ return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[ch];
47797
+ });
47798
+ }
47799
+
47800
+ function statusClass(status) {
47801
+ switch (status) {
47802
+ case "running": return "running";
47803
+ case "failed": return "failed";
47804
+ case "requested":
47805
+ case "provisioning":
47806
+ case "stopping": return "requested";
47807
+ case "paused":
47808
+ case "stopped": return "paused";
47809
+ default: return "";
47810
+ }
47811
+ }
47812
+
47813
+ function healthCell(item) {
47814
+ if (item.lastError) return '<span style="color:#b91c1c">' + esc(item.lastError) + "</span>";
47815
+ if (item.lastHealthAt) return '<span class="muted">' + esc(item.lastHealthAt) + "</span>";
47816
+ return '<span class="muted">\u2014</span>';
47817
+ }
47818
+
47819
+ function render(payload) {
47820
+ var deployments = payload && Array.isArray(payload.deployments) ? payload.deployments : [];
47821
+ var error = payload && payload.error ? String(payload.error) : "";
47822
+ var html = "";
47823
+ if (error) html += '<div class="error">' + esc(error) + "</div>";
47824
+ if (deployments.length === 0) {
47825
+ html += '<p class="state">' + (error ? "No deployments available." : "No resident plugin deployments.") + "</p>";
47826
+ root.innerHTML = html;
47827
+ sendSize();
47828
+ return;
47829
+ }
47830
+ html += "<table><thead><tr><th>Plugin</th><th>Status</th><th>Mode</th><th>Endpoint</th><th>Health</th></tr></thead><tbody>";
47831
+ deployments.forEach(function (item) {
47832
+ var status = item && item.status ? String(item.status) : "unknown";
47833
+ html += "<tr>";
47834
+ html += '<td class="type">' + esc(item && item.pluginType) + "</td>";
47835
+ html += '<td><span class="status"><span class="dot ' + statusClass(status) + '"></span>' + esc(status) + "</span></td>";
47836
+ html += '<td class="mode">' + esc(item && item.mode) + "</td>";
47837
+ html += '<td class="detail">' + (item && item.endpoint ? esc(item.endpoint) : '<span class="muted">\u2014</span>') + "</td>";
47838
+ html += '<td class="detail">' + healthCell(item || {}) + "</td>";
47839
+ html += "</tr>";
47840
+ });
47841
+ html += "</tbody></table>";
47842
+ root.innerHTML = html;
47843
+ sendSize();
47844
+ }
47845
+
47846
+ function readResult(params) {
47847
+ if (!params || typeof params !== "object") return null;
47848
+ if (params.structuredContent && typeof params.structuredContent === "object") {
47849
+ return params.structuredContent;
47850
+ }
47851
+ var content = params.content;
47852
+ var text = content && content[0] && typeof content[0].text === "string" ? content[0].text : "";
47853
+ if (!text) return null;
47854
+ try { return JSON.parse(text); } catch (err) { return null; }
47855
+ }
47856
+
47857
+ // 1) Register the tool-result listener BEFORE initializing so the data
47858
+ // the host pushes right after the handshake is never missed.
47859
+ window.addEventListener("message", function (event) {
47860
+ var data = event.data;
47861
+ if (!data || typeof data !== "object" || typeof data.method !== "string") return;
47862
+ if (data.method === "ui/notifications/tool-result") {
47863
+ render(readResult(data.params));
47864
+ }
47865
+ });
47866
+
47867
+ if (typeof ResizeObserver !== "undefined") {
47868
+ new ResizeObserver(sendSize).observe(document.documentElement);
47869
+ } else {
47870
+ window.addEventListener("resize", sendSize);
47871
+ }
47872
+
47873
+ // 2) MCP Apps lifecycle: initialize, then signal readiness. The host
47874
+ // pushes tool data only after this completes.
47875
+ rpc("ui/initialize", {
47876
+ protocolVersion: "2025-06-18",
47877
+ capabilities: {},
47878
+ clientInfo: { name: "sandbox-plugins-ui", version: "1.0.0" },
47879
+ appCapabilities: { availableDisplayModes: ["inline"] }
47880
+ }).then(function () {
47881
+ notify("ui/notifications/initialized", {});
47882
+ sendSize();
47883
+ }).catch(function (err) {
47884
+ root.innerHTML = '<p class="state">Failed to initialize: ' + esc((err && err.message) || err) + "</p>";
47885
+ sendSize();
47886
+ });
47887
+ })();
47888
+ </script>
47889
+ </body>
47890
+ </html>
47891
+ `;
46973
47892
 
46974
47893
  // src/middlewares/sandboxPluginsMiddleware.ts
47894
+ var SANDBOX_PLUGINS_UI_REF = {
47895
+ kind: "plugin",
47896
+ pluginType: "sandbox-plugins",
47897
+ resource: SANDBOX_PLUGINS_UI_RESOURCE,
47898
+ displayMode: "inline"
47899
+ };
46975
47900
  var PLUGIN_BUILDER_SKILLS = [...Object.keys(SANDBOX_PLUGIN_SKILLS), "task-tracking"];
46976
47901
  function tenantIdFrom(exeConfig) {
46977
47902
  const c = exeConfig;
@@ -46989,6 +47914,15 @@ function collectSecretValues(connection, fields) {
46989
47914
  function errorText(err) {
46990
47915
  return err instanceof Error ? err.message : String(err);
46991
47916
  }
47917
+ function deploymentUnavailable(action) {
47918
+ const error = `plugin deployment service is not configured; cannot ${action}. Resident plugin services are unavailable in this environment.`;
47919
+ if (action === "undeploy") return JSON.stringify({ undeployed: false, error });
47920
+ if (action === "status") return JSON.stringify({ status: "unavailable", error });
47921
+ return JSON.stringify({ deployed: false, error });
47922
+ }
47923
+ function deploymentsResult(payload) {
47924
+ return appendUiFence(JSON.stringify(payload), SANDBOX_PLUGINS_UI_REF);
47925
+ }
46992
47926
  function redactString(value, secrets) {
46993
47927
  let out = value;
46994
47928
  for (const secret of secrets) out = out.split(secret).join("[redacted]");
@@ -47009,10 +47943,24 @@ function redactSecretValues(payload, secrets) {
47009
47943
  return walk(payload);
47010
47944
  }
47011
47945
  var managePluginSchema = z73.object({
47012
- action: z73.enum(["list", "get", "validate", "create", "update", "delete", "reload", "test"]),
47946
+ action: z73.enum([
47947
+ "list",
47948
+ "get",
47949
+ "validate",
47950
+ "create",
47951
+ "update",
47952
+ "delete",
47953
+ "reload",
47954
+ "test",
47955
+ "deploy",
47956
+ "undeploy",
47957
+ "status",
47958
+ "deployments"
47959
+ ]),
47013
47960
  type: z73.string().optional(),
47014
47961
  manifest: z73.record(z73.unknown()).optional(),
47015
47962
  handlers: z73.record(z73.string()).optional(),
47963
+ mode: z73.enum(["onDemand", "alwaysOn"]).optional(),
47016
47964
  target: z73.object({
47017
47965
  kind: z73.enum(["tool", "connection-test", "connection-discover"]),
47018
47966
  name: z73.string().optional(),
@@ -47025,9 +47973,18 @@ var sandboxPluginsPlugin = {
47025
47973
  type: "sandbox-plugins",
47026
47974
  category: "execution",
47027
47975
  name: "Sandbox Plugins",
47028
- 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.",
47976
+ 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.",
47029
47977
  recommends: ["skill"],
47030
- tools: [{ name: "manage_plugin", description: "Author and manage tenant sandbox plugins (tools, optional per-tool HTML UI, connections, and Open/MCP exposure)" }],
47978
+ tools: [
47979
+ {
47980
+ name: "manage_plugin",
47981
+ 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.",
47982
+ ui: { resource: SANDBOX_PLUGINS_UI_RESOURCE, displayMode: "inline" }
47983
+ }
47984
+ ],
47985
+ uiResources: {
47986
+ [SANDBOX_PLUGINS_UI_RESOURCE]: { html: SANDBOX_PLUGINS_APP_HTML }
47987
+ },
47031
47988
  configSchema: { type: "object", properties: {} },
47032
47989
  defaultConfig: {}
47033
47990
  },
@@ -47048,7 +48005,7 @@ var sandboxPluginsPlugin = {
47048
48005
  id: "sandbox-plugins",
47049
48006
  type: "sandbox-plugins",
47050
48007
  name: "Sandbox Plugins",
47051
- description: "Author and manage tenant plugins (list/validate/create/update/delete/reload/test)",
48008
+ description: "Author and manage tenant plugins (list/validate/create/update/delete/reload/test/deploy/undeploy/status)",
47052
48009
  enabled: true,
47053
48010
  config: {}
47054
48011
  },
@@ -47244,13 +48201,74 @@ var sandboxPluginsPlugin = {
47244
48201
  return JSON.stringify(redactSecretValues({ ok: false, error: errorText(err) }, secrets));
47245
48202
  }
47246
48203
  }
48204
+ case "deploy": {
48205
+ if (!input.type) return "type is required";
48206
+ const service3 = getPluginDeploymentService();
48207
+ if (!service3) return deploymentUnavailable("deploy");
48208
+ try {
48209
+ const result = await service3.deploy(tenantId2, input.type, {
48210
+ ...input.mode === void 0 ? {} : { mode: input.mode }
48211
+ });
48212
+ return JSON.stringify({
48213
+ deployed: true,
48214
+ status: result.status,
48215
+ endpoint: result.endpoint,
48216
+ mode: result.mode
48217
+ });
48218
+ } catch (err) {
48219
+ return JSON.stringify({ deployed: false, error: errorText(err) });
48220
+ }
48221
+ }
48222
+ case "undeploy": {
48223
+ if (!input.type) return "type is required";
48224
+ const service3 = getPluginDeploymentService();
48225
+ if (!service3) return deploymentUnavailable("undeploy");
48226
+ try {
48227
+ const result = await service3.undeploy(tenantId2, input.type);
48228
+ return JSON.stringify({ undeployed: result.undeployed });
48229
+ } catch (err) {
48230
+ return JSON.stringify({ undeployed: false, error: errorText(err) });
48231
+ }
48232
+ }
48233
+ case "status": {
48234
+ if (!input.type) return "type is required";
48235
+ const service3 = getPluginDeploymentService();
48236
+ if (!service3) return deploymentUnavailable("status");
48237
+ try {
48238
+ const result = await service3.status(tenantId2, input.type);
48239
+ return JSON.stringify({
48240
+ status: result.status,
48241
+ endpoint: result.endpoint,
48242
+ mode: result.mode,
48243
+ lastHealthAt: result.lastHealthAt,
48244
+ lastError: result.lastError
48245
+ });
48246
+ } catch (err) {
48247
+ return JSON.stringify({ status: "unavailable", error: errorText(err) });
48248
+ }
48249
+ }
48250
+ case "deployments": {
48251
+ const service3 = getPluginDeploymentService();
48252
+ if (!service3) {
48253
+ return deploymentsResult({
48254
+ deployments: [],
48255
+ error: "plugin deployment service is not configured; cannot list deployments. Resident plugin services are unavailable in this environment."
48256
+ });
48257
+ }
48258
+ try {
48259
+ const deployments = await service3.list(tenantId2);
48260
+ return deploymentsResult({ deployments });
48261
+ } catch (err) {
48262
+ return deploymentsResult({ deployments: [], error: errorText(err) });
48263
+ }
48264
+ }
47247
48265
  default:
47248
48266
  return `unsupported action: ${input.action}`;
47249
48267
  }
47250
48268
  },
47251
48269
  {
47252
48270
  name: "manage_plugin",
47253
- description: "Author and manage tenant sandbox plugins (list/get/validate/create/update/delete/reload/test).",
48271
+ 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.",
47254
48272
  schema: managePluginSchema
47255
48273
  }
47256
48274
  )
@@ -47305,6 +48323,526 @@ function buildBundleFromDist(distDir, version) {
47305
48323
  };
47306
48324
  }
47307
48325
 
48326
+ // src/sandbox_plugin/PluginDeploymentRuntimeRegistry.ts
48327
+ var PluginDeploymentRuntimeRegistry = class {
48328
+ /**
48329
+ * @param options - Optional injectable clock for deterministic health timestamps.
48330
+ */
48331
+ constructor(options = {}) {
48332
+ this.entries = /* @__PURE__ */ new Map();
48333
+ this.inFlight = /* @__PURE__ */ new Map();
48334
+ this.now = options.now ?? Date.now;
48335
+ }
48336
+ /**
48337
+ * Cache the resolved proxy endpoint for a deployment.
48338
+ *
48339
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48340
+ * @param endpoint - Relative proxy path, e.g. `/api/sandboxes/plugin-abc/proxy/8080`.
48341
+ */
48342
+ setEndpoint(id, endpoint) {
48343
+ this.entry(id).endpoint = endpoint;
48344
+ }
48345
+ /**
48346
+ * Read the cached endpoint for a deployment.
48347
+ *
48348
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48349
+ * @returns The cached endpoint, or `undefined` when none is known.
48350
+ */
48351
+ getEndpoint(id) {
48352
+ return this.entries.get(id)?.endpoint;
48353
+ }
48354
+ /**
48355
+ * Record a successful health observation and clear any prior error.
48356
+ *
48357
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48358
+ */
48359
+ markHealthy(id) {
48360
+ const entry = this.entry(id);
48361
+ entry.lastHealthAt = this.now();
48362
+ delete entry.lastError;
48363
+ }
48364
+ /**
48365
+ * Record a failed health observation.
48366
+ *
48367
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48368
+ * @param error - Human-readable failure description.
48369
+ */
48370
+ markUnhealthy(id, error) {
48371
+ const entry = this.entry(id);
48372
+ entry.lastHealthAt = this.now();
48373
+ entry.lastError = error;
48374
+ }
48375
+ /**
48376
+ * Read the last observed health for a deployment.
48377
+ *
48378
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48379
+ * @returns The cached health, or `undefined` when the id is unknown.
48380
+ */
48381
+ getHealth(id) {
48382
+ const entry = this.entries.get(id);
48383
+ if (!entry) return void 0;
48384
+ const health = {};
48385
+ if (entry.lastHealthAt !== void 0) health.lastHealthAt = entry.lastHealthAt;
48386
+ if (entry.lastError !== void 0) health.lastError = entry.lastError;
48387
+ return health;
48388
+ }
48389
+ /**
48390
+ * Run a cold start/resume factory at most once per id at a time.
48391
+ *
48392
+ * Concurrent calls for the same id return the same in-flight promise and run
48393
+ * `factory` exactly once. The in-flight entry is cleared when the factory
48394
+ * settles, so a later call runs it again (retry after failure is allowed).
48395
+ *
48396
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48397
+ * @param factory - Idempotent start/resume operation for the resident service.
48398
+ * @returns A promise that settles with the factory outcome.
48399
+ */
48400
+ ensureRunning(id, factory) {
48401
+ const existing = this.inFlight.get(id);
48402
+ if (existing) return existing;
48403
+ let started;
48404
+ try {
48405
+ started = factory();
48406
+ } catch (error) {
48407
+ started = Promise.reject(error);
48408
+ }
48409
+ const tracked = started.finally(() => {
48410
+ if (this.inFlight.get(id) === tracked) {
48411
+ this.inFlight.delete(id);
48412
+ }
48413
+ });
48414
+ this.inFlight.set(id, tracked);
48415
+ return tracked;
48416
+ }
48417
+ /**
48418
+ * Drop cached endpoint and health for one deployment.
48419
+ *
48420
+ * @param id - Composed key `${tenantId}:${pluginType}`.
48421
+ */
48422
+ clear(id) {
48423
+ this.entries.delete(id);
48424
+ }
48425
+ /** Drop all cached endpoints, health, and in-flight coordination entries. */
48426
+ reset() {
48427
+ this.entries.clear();
48428
+ this.inFlight.clear();
48429
+ }
48430
+ entry(id) {
48431
+ const existing = this.entries.get(id);
48432
+ if (existing) return existing;
48433
+ const created = {};
48434
+ this.entries.set(id, created);
48435
+ return created;
48436
+ }
48437
+ };
48438
+
48439
+ // src/sandbox_plugin/host/pluginHostProtocol.ts
48440
+ var INLINE_INPUT_LIMIT_BYTES2 = 48 * 1024;
48441
+ var DEFAULT_HANDLER_TIMEOUT_MS = 6e4;
48442
+ var MIN_HANDLER_TIMEOUT_MS = 1e3;
48443
+ var MAX_HANDLER_TIMEOUT_MS = 3e5;
48444
+ function isRecord11(value) {
48445
+ return typeof value === "object" && value !== null && !Array.isArray(value);
48446
+ }
48447
+ function isEnvelope3(value) {
48448
+ return isRecord11(value) && typeof value.ok === "boolean";
48449
+ }
48450
+ function parseHostArgs(argv) {
48451
+ let type;
48452
+ let port;
48453
+ for (let index = 0; index < argv.length; index += 1) {
48454
+ const token = argv[index];
48455
+ if (token === "--type" && index + 1 < argv.length) {
48456
+ type = argv[index + 1];
48457
+ index += 1;
48458
+ } else if (token.startsWith("--type=")) {
48459
+ type = token.slice("--type=".length);
48460
+ } else if (token === "--port" && index + 1 < argv.length) {
48461
+ port = argv[index + 1];
48462
+ index += 1;
48463
+ } else if (token.startsWith("--port=")) {
48464
+ port = token.slice("--port=".length);
48465
+ }
48466
+ }
48467
+ const parsedPort = Number(port);
48468
+ if (typeof type !== "string" || type.length === 0) {
48469
+ throw new Error("parseHostArgs: missing or empty --type");
48470
+ }
48471
+ if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
48472
+ throw new Error("parseHostArgs: invalid --port");
48473
+ }
48474
+ return { type, port: parsedPort };
48475
+ }
48476
+ function resolveHandlerTimeoutMs(value) {
48477
+ if (value === null || value === void 0 || value === "") return DEFAULT_HANDLER_TIMEOUT_MS;
48478
+ const numeric = typeof value === "number" ? value : Number(value);
48479
+ if (!Number.isFinite(numeric)) return DEFAULT_HANDLER_TIMEOUT_MS;
48480
+ return Math.min(Math.max(Math.round(numeric), MIN_HANDLER_TIMEOUT_MS), MAX_HANDLER_TIMEOUT_MS);
48481
+ }
48482
+ function parseHandlerEnvelope(stdout) {
48483
+ const trimmed = stdout.trim();
48484
+ if (!trimmed) return { ok: false, error: "empty handler output" };
48485
+ const whole = tryParse(trimmed);
48486
+ if (isEnvelope3(whole)) return normalizeEnvelope(whole);
48487
+ const lines = stdout.split("\n");
48488
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
48489
+ const line = lines[index]?.trim();
48490
+ if (!line) continue;
48491
+ const candidate = tryParse(line);
48492
+ if (isEnvelope3(candidate)) return normalizeEnvelope(candidate);
48493
+ }
48494
+ return { ok: true, result: trimmed };
48495
+ }
48496
+ function buildHandlerCommand(handlerPath) {
48497
+ return `node ${quotePosixShellArg2(handlerPath)}`;
48498
+ }
48499
+ function encodeInput(input) {
48500
+ return Buffer.from(JSON.stringify(input ?? null), "utf8").toString("base64");
48501
+ }
48502
+ function buildConnectionPayload2(args) {
48503
+ if (Array.isArray(args.connections) && args.connections.length > 0) {
48504
+ return {
48505
+ connection: args.connection ?? args.connections[0]?.config,
48506
+ connections: args.connections
48507
+ };
48508
+ }
48509
+ if (args.connection) {
48510
+ return { connection: args.connection };
48511
+ }
48512
+ return null;
48513
+ }
48514
+ function quotePosixShellArg2(value) {
48515
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
48516
+ }
48517
+ function tryParse(text) {
48518
+ try {
48519
+ return JSON.parse(text);
48520
+ } catch {
48521
+ return void 0;
48522
+ }
48523
+ }
48524
+ function normalizeEnvelope(parsed) {
48525
+ if (parsed.ok) return { ok: true, result: parsed.result };
48526
+ return { ok: false, error: typeof parsed.error === "string" ? parsed.error : void 0 };
48527
+ }
48528
+
48529
+ // src/sandbox_plugin/host/pluginHostSource.ts
48530
+ import { createHash as createHash8 } from "crypto";
48531
+ var PLUGIN_HOST_VERSION = "1.1.0";
48532
+ var PLUGIN_HOST_SERVER_SOURCE = String.raw`#!/usr/bin/env node
48533
+ "use strict";
48534
+
48535
+ var http = require("node:http");
48536
+ var fs = require("node:fs");
48537
+ var os = require("node:os");
48538
+ var path = require("node:path");
48539
+ var crypto = require("node:crypto");
48540
+ var execFile = require("node:child_process").execFile;
48541
+
48542
+ var VERSION = "${PLUGIN_HOST_VERSION}";
48543
+ var PLUGINS_ROOT = "/root/.agents/plugins";
48544
+ var INLINE_INPUT_LIMIT_BYTES = 48 * 1024;
48545
+ var MAX_BODY_BYTES = 8 * 1024 * 1024;
48546
+ var MAX_RESULT_BYTES = 16 * 1024 * 1024;
48547
+ var DEFAULT_HANDLER_TIMEOUT_MS = 60000;
48548
+ var MIN_HANDLER_TIMEOUT_MS = 1000;
48549
+ var MAX_HANDLER_TIMEOUT_MS = 300000;
48550
+
48551
+ function resolveHandlerTimeoutMs(value) {
48552
+ if (value === null || value === undefined || value === "") {
48553
+ return DEFAULT_HANDLER_TIMEOUT_MS;
48554
+ }
48555
+ var numeric = typeof value === "number" ? value : Number(value);
48556
+ if (!isFinite(numeric)) {
48557
+ return DEFAULT_HANDLER_TIMEOUT_MS;
48558
+ }
48559
+ return Math.min(Math.max(Math.round(numeric), MIN_HANDLER_TIMEOUT_MS), MAX_HANDLER_TIMEOUT_MS);
48560
+ }
48561
+
48562
+ function isRecord(value) {
48563
+ return typeof value === "object" && value !== null && !Array.isArray(value);
48564
+ }
48565
+
48566
+ function isEnvelope(value) {
48567
+ return isRecord(value) && typeof value.ok === "boolean";
48568
+ }
48569
+
48570
+ function tryParse(text) {
48571
+ try {
48572
+ return JSON.parse(text);
48573
+ } catch (err) {
48574
+ return undefined;
48575
+ }
48576
+ }
48577
+
48578
+ function normalizeEnvelope(parsed) {
48579
+ if (parsed.ok) {
48580
+ return { ok: true, result: parsed.result };
48581
+ }
48582
+ return { ok: false, error: typeof parsed.error === "string" ? parsed.error : undefined };
48583
+ }
48584
+
48585
+ function parseHandlerEnvelope(stdout) {
48586
+ var text = String(stdout == null ? "" : stdout);
48587
+ var trimmed = text.trim();
48588
+ if (!trimmed) {
48589
+ // Deliberate divergence from SandboxPluginExecutor, which returns
48590
+ // { ok: true, result: "" } for empty stdout; the resident host treats empty
48591
+ // output as a handler failure so a crashed/no-op handler is not a "success".
48592
+ return { ok: false, error: "empty handler output" };
48593
+ }
48594
+ var whole = tryParse(trimmed);
48595
+ if (isEnvelope(whole)) {
48596
+ return normalizeEnvelope(whole);
48597
+ }
48598
+ var lines = text.split("\n");
48599
+ for (var index = lines.length - 1; index >= 0; index -= 1) {
48600
+ var line = lines[index].trim();
48601
+ if (!line) {
48602
+ continue;
48603
+ }
48604
+ var candidate = tryParse(line);
48605
+ if (isEnvelope(candidate)) {
48606
+ return normalizeEnvelope(candidate);
48607
+ }
48608
+ }
48609
+ return { ok: true, result: trimmed };
48610
+ }
48611
+
48612
+ function quotePosixShellArg(value) {
48613
+ return "'" + String(value).replace(/'/g, "'\"'\"'") + "'";
48614
+ }
48615
+
48616
+ function buildHandlerCommand(handlerPath) {
48617
+ return "node " + quotePosixShellArg(handlerPath);
48618
+ }
48619
+
48620
+ function parseHostArgs(argv) {
48621
+ var type;
48622
+ var port;
48623
+ for (var index = 0; index < argv.length; index += 1) {
48624
+ var token = argv[index];
48625
+ if (token === "--type" && index + 1 < argv.length) {
48626
+ type = argv[index + 1];
48627
+ index += 1;
48628
+ } else if (token.indexOf("--type=") === 0) {
48629
+ type = token.slice("--type=".length);
48630
+ } else if (token === "--port" && index + 1 < argv.length) {
48631
+ port = argv[index + 1];
48632
+ index += 1;
48633
+ } else if (token.indexOf("--port=") === 0) {
48634
+ port = token.slice("--port=".length);
48635
+ }
48636
+ }
48637
+ var parsedPort = Number(port);
48638
+ if (typeof type !== "string" || type.length === 0) {
48639
+ throw new Error("parseHostArgs: missing or empty --type");
48640
+ }
48641
+ if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
48642
+ throw new Error("parseHostArgs: invalid --port");
48643
+ }
48644
+ return { type: type, port: parsedPort };
48645
+ }
48646
+
48647
+ function encodeInput(input) {
48648
+ return Buffer.from(JSON.stringify(input === undefined ? null : input), "utf8").toString("base64");
48649
+ }
48650
+
48651
+ function buildConnectionPayload(body) {
48652
+ if (Array.isArray(body.connections) && body.connections.length > 0) {
48653
+ return {
48654
+ connection: body.connection !== undefined ? body.connection : body.connections[0].config,
48655
+ connections: body.connections,
48656
+ };
48657
+ }
48658
+ if (body.connection) {
48659
+ return { connection: body.connection };
48660
+ }
48661
+ return null;
48662
+ }
48663
+
48664
+ function resolveHandlerPath(type, handler) {
48665
+ if (typeof handler !== "string" || handler.length === 0) {
48666
+ throw new Error("missing handler");
48667
+ }
48668
+ if (handler.charAt(0) === "/" || handler.split("/").indexOf("..") !== -1) {
48669
+ throw new Error("invalid handler path");
48670
+ }
48671
+ return path.join(PLUGINS_ROOT, type, handler);
48672
+ }
48673
+
48674
+ function readJsonBody(req) {
48675
+ return new Promise(function (resolve, reject) {
48676
+ var chunks = [];
48677
+ var size = 0;
48678
+ req.on("data", function (chunk) {
48679
+ size += chunk.length;
48680
+ if (size > MAX_BODY_BYTES) {
48681
+ reject(new Error("request body too large"));
48682
+ req.destroy();
48683
+ return;
48684
+ }
48685
+ chunks.push(chunk);
48686
+ });
48687
+ req.on("end", function () {
48688
+ try {
48689
+ var text = Buffer.concat(chunks).toString("utf8");
48690
+ resolve(text ? JSON.parse(text) : {});
48691
+ } catch (err) {
48692
+ reject(new Error("invalid JSON body"));
48693
+ }
48694
+ });
48695
+ req.on("error", reject);
48696
+ });
48697
+ }
48698
+
48699
+ function runNode(handlerPath, pluginDir, env, timeoutMs) {
48700
+ return new Promise(function (resolve) {
48701
+ execFile(
48702
+ process.execPath,
48703
+ [handlerPath],
48704
+ { cwd: pluginDir, env: env, timeout: timeoutMs, maxBuffer: MAX_RESULT_BYTES },
48705
+ function (error, stdout, stderr) {
48706
+ resolve({ error: error, stdout: stdout || "", stderr: stderr || "" });
48707
+ }
48708
+ );
48709
+ });
48710
+ }
48711
+
48712
+ function failureFromExec(exec, command) {
48713
+ var parsed = parseHandlerEnvelope(exec.stdout);
48714
+ if (!parsed.ok) {
48715
+ return parsed;
48716
+ }
48717
+ var code = exec.error && typeof exec.error.code === "number" ? exec.error.code : "n/a";
48718
+ var detail = exec.stdout ? exec.stdout.slice(0, 4096) : exec.error ? String(exec.error.message) : "";
48719
+ return { ok: false, error: "handler failed (exit " + code + "): " + detail + " [" + command + "]" };
48720
+ }
48721
+
48722
+ async function runHandler(body) {
48723
+ var pluginDir = path.join(PLUGINS_ROOT, config.type);
48724
+ var handlerPath = resolveHandlerPath(config.type, body.handler);
48725
+ var command = buildHandlerCommand(handlerPath);
48726
+ var inputJson = JSON.stringify(body.input === undefined ? null : body.input);
48727
+ var connectionPayload = buildConnectionPayload(body);
48728
+ var inputIsLarge = Buffer.byteLength(inputJson, "utf8") > INLINE_INPUT_LIMIT_BYTES;
48729
+ var needsTempDir = inputIsLarge || connectionPayload !== null;
48730
+ var tempDir = null;
48731
+ var env = Object.assign({}, process.env);
48732
+ try {
48733
+ if (needsTempDir) {
48734
+ tempDir = path.join(os.tmpdir(), "plugin-run-" + Date.now() + "-" + Math.random().toString(36).slice(2));
48735
+ fs.mkdirSync(tempDir, { recursive: true });
48736
+ }
48737
+ if (inputIsLarge) {
48738
+ var inputFile = path.join(tempDir, "input.json");
48739
+ fs.writeFileSync(inputFile, inputJson);
48740
+ env.PLUGIN_INPUT_FILE = inputFile;
48741
+ } else {
48742
+ env.PLUGIN_INPUT = encodeInput(body.input);
48743
+ }
48744
+ if (connectionPayload !== null) {
48745
+ var connectionFile = path.join(tempDir, "connection.json");
48746
+ fs.writeFileSync(connectionFile, JSON.stringify(connectionPayload));
48747
+ env.PLUGIN_CONNECTION_FILE = connectionFile;
48748
+ }
48749
+ env.PLUGIN_CONTEXT = encodeInput(body.context || {});
48750
+ var handlerTimeoutMs = resolveHandlerTimeoutMs(body.timeoutMs);
48751
+ var exec = await runNode(handlerPath, pluginDir, env, handlerTimeoutMs);
48752
+ if (exec.error) {
48753
+ return failureFromExec(exec, command);
48754
+ }
48755
+ return parseHandlerEnvelope(exec.stdout);
48756
+ } finally {
48757
+ if (tempDir) {
48758
+ try {
48759
+ fs.rmSync(tempDir, { recursive: true, force: true });
48760
+ } catch (err) {
48761
+ // best-effort cleanup
48762
+ }
48763
+ }
48764
+ }
48765
+ }
48766
+
48767
+ function manifestHash() {
48768
+ try {
48769
+ var content = fs.readFileSync(path.join(PLUGINS_ROOT, config.type, "plugin.json"), "utf8");
48770
+ return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
48771
+ } catch (err) {
48772
+ return "";
48773
+ }
48774
+ }
48775
+
48776
+ function sendJson(res, status, payload) {
48777
+ var body = JSON.stringify(payload);
48778
+ res.writeHead(status, {
48779
+ "content-type": "application/json",
48780
+ "content-length": Buffer.byteLength(body),
48781
+ });
48782
+ res.end(body);
48783
+ }
48784
+
48785
+ function handleRequest(req, res) {
48786
+ var url = String(req.url || "").split("?")[0];
48787
+ if (req.method === "GET" && url === "/health") {
48788
+ sendJson(res, 200, { ok: true, version: VERSION });
48789
+ return;
48790
+ }
48791
+ if (req.method === "GET" && url === "/meta") {
48792
+ sendJson(res, 200, { type: config.type, manifestHash: manifestHash() });
48793
+ return;
48794
+ }
48795
+ if (req.method === "POST" && url === "/invoke") {
48796
+ readJsonBody(req)
48797
+ .then(function (body) {
48798
+ if (!isRecord(body)) {
48799
+ sendJson(res, 400, { ok: false, error: "invalid body" });
48800
+ return;
48801
+ }
48802
+ return runHandler(body).then(function (result) {
48803
+ sendJson(res, 200, result);
48804
+ });
48805
+ })
48806
+ .catch(function (err) {
48807
+ sendJson(res, 400, { ok: false, error: String(err && err.message ? err.message : err) });
48808
+ });
48809
+ return;
48810
+ }
48811
+ sendJson(res, 404, { ok: false, error: "not found" });
48812
+ }
48813
+
48814
+ var config = parseHostArgs(process.argv);
48815
+ var server = http.createServer(handleRequest);
48816
+ server.listen(config.port, "0.0.0.0", function () {
48817
+ process.stdout.write(
48818
+ "[plugin-host] listening type=" + config.type + " port=" + config.port + " version=" + VERSION + "\n"
48819
+ );
48820
+ });
48821
+ `;
48822
+ function pluginHostContentHash() {
48823
+ return createHash8("sha256").update(`${PLUGIN_HOST_VERSION}:${PLUGIN_HOST_SERVER_SOURCE}`).digest("hex").slice(0, 16);
48824
+ }
48825
+
48826
+ // src/sandbox_plugin/host/pluginHostInstaller.ts
48827
+ var PLUGIN_HOST_DIR = "/root/.agents/.plugin-host";
48828
+ var SERVER_PATH = `${PLUGIN_HOST_DIR}/server.js`;
48829
+ var VERSION_PATH = `${PLUGIN_HOST_DIR}/version.json`;
48830
+ async function installPluginHost(sandbox) {
48831
+ const hash = pluginHostContentHash();
48832
+ try {
48833
+ const existing = await sandbox.file.readFile(VERSION_PATH);
48834
+ const parsed = JSON.parse(existing.content);
48835
+ if (parsed.hash === hash) {
48836
+ return { path: SERVER_PATH, version: PLUGIN_HOST_VERSION, hash, changed: false };
48837
+ }
48838
+ } catch {
48839
+ }
48840
+ await sandbox.file.createDirectory(PLUGIN_HOST_DIR).catch(() => void 0);
48841
+ await sandbox.file.writeFile(SERVER_PATH, PLUGIN_HOST_SERVER_SOURCE);
48842
+ await sandbox.file.writeFile(VERSION_PATH, JSON.stringify({ version: PLUGIN_HOST_VERSION, hash }, null, 2));
48843
+ return { path: SERVER_PATH, version: PLUGIN_HOST_VERSION, hash, changed: true };
48844
+ }
48845
+
47308
48846
  // src/personal_assistant/PersonalAssistantConfig.ts
47309
48847
  function deepClone(obj) {
47310
48848
  return JSON.parse(JSON.stringify(obj));
@@ -47689,16 +49227,16 @@ var IdRemapper = class {
47689
49227
  };
47690
49228
 
47691
49229
  // src/util/directRoomId.ts
47692
- import { createHash as createHash8 } from "crypto";
49230
+ import { createHash as createHash9 } from "crypto";
47693
49231
  function generateDirectHumanProjectId(tenantId2, workspaceId, userId1, userId2) {
47694
49232
  const sortedUserIds = [userId1, userId2].sort();
47695
49233
  const parts = [tenantId2, workspaceId, ...sortedUserIds];
47696
- const hash = createHash8("sha256").update(parts.join(":")).digest("hex");
49234
+ const hash = createHash9("sha256").update(parts.join(":")).digest("hex");
47697
49235
  return `direct_human_${hash}`;
47698
49236
  }
47699
49237
  function generateDirectBotProjectId(tenantId2, workspaceId, userId, assistantId) {
47700
49238
  const parts = [tenantId2, workspaceId, userId, assistantId];
47701
- const hash = createHash8("sha256").update(parts.join(":")).digest("hex");
49239
+ const hash = createHash9("sha256").update(parts.join(":")).digest("hex");
47702
49240
  return `direct_bot_${hash}`;
47703
49241
  }
47704
49242
  function parseDirectRoomType(projectId) {
@@ -47727,6 +49265,7 @@ export {
47727
49265
  CapabilityBundleValidationError,
47728
49266
  CapabilityToolNameConflictError,
47729
49267
  CapabilityToolRegistry,
49268
+ ChannelConnectionLeader,
47730
49269
  ChunkBuffer,
47731
49270
  ChunkBufferLatticeManager,
47732
49271
  CollectionLatticeManager,
@@ -47738,7 +49277,9 @@ export {
47738
49277
  DEFAULT_CALIBRATION_PROBES,
47739
49278
  DEFAULT_CONNECT_TIMEOUT_MS,
47740
49279
  DEFAULT_DELAY_MS,
49280
+ DEFAULT_HANDLER_TIMEOUT_MS,
47741
49281
  DEFAULT_MAX_ATTEMPTS,
49282
+ DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS,
47742
49283
  DaytonaInstance,
47743
49284
  DaytonaProvider,
47744
49285
  DefaultScheduleClient,
@@ -47756,6 +49297,7 @@ export {
47756
49297
  HEALTH_CHECK_INTERVAL_MS,
47757
49298
  HEALTH_CHECK_TIMEOUT_MS,
47758
49299
  HumanMessage7 as HumanMessage,
49300
+ INLINE_INPUT_LIMIT_BYTES2 as INLINE_INPUT_LIMIT_BYTES,
47759
49301
  IdRemapper,
47760
49302
  InMemoryA2AApiKeyStore,
47761
49303
  InMemoryAgentWebAppStore,
@@ -47796,10 +49338,12 @@ export {
47796
49338
  LocalSandboxProvider,
47797
49339
  LoggerLatticeManager,
47798
49340
  MAX_BACKOFF_MS,
49341
+ MAX_HANDLER_TIMEOUT_MS,
47799
49342
  MAX_LINE_LENGTH,
47800
49343
  MAX_PACK_ENTRIES,
47801
49344
  MAX_PACK_FILE_BYTES,
47802
49345
  MAX_PACK_TOTAL_BYTES,
49346
+ MIN_HANDLER_TIMEOUT_MS,
47803
49347
  ManagedTaskStartError,
47804
49348
  McpLatticeManager,
47805
49349
  MemoryBackend,
@@ -47818,8 +49362,12 @@ export {
47818
49362
  PACK_DIRECTORY_TIMEOUT_SECONDS,
47819
49363
  PLATFORM_SECRET_ENV_VARS,
47820
49364
  PLUGIN_CATALOG_CHANGED_TOPIC,
49365
+ PLUGIN_HOST_DIR,
49366
+ PLUGIN_HOST_SERVER_SOURCE,
49367
+ PLUGIN_HOST_VERSION,
47821
49368
  PersonalAssistantConfig,
47822
49369
  PinoLoggerClient,
49370
+ PluginDeploymentRuntimeRegistry,
47823
49371
  PluginRegistry,
47824
49372
  PostgresDatabase,
47825
49373
  ProjectBotMembershipIdConflictError,
@@ -47881,7 +49429,9 @@ export {
47881
49429
  appendUiFence,
47882
49430
  assertSafeUpstreamUrl,
47883
49431
  buildBundleFromDist,
49432
+ buildConnectionPayload2 as buildConnectionPayload,
47884
49433
  buildGrepResultsDict,
49434
+ buildHandlerCommand,
47885
49435
  buildInput,
47886
49436
  buildNamedVolumeName,
47887
49437
  buildRemotePackDirectoryCommand,
@@ -47949,9 +49499,11 @@ export {
47949
49499
  decrypt,
47950
49500
  decryptConfigFields,
47951
49501
  describeCronExpression,
49502
+ disconnectAllChannels,
47952
49503
  documentLearningPlugin,
47953
49504
  documentParserPlugin,
47954
49505
  embeddingsLatticeManager,
49506
+ encodeInput,
47955
49507
  encrypt,
47956
49508
  encryptConfigFields,
47957
49509
  ensureBuiltinAgentsForTenant,
@@ -47995,6 +49547,7 @@ export {
47995
49547
  getModelLattice,
47996
49548
  getNextCronTime,
47997
49549
  getOrCreateCollectionVectorStore,
49550
+ getPluginDeploymentService,
47998
49551
  getPluginSkillManifestVersion,
47999
49552
  getQueueLattice,
48000
49553
  getReplicaId,
@@ -48023,6 +49576,8 @@ export {
48023
49576
  hasChunkBuffer,
48024
49577
  hasDelegatedWorkerToken,
48025
49578
  hasRuntimeCapabilityPolicy,
49579
+ installPluginHost,
49580
+ invokeResident,
48026
49581
  invokeWithRetry,
48027
49582
  isBuiltInSkill,
48028
49583
  isDirectRoom,
@@ -48050,6 +49605,8 @@ export {
48050
49605
  parallelLimit,
48051
49606
  parseCronExpression,
48052
49607
  parseDirectRoomType,
49608
+ parseHandlerEnvelope,
49609
+ parseHostArgs,
48053
49610
  parseJudgeVerdict,
48054
49611
  parsePackSize,
48055
49612
  parseQueuedExecutionMode,
@@ -48058,6 +49615,7 @@ export {
48058
49615
  parseTrustedRunContext,
48059
49616
  parseYaml,
48060
49617
  performStringReplacement,
49618
+ pluginHostContentHash,
48061
49619
  postProcessTaskMutationResponse,
48062
49620
  prepareTaskDispatch,
48063
49621
  projectMutationMutex,
@@ -48096,6 +49654,7 @@ export {
48096
49654
  renderTemplate,
48097
49655
  resetReplicaIdForTests,
48098
49656
  resolveEnabledPluginSkill,
49657
+ resolveHandlerTimeoutMs,
48099
49658
  resolveJudgeModelKey,
48100
49659
  resolvePath,
48101
49660
  resolvePluginConnections,
@@ -48115,6 +49674,7 @@ export {
48115
49674
  setBindingRegistry,
48116
49675
  setEvalRunService,
48117
49676
  setMenuRegistry,
49677
+ setPluginDeploymentService,
48118
49678
  setSandboxPluginCatalog,
48119
49679
  skillLatticeManager,
48120
49680
  sqlDatabaseManager,