@axiom-lattice/core 6.3.2 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +883 -8
- package/dist/index.d.ts +883 -8
- package/dist/index.js +1722 -131
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1696 -129
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
|
13245
|
+
function isRecord4(value) {
|
|
13078
13246
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13079
13247
|
}
|
|
13080
|
-
function
|
|
13081
|
-
return
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 =
|
|
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:
|
|
13590
|
-
defaultConfig:
|
|
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
|
|
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 (
|
|
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:
|
|
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:
|
|
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:
|
|
14661
|
+
executor: buildExecutor(resolveSandbox),
|
|
14241
14662
|
reservedTypes: () => new Set(PluginRegistry.list())
|
|
14242
14663
|
});
|
|
14243
14664
|
return catalog;
|
|
@@ -14246,6 +14667,22 @@ function setSandboxPluginCatalog(instance) {
|
|
|
14246
14667
|
catalog = instance;
|
|
14247
14668
|
}
|
|
14248
14669
|
|
|
14670
|
+
// src/plugin/listTenantPlugins.ts
|
|
14671
|
+
async function collectPluginSources(tenantId2) {
|
|
14672
|
+
const globalPlugins = PluginRegistry.list().map((type) => PluginRegistry.get(type)).filter((plugin) => Boolean(plugin));
|
|
14673
|
+
let tenantPlugins = [];
|
|
14674
|
+
try {
|
|
14675
|
+
tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2)).plugins;
|
|
14676
|
+
} catch {
|
|
14677
|
+
tenantPlugins = [];
|
|
14678
|
+
}
|
|
14679
|
+
return { globalPlugins, tenantPlugins };
|
|
14680
|
+
}
|
|
14681
|
+
async function listTenantPlugins(tenantId2) {
|
|
14682
|
+
const { globalPlugins, tenantPlugins } = await collectPluginSources(tenantId2);
|
|
14683
|
+
return mergePluginSources(globalPlugins, tenantPlugins);
|
|
14684
|
+
}
|
|
14685
|
+
|
|
14249
14686
|
// src/agent_lattice/builders/taskMiddlewareValidation.ts
|
|
14250
14687
|
var REQUIRED_TASK_TOOLS = ["manage_task", "task"];
|
|
14251
14688
|
function validateTaskReviewMode(reviewMode) {
|
|
@@ -14368,23 +14805,23 @@ function toolName(tool68) {
|
|
|
14368
14805
|
return typeof tool68.name === "string" ? tool68.name : void 0;
|
|
14369
14806
|
}
|
|
14370
14807
|
function schemaHasConnectionKey(schema6) {
|
|
14371
|
-
const definition =
|
|
14808
|
+
const definition = isRecord8(schema6._def) ? schema6._def : void 0;
|
|
14372
14809
|
const shape = definition?.shape;
|
|
14373
14810
|
const fields = typeof shape === "function" ? shape() : shape;
|
|
14374
|
-
return
|
|
14811
|
+
return isRecord8(fields) && Object.prototype.hasOwnProperty.call(fields, "connectionKey");
|
|
14375
14812
|
}
|
|
14376
14813
|
function hasConnectionKeySchema(tool68) {
|
|
14377
|
-
if (!
|
|
14814
|
+
if (!isRecord8(tool68) || !isRecord8(tool68.schema)) return false;
|
|
14378
14815
|
if (schemaHasConnectionKey(tool68.schema)) return true;
|
|
14379
|
-
const definition =
|
|
14816
|
+
const definition = isRecord8(tool68.schema._def) ? tool68.schema._def : void 0;
|
|
14380
14817
|
if (Array.isArray(definition?.options) && definition.options.length > 0) {
|
|
14381
14818
|
return definition.options.every(
|
|
14382
|
-
(option) =>
|
|
14819
|
+
(option) => isRecord8(option) && schemaHasConnectionKey(option)
|
|
14383
14820
|
);
|
|
14384
14821
|
}
|
|
14385
14822
|
return false;
|
|
14386
14823
|
}
|
|
14387
|
-
function
|
|
14824
|
+
function isRecord8(value) {
|
|
14388
14825
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14389
14826
|
}
|
|
14390
14827
|
var CapabilityToolRegistry = class {
|
|
@@ -14476,17 +14913,17 @@ var CapabilityToolRegistry = class {
|
|
|
14476
14913
|
// src/middlewares/capabilityGuardMiddleware.ts
|
|
14477
14914
|
import { ToolMessage as ToolMessage5 } from "@langchain/core/messages";
|
|
14478
14915
|
import { createMiddleware as createMiddleware13 } from "langchain";
|
|
14479
|
-
function
|
|
14916
|
+
function isRecord9(value) {
|
|
14480
14917
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14481
14918
|
}
|
|
14482
14919
|
function capabilityRuntimeFromRequest(request) {
|
|
14483
|
-
if (!
|
|
14920
|
+
if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
|
|
14484
14921
|
const configurable = request.runtime.configurable;
|
|
14485
14922
|
const context = request.runtime.context;
|
|
14486
|
-
const runConfig =
|
|
14923
|
+
const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
|
|
14487
14924
|
if (!runConfig) return void 0;
|
|
14488
14925
|
const value = runConfig.capabilityRuntime;
|
|
14489
|
-
if (!
|
|
14926
|
+
if (!isRecord9(value) || typeof value.revision !== "string" || !isRecord9(value.middleware)) {
|
|
14490
14927
|
return void 0;
|
|
14491
14928
|
}
|
|
14492
14929
|
return value;
|
|
@@ -14498,10 +14935,10 @@ function hasActiveRuntimePolicy(runtime) {
|
|
|
14498
14935
|
return Object.keys(runtime.middleware).length > 0;
|
|
14499
14936
|
}
|
|
14500
14937
|
function tenantIdFromRequest(request) {
|
|
14501
|
-
if (!
|
|
14938
|
+
if (!isRecord9(request) || !isRecord9(request.runtime)) return void 0;
|
|
14502
14939
|
const configurable = request.runtime.configurable;
|
|
14503
14940
|
const context = request.runtime.context;
|
|
14504
|
-
const runConfig =
|
|
14941
|
+
const runConfig = isRecord9(configurable) && isRecord9(configurable.runConfig) ? configurable.runConfig : isRecord9(context) && isRecord9(context.runConfig) ? context.runConfig : void 0;
|
|
14505
14942
|
return runConfig && typeof runConfig.tenantId === "string" ? runConfig.tenantId : void 0;
|
|
14506
14943
|
}
|
|
14507
14944
|
async function standardConnectionAllowed(request, capabilityType, config) {
|
|
@@ -14509,8 +14946,8 @@ async function standardConnectionAllowed(request, capabilityType, config) {
|
|
|
14509
14946
|
const hasConnectAll = Object.prototype.hasOwnProperty.call(config, "connectAll");
|
|
14510
14947
|
if (!hasConnections && (!hasConnectAll || config.connectAll !== true)) return false;
|
|
14511
14948
|
const tenantId2 = tenantIdFromRequest(request);
|
|
14512
|
-
const resource =
|
|
14513
|
-
const hasConnectionKey =
|
|
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(
|
|
14514
14951
|
request.toolCall.args,
|
|
14515
14952
|
"connectionKey"
|
|
14516
14953
|
);
|
|
@@ -14576,10 +15013,10 @@ function createCapabilityGuardMiddleware(registry4) {
|
|
|
14576
15013
|
toolName: toolName2
|
|
14577
15014
|
});
|
|
14578
15015
|
}
|
|
14579
|
-
const standardConfig =
|
|
15016
|
+
const standardConfig = isRecord9(policy.config) ? policy.config : {};
|
|
14580
15017
|
if (registry4.isStandardConnectionTool(toolName2) && !await standardConnectionAllowed(request, metadata.capabilityType, standardConfig)) {
|
|
14581
15018
|
const args2 = request.toolCall.args;
|
|
14582
|
-
const resource =
|
|
15019
|
+
const resource = isRecord9(args2) && typeof args2.connectionKey === "string" ? args2.connectionKey : void 0;
|
|
14583
15020
|
return denialMessage(request.toolCall, {
|
|
14584
15021
|
code: "RESOURCE_NOT_ALLOWED",
|
|
14585
15022
|
message: typeof resource === "string" ? `Resource '${resource}' is not allowed by the capability policy` : "Resource selector 'connectionKey' must be a permitted string",
|
|
@@ -14592,8 +15029,8 @@ function createCapabilityGuardMiddleware(registry4) {
|
|
|
14592
15029
|
const selector = metadata.resourceSelector;
|
|
14593
15030
|
const args = request.toolCall.args;
|
|
14594
15031
|
if (selector) {
|
|
14595
|
-
const resource =
|
|
14596
|
-
const config =
|
|
15032
|
+
const resource = isRecord9(args) ? args[selector.argumentField] : void 0;
|
|
15033
|
+
const config = isRecord9(policy.config) ? policy.config : {};
|
|
14597
15034
|
const hasExplicitResources = Object.prototype.hasOwnProperty.call(config, selector.configField);
|
|
14598
15035
|
const allowsAll = !hasExplicitResources && selector.allowAllField !== void 0 && config[selector.allowAllField] === true;
|
|
14599
15036
|
const allowedResources = config[selector.configField];
|
|
@@ -14810,13 +15247,7 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
|
|
|
14810
15247
|
));
|
|
14811
15248
|
}
|
|
14812
15249
|
}
|
|
14813
|
-
|
|
14814
|
-
try {
|
|
14815
|
-
tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2 ?? "default")).plugins;
|
|
14816
|
-
} catch {
|
|
14817
|
-
tenantPlugins = [];
|
|
14818
|
-
}
|
|
14819
|
-
const globalPlugins = PluginRegistry.list().map((type) => PluginRegistry.get(type)).filter((p) => Boolean(p));
|
|
15250
|
+
const { globalPlugins, tenantPlugins } = await collectPluginSources(tenantId2 ?? "default");
|
|
14820
15251
|
const mergedPlugins = mergePluginSources(globalPlugins, tenantPlugins);
|
|
14821
15252
|
const mergedByType = new Map(mergedPlugins.map((p) => [p.meta.type, p]));
|
|
14822
15253
|
const enabledPluginTypes = new Set(
|
|
@@ -29324,10 +29755,8 @@ function getAvailableTools(tenantId2) {
|
|
|
29324
29755
|
function getAvailableModels() {
|
|
29325
29756
|
return modelLatticeManager.getAllByTenant("default");
|
|
29326
29757
|
}
|
|
29327
|
-
function getAvailableMiddlewareTypes() {
|
|
29328
|
-
return
|
|
29329
|
-
(type) => typeof PluginRegistry.get(type)?.middleware === "function"
|
|
29330
|
-
);
|
|
29758
|
+
async function getAvailableMiddlewareTypes(tenantId2) {
|
|
29759
|
+
return (await listTenantPlugins(tenantId2)).filter((plugin) => typeof plugin.middleware === "function").map((plugin) => plugin.meta.type);
|
|
29331
29760
|
}
|
|
29332
29761
|
function normalizeProjectScope(projectId) {
|
|
29333
29762
|
return !projectId || projectId === "default" ? "default" : projectId;
|
|
@@ -29366,7 +29795,7 @@ function requireSkillDeclaration(skillLoaded) {
|
|
|
29366
29795
|
}
|
|
29367
29796
|
async function validateAgentReferences(input, tenantId2) {
|
|
29368
29797
|
const availableTools = getAvailableTools(tenantId2).map((tool68) => tool68.key);
|
|
29369
|
-
const availableMiddlewareTypes = getAvailableMiddlewareTypes();
|
|
29798
|
+
const availableMiddlewareTypes = await getAvailableMiddlewareTypes(tenantId2);
|
|
29370
29799
|
const availableCustomMiddlewareKeys = CustomMiddlewareRegistry.list().filter(
|
|
29371
29800
|
(key5) => CustomMiddlewareRegistry.get(key5) !== void 0
|
|
29372
29801
|
);
|
|
@@ -30046,9 +30475,10 @@ registerToolLattice(
|
|
|
30046
30475
|
description: "\u5217\u51FA\u5F53\u524D\u7CFB\u7EDF\u4E2D\u6240\u6709\u53EF\u7528\u7684\u4E2D\u95F4\u4EF6\u7C7B\u578B\uFF08Middlewares\uFF09\uFF0C\u5305\u62EC\u5185\u7F6E\u548C\u81EA\u5B9A\u4E49\u63D2\u4EF6\u3002\u8FD4\u56DE\u6BCF\u4E2A\u4E2D\u95F4\u4EF6\u7684 type\u3001name\u3001description\u3001tools \u6E05\u5355\uFF08\u652F\u6301 allowedTools \u8FC7\u6EE4\uFF09\u3001configSchema\uFF08\u914D\u7F6E\u9762\u677F\u9700\u8981\u54EA\u4E9B\u5B57\u6BB5\uFF09\u548C connectionSchema\uFF08\u662F\u5426\u652F\u6301\u8FDE\u63A5\u6D4B\u8BD5\u548C\u8D44\u6E90\u53D1\u73B0\uFF09\u3002\n\n\u4F7F\u7528\u573A\u666F\uFF1A\n1. \u5728\u521B\u5EFA agent \u524D\uFF0C\u5148\u8C03\u6B64\u5DE5\u5177\u4E86\u89E3\u6709\u54EA\u4E9B\u4E2D\u95F4\u4EF6\u53EF\u914D\u7F6E\n2. \u6839\u636E configSchema \u51B3\u5B9A\u9700\u8981\u63D0\u4F9B\u54EA\u4E9B\u914D\u7F6E\u5B57\u6BB5\uFF1B\u65B0\u8FDE\u63A5\u578B\u63D2\u4EF6\u4F7F\u7528 connections \u548C connectAll\uFF0C\u5185\u7F6E\u65E7\u63D2\u4EF6\u7684 selector \u5B57\u6BB5\u7531 core \u7684 legacy compatibility map \u5B9A\u4E49\n3. \u5982\u679C\u67D0\u4E2A\u4E2D\u95F4\u4EF6\u7684 connectionSchema \u5B58\u5728\uFF0C\u8BF4\u660E\u5B83\u662F\u8FDE\u63A5\u578B\u4E2D\u95F4\u4EF6\uFF0C\u9700\u8981\u518D\u8C03 list_connections \u83B7\u53D6\u53EF\u7528\u8FDE\u63A5\n4. \u7528\u8FD4\u56DE\u7684 type \u5B57\u6BB5\u6784\u5EFA middleware \u6570\u7EC4\u4F20\u7ED9 create_agent / update_agent",
|
|
30047
30476
|
schema: z52.object({})
|
|
30048
30477
|
},
|
|
30049
|
-
async () => {
|
|
30050
|
-
const
|
|
30051
|
-
const
|
|
30478
|
+
async (_input, exeConfig) => {
|
|
30479
|
+
const tenantId2 = getTenantId2(exeConfig);
|
|
30480
|
+
const plugins = await listTenantPlugins(tenantId2);
|
|
30481
|
+
const metas = plugins.filter((plugin) => typeof plugin.middleware === "function").map(serializePluginMeta);
|
|
30052
30482
|
return JSON.stringify(metas);
|
|
30053
30483
|
}
|
|
30054
30484
|
);
|
|
@@ -30359,14 +30789,7 @@ function ensureBuiltinAgentsForTenant(tenantId2) {
|
|
|
30359
30789
|
|
|
30360
30790
|
// src/agent_lattice/pluginAgents.ts
|
|
30361
30791
|
async function ensurePluginAgentsForTenant(tenantId2) {
|
|
30362
|
-
const
|
|
30363
|
-
let tenantPlugins = [];
|
|
30364
|
-
try {
|
|
30365
|
-
tenantPlugins = (await getSandboxPluginCatalog().ensureLoaded(tenantId2)).plugins;
|
|
30366
|
-
} catch {
|
|
30367
|
-
tenantPlugins = [];
|
|
30368
|
-
}
|
|
30369
|
-
for (const plugin of mergePluginSources(globalPlugins, tenantPlugins)) {
|
|
30792
|
+
for (const plugin of await listTenantPlugins(tenantId2)) {
|
|
30370
30793
|
if (!plugin.agents) continue;
|
|
30371
30794
|
for (const [key5, config] of Object.entries(plugin.agents)) {
|
|
30372
30795
|
try {
|
|
@@ -30394,6 +30817,7 @@ function assistantToConfig(assistant) {
|
|
|
30394
30817
|
prompt: typeof assistant.graphDefinition === "string" ? assistant.graphDefinition : JSON.stringify(assistant.graphDefinition)
|
|
30395
30818
|
};
|
|
30396
30819
|
Object.assign(config, graphDef);
|
|
30820
|
+
if (assistant.ownerUserId !== void 0) config.ownerUserId = assistant.ownerUserId;
|
|
30397
30821
|
return config;
|
|
30398
30822
|
}
|
|
30399
30823
|
var _AgentLatticeManager = class _AgentLatticeManager extends BaseLatticeManager {
|
|
@@ -32786,6 +33210,9 @@ var MicrosandboxRemoteInstance = class {
|
|
|
32786
33210
|
}
|
|
32787
33211
|
return "unknown";
|
|
32788
33212
|
}
|
|
33213
|
+
async getEndpoint(port) {
|
|
33214
|
+
return this.client.getEndpoint(this.name, port);
|
|
33215
|
+
}
|
|
32789
33216
|
/**
|
|
32790
33217
|
* Pack a sandbox folder into a sibling ZIP through the microsandbox shell.
|
|
32791
33218
|
*
|
|
@@ -32852,6 +33279,19 @@ var MicrosandboxServiceClient = class {
|
|
|
32852
33279
|
method: "GET"
|
|
32853
33280
|
});
|
|
32854
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
|
+
}
|
|
32855
33295
|
async readFile(sandboxName, path8) {
|
|
32856
33296
|
return this.request("/api/files/read", {
|
|
32857
33297
|
method: "POST",
|
|
@@ -33116,9 +33556,9 @@ var MicrosandboxRemoteProvider = class {
|
|
|
33116
33556
|
}
|
|
33117
33557
|
buildEnsureInput(config) {
|
|
33118
33558
|
const defaultMicrosandboxRemoteConfig = getDefaultMicrosandboxRemoteConfig();
|
|
33119
|
-
const image = this.config.image ?? defaultMicrosandboxRemoteConfig.image;
|
|
33559
|
+
const image = config.image ?? this.config.image ?? defaultMicrosandboxRemoteConfig.image;
|
|
33120
33560
|
console.log(
|
|
33121
|
-
`[MicrosandboxRemote] buildEnsureInput image=${image} from=${this.config.image ? "providerConfig" : "defaultConfig"}`
|
|
33561
|
+
`[MicrosandboxRemote] buildEnsureInput image=${image} from=${config.image ? "runConfig" : this.config.image ? "providerConfig" : "defaultConfig"}`
|
|
33122
33562
|
);
|
|
33123
33563
|
return {
|
|
33124
33564
|
image,
|
|
@@ -33128,7 +33568,10 @@ var MicrosandboxRemoteProvider = class {
|
|
|
33128
33568
|
...this.config.env,
|
|
33129
33569
|
...buildSandboxMetadataEnv(config)
|
|
33130
33570
|
},
|
|
33131
|
-
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
|
|
33132
33575
|
};
|
|
33133
33576
|
}
|
|
33134
33577
|
buildDefaultVolumes(config) {
|
|
@@ -34854,6 +35297,157 @@ async function connectAllChannels(getAdapter, options = {}) {
|
|
|
34854
35297
|
}
|
|
34855
35298
|
}
|
|
34856
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
|
+
};
|
|
34857
35451
|
|
|
34858
35452
|
// src/eval_lattice/LatticeEval.ts
|
|
34859
35453
|
import { HumanMessage as HumanMessage5 } from "@langchain/core/messages";
|
|
@@ -36314,7 +36908,7 @@ function getCoordinationLeaseStore() {
|
|
|
36314
36908
|
}
|
|
36315
36909
|
|
|
36316
36910
|
// src/services/withLease.ts
|
|
36317
|
-
import { randomUUID as
|
|
36911
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
36318
36912
|
var LeaseAcquisitionTimeoutError = class extends Error {
|
|
36319
36913
|
/**
|
|
36320
36914
|
* @param key Lease key that could not be acquired.
|
|
@@ -36339,7 +36933,7 @@ async function withLease(store, key5, ownerLabel, fn, options = {}) {
|
|
|
36339
36933
|
const acquireTimeoutMs = options.acquireTimeoutMs ?? 1e4;
|
|
36340
36934
|
const retryDelayMs = options.retryDelayMs ?? 50;
|
|
36341
36935
|
const heartbeatMs = options.heartbeatMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
|
|
36342
|
-
const holder = `${ownerLabel}#${
|
|
36936
|
+
const holder = `${ownerLabel}#${randomUUID9()}`;
|
|
36343
36937
|
const deadline = Date.now() + acquireTimeoutMs;
|
|
36344
36938
|
for (; ; ) {
|
|
36345
36939
|
if (await store.tryAcquire(key5, holder, ttlMs)) break;
|
|
@@ -36438,7 +37032,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
|
|
|
36438
37032
|
}
|
|
36439
37033
|
|
|
36440
37034
|
// src/services/RoomAgentMessageService.ts
|
|
36441
|
-
import { randomUUID as
|
|
37035
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
36442
37036
|
import { snapshotExactArray, snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
|
|
36443
37037
|
var INPUT_KEYS = [
|
|
36444
37038
|
"tenantId",
|
|
@@ -36526,7 +37120,7 @@ var RoomAgentMessageService = class {
|
|
|
36526
37120
|
throw new Error("Invalid project room membership");
|
|
36527
37121
|
}
|
|
36528
37122
|
const candidate = {
|
|
36529
|
-
id: (this.deps.idFactory ??
|
|
37123
|
+
id: (this.deps.idFactory ?? randomUUID10)(),
|
|
36530
37124
|
tenantId: input.tenantId,
|
|
36531
37125
|
workspaceId: input.workspaceId,
|
|
36532
37126
|
projectId: input.projectId,
|
|
@@ -39113,7 +39707,7 @@ function createTaskLifecycleService(deps) {
|
|
|
39113
39707
|
}
|
|
39114
39708
|
|
|
39115
39709
|
// src/services/TaskExecutionService.ts
|
|
39116
|
-
import { randomUUID as
|
|
39710
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
39117
39711
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
39118
39712
|
import { isExecutionResultEventKey as isExecutionResultEventKey2 } from "@axiom-lattice/protocols";
|
|
39119
39713
|
|
|
@@ -39436,13 +40030,13 @@ async function reconcileAllTenantTaskRuns(input) {
|
|
|
39436
40030
|
}
|
|
39437
40031
|
|
|
39438
40032
|
// src/services/taskRunLease.ts
|
|
39439
|
-
import { randomUUID as
|
|
40033
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
39440
40034
|
function taskRunLeaseKey(taskId) {
|
|
39441
40035
|
return `task-run:${taskId}`;
|
|
39442
40036
|
}
|
|
39443
40037
|
async function tryAcquireTaskRun(input) {
|
|
39444
40038
|
const key5 = taskRunLeaseKey(input.taskId);
|
|
39445
|
-
const holder = `${input.owner}#${
|
|
40039
|
+
const holder = `${input.owner}#${randomUUID11()}`;
|
|
39446
40040
|
if (!await input.store.tryAcquire(key5, holder, input.ttlMs)) return null;
|
|
39447
40041
|
const heartbeatMs = input.heartbeatMs ?? Math.max(1e3, Math.floor(input.ttlMs / 3));
|
|
39448
40042
|
let released = false;
|
|
@@ -39809,11 +40403,11 @@ function createTaskExecutionService(deps) {
|
|
|
39809
40403
|
let threadId;
|
|
39810
40404
|
let runId;
|
|
39811
40405
|
try {
|
|
39812
|
-
threadId = storedThreadId ?? (deps.createThreadId ??
|
|
40406
|
+
threadId = storedThreadId ?? (deps.createThreadId ?? randomUUID12)();
|
|
39813
40407
|
if (task.status === "interrupted" && reuseCurrentRun) {
|
|
39814
40408
|
runId = storedRunId;
|
|
39815
40409
|
} else if (task.status === "interrupted" || task.status === "failed") {
|
|
39816
|
-
runId = (deps.createRunId ??
|
|
40410
|
+
runId = (deps.createRunId ?? randomUUID12)();
|
|
39817
40411
|
}
|
|
39818
40412
|
} catch (error) {
|
|
39819
40413
|
return {
|
|
@@ -40072,11 +40666,11 @@ function createTaskExecutionService(deps) {
|
|
|
40072
40666
|
}
|
|
40073
40667
|
|
|
40074
40668
|
// src/services/leadership.ts
|
|
40075
|
-
import { randomUUID as
|
|
40669
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
40076
40670
|
async function runAsLeader(store, name, fn, options = {}) {
|
|
40077
40671
|
const key5 = `leader:${name}`;
|
|
40078
40672
|
const owner2 = options.owner ?? getReplicaId();
|
|
40079
|
-
const holder = `${owner2}#${
|
|
40673
|
+
const holder = `${owner2}#${randomUUID13()}`;
|
|
40080
40674
|
const ttlMs = options.ttlMs ?? 6e4;
|
|
40081
40675
|
if (!await store.tryAcquire(key5, holder, ttlMs)) return { ran: false };
|
|
40082
40676
|
const heartbeat = setInterval(() => {
|
|
@@ -41309,16 +41903,16 @@ function createAskUserToClarifyTool() {
|
|
|
41309
41903
|
}
|
|
41310
41904
|
|
|
41311
41905
|
// src/middlewares/askUserClarifyMiddleware.ts
|
|
41312
|
-
function
|
|
41906
|
+
function isRecord10(value) {
|
|
41313
41907
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41314
41908
|
}
|
|
41315
41909
|
function isProjectRoomRun(request) {
|
|
41316
|
-
if (!
|
|
41910
|
+
if (!isRecord10(request) || !isRecord10(request.runtime)) return false;
|
|
41317
41911
|
const runtime = request.runtime;
|
|
41318
|
-
const configurable =
|
|
41319
|
-
const context =
|
|
41320
|
-
const runConfig =
|
|
41321
|
-
return
|
|
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);
|
|
41322
41916
|
}
|
|
41323
41917
|
function createAskUserClarifyMiddleware() {
|
|
41324
41918
|
return createMiddleware21({
|
|
@@ -42763,15 +43357,16 @@ function executorString(value) {
|
|
|
42763
43357
|
}
|
|
42764
43358
|
async function renderAvailableExecutors(rc, tenantId2) {
|
|
42765
43359
|
const authority = classifyTaskAuthority(rc);
|
|
42766
|
-
const
|
|
42767
|
-
const
|
|
42768
|
-
const
|
|
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"}`;
|
|
42769
43364
|
const cached2 = executorCache.get(cacheKey);
|
|
42770
43365
|
const now = Date.now();
|
|
42771
43366
|
if (cached2 && now - cached2.at < EXECUTOR_CACHE_TTL_MS) return cached2.text;
|
|
42772
43367
|
const lines = [];
|
|
42773
43368
|
try {
|
|
42774
|
-
if (
|
|
43369
|
+
if (useRoster && projectId) {
|
|
42775
43370
|
const store = getStoreLattice("default", "projectBotMembership").store;
|
|
42776
43371
|
if (typeof store.list === "function") {
|
|
42777
43372
|
const bots = await store.list(tenantId2, projectId);
|
|
@@ -42786,11 +43381,15 @@ async function renderAvailableExecutors(rc, tenantId2) {
|
|
|
42786
43381
|
}
|
|
42787
43382
|
}
|
|
42788
43383
|
} else {
|
|
43384
|
+
const exempt = trusted !== void 0 && trustedRosterExempt(trusted) ? trusted : void 0;
|
|
43385
|
+
const excluded = exempt ? await excludedRosterExecutors(exempt) : /* @__PURE__ */ new Set();
|
|
42789
43386
|
const configs = await agentLatticeManager.getAllAgentConfigsByTenant(tenantId2);
|
|
42790
43387
|
for (const config of configs.slice(0, 40)) {
|
|
42791
43388
|
const row = config;
|
|
42792
43389
|
const id = executorString(row.key);
|
|
42793
43390
|
if (!id) continue;
|
|
43391
|
+
if (row.ownerUserId !== void 0) continue;
|
|
43392
|
+
if (excluded.has(id)) continue;
|
|
42794
43393
|
const name = executorString(row.name);
|
|
42795
43394
|
const description = executorString(row.description);
|
|
42796
43395
|
lines.push(`- ${id}${name ? ` \u2014 ${name}` : ""}${description ? ` (${description.slice(0, 120)})` : ""}`);
|
|
@@ -42802,7 +43401,7 @@ async function renderAvailableExecutors(rc, tenantId2) {
|
|
|
42802
43401
|
executorCache.set(cacheKey, { at: now, text: "" });
|
|
42803
43402
|
return "";
|
|
42804
43403
|
}
|
|
42805
|
-
const header =
|
|
43404
|
+
const header = useRoster ? "## Available task executors (Project roster)" : "## Available task executors";
|
|
42806
43405
|
const text = `
|
|
42807
43406
|
|
|
42808
43407
|
${header}
|
|
@@ -42814,6 +43413,22 @@ ${lines.join("\n")}
|
|
|
42814
43413
|
executorCache.set(cacheKey, { at: now, text });
|
|
42815
43414
|
return text;
|
|
42816
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
|
+
}
|
|
42817
43432
|
function delegatedTaskScopeError(taskId) {
|
|
42818
43433
|
return JSON.stringify({
|
|
42819
43434
|
success: false,
|
|
@@ -42863,7 +43478,7 @@ function trustedRosterScope(trusted) {
|
|
|
42863
43478
|
};
|
|
42864
43479
|
}
|
|
42865
43480
|
function trustedRosterExempt(trusted) {
|
|
42866
|
-
return trusted.projectTask?.rosterExempt === true;
|
|
43481
|
+
return trusted.projectTask?.rosterExempt === true || trusted.projectRoom?.rosterExempt === true;
|
|
42867
43482
|
}
|
|
42868
43483
|
async function requireTrustedOwner(trusted, targetAssistantId) {
|
|
42869
43484
|
const context = trusted.projectTask ?? trusted.projectRoom;
|
|
@@ -42879,6 +43494,17 @@ async function requireTrustedOwner(trusted, targetAssistantId) {
|
|
|
42879
43494
|
targetAssistantId
|
|
42880
43495
|
});
|
|
42881
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
|
+
}
|
|
42882
43508
|
function trustedTaskContextFailure(task, trusted) {
|
|
42883
43509
|
if (task.tenantId === trusted.tenantId && task.workspaceId === trusted.workspaceId && task.projectId === trusted.projectId) return void 0;
|
|
42884
43510
|
return JSON.stringify({
|
|
@@ -43483,6 +44109,12 @@ function createTaskMiddleware(options = {}) {
|
|
|
43483
44109
|
"Target Project task owner is not active in the exact roster."
|
|
43484
44110
|
));
|
|
43485
44111
|
}
|
|
44112
|
+
} else {
|
|
44113
|
+
const ownerError = await rosterExemptOwnerError(
|
|
44114
|
+
(trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
|
|
44115
|
+
ownerId
|
|
44116
|
+
);
|
|
44117
|
+
if (ownerError) return ownerError;
|
|
43486
44118
|
}
|
|
43487
44119
|
}
|
|
43488
44120
|
if (!delegatedTaskId && (typeof effectiveOwnerId !== "string" || effectiveOwnerId.length === 0)) {
|
|
@@ -43544,6 +44176,12 @@ function createTaskMiddleware(options = {}) {
|
|
|
43544
44176
|
} catch (error) {
|
|
43545
44177
|
return projectPolicyError(error);
|
|
43546
44178
|
}
|
|
44179
|
+
} else {
|
|
44180
|
+
const ownerError = await rosterExemptOwnerError(
|
|
44181
|
+
(trustedProject.projectTask ?? trustedProject.projectRoom)?.tenantId ?? tenantId2,
|
|
44182
|
+
ownerId
|
|
44183
|
+
);
|
|
44184
|
+
if (ownerError) return ownerError;
|
|
43547
44185
|
}
|
|
43548
44186
|
}
|
|
43549
44187
|
return store.create({
|
|
@@ -44591,7 +45229,7 @@ function createTaskMiddleware(options = {}) {
|
|
|
44591
45229
|
}
|
|
44592
45230
|
const callerAssistantId = rc.assistant_id;
|
|
44593
45231
|
const agentId = input.agentId === "general-purpose" && callerAssistantId ? `${callerAssistantId}-general-purpose` : input.agentId;
|
|
44594
|
-
const
|
|
45232
|
+
const service3 = createTaskExecutionService({
|
|
44595
45233
|
taskStore: getTaskStore(),
|
|
44596
45234
|
workItemStore: getTaskWorkItemStore(),
|
|
44597
45235
|
resolveAgent: async (targetTenantId, agentId2) => !!await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId2),
|
|
@@ -44616,7 +45254,7 @@ function createTaskMiddleware(options = {}) {
|
|
|
44616
45254
|
}
|
|
44617
45255
|
});
|
|
44618
45256
|
const trustedCaller = authority.kind === "projectTask" || authority.kind === "projectRoom" ? authority.trusted.projectTask ?? authority.trusted.projectRoom : void 0;
|
|
44619
|
-
return JSON.stringify(await
|
|
45257
|
+
return JSON.stringify(await service3.start({
|
|
44620
45258
|
tenantId: tenantId2,
|
|
44621
45259
|
workspaceId: rc.workspaceId,
|
|
44622
45260
|
projectId: rc.projectId,
|
|
@@ -46138,6 +46776,24 @@ function snapshotMembership3(value) {
|
|
|
46138
46776
|
function snapshotRows(value) {
|
|
46139
46777
|
return snapshotExactArray2(value) ?? [];
|
|
46140
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
|
+
}
|
|
46141
46797
|
async function hasActiveCurrentMembership(scope) {
|
|
46142
46798
|
const memberships = getStoreLattice("default", "projectBotMembership").store;
|
|
46143
46799
|
const member = snapshotMembership3(await memberships.findById(scope.tenantId, scope.membershipId));
|
|
@@ -46154,11 +46810,11 @@ function createProjectRoomMiddleware() {
|
|
|
46154
46810
|
if (!scope) return CONTEXT_REQUIRED;
|
|
46155
46811
|
const digest = createHash7("sha256").update(text).digest("hex");
|
|
46156
46812
|
try {
|
|
46157
|
-
const
|
|
46813
|
+
const service3 = new RoomAgentMessageService({
|
|
46158
46814
|
memberships: getStoreLattice("default", "projectBotMembership").store,
|
|
46159
46815
|
messages: getStoreLattice("default", "projectRoomMessage").store
|
|
46160
46816
|
});
|
|
46161
|
-
await
|
|
46817
|
+
await service3.post({
|
|
46162
46818
|
tenantId: scope.tenantId,
|
|
46163
46819
|
workspaceId: scope.workspaceId,
|
|
46164
46820
|
projectId: scope.projectId,
|
|
@@ -46184,6 +46840,25 @@ function createProjectRoomMiddleware() {
|
|
|
46184
46840
|
if (!scope) return CONTEXT_REQUIRED;
|
|
46185
46841
|
try {
|
|
46186
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
|
+
}
|
|
46187
46862
|
const rows = await getStoreLattice("default", "projectBotMembership").store.list(scope.tenantId, scope.projectId);
|
|
46188
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) => ({
|
|
46189
46864
|
assistantId: member.assistantId,
|
|
@@ -46286,13 +46961,13 @@ var TEST_TEMPLATE = `const data = process.env.PLUGIN_CONNECTION_FILE
|
|
|
46286
46961
|
: {};
|
|
46287
46962
|
const conn = data.connection ?? data.connections?.[0]?.config ?? {};
|
|
46288
46963
|
const message = conn.apiKey ? "ok" : "missing apiKey";
|
|
46289
|
-
process.stdout.write(JSON.stringify({ ok: Boolean(conn.apiKey), message }));
|
|
46964
|
+
process.stdout.write(JSON.stringify({ ok: true, result: { ok: Boolean(conn.apiKey), message } }));
|
|
46290
46965
|
`;
|
|
46291
46966
|
var DISCOVER_TEMPLATE = `const data = process.env.PLUGIN_CONNECTION_FILE
|
|
46292
46967
|
? JSON.parse(require("fs").readFileSync(process.env.PLUGIN_CONNECTION_FILE, "utf8"))
|
|
46293
46968
|
: {};
|
|
46294
46969
|
const conn = data.connection ?? data.connections?.[0]?.config ?? {};
|
|
46295
|
-
process.stdout.write(JSON.stringify([{ id: "sample", name: "Sample resource" }]));
|
|
46970
|
+
process.stdout.write(JSON.stringify({ ok: true, result: [{ id: "sample", name: "Sample resource" }] }));
|
|
46296
46971
|
`;
|
|
46297
46972
|
var UI_TEMPLATE = `<!doctype html>
|
|
46298
46973
|
<html>
|
|
@@ -46845,7 +47520,7 @@ captured).
|
|
|
46845
47520
|
}
|
|
46846
47521
|
},
|
|
46847
47522
|
"sandbox-plugins-connection": {
|
|
46848
|
-
version: "1.
|
|
47523
|
+
version: "1.1.0",
|
|
46849
47524
|
content: `---
|
|
46850
47525
|
name: sandbox-plugins-connection
|
|
46851
47526
|
description: Define connection fields and test/discover handlers for a tenant sandbox plugin. Trigger: plugin needs credentials or resource discovery.
|
|
@@ -46861,17 +47536,20 @@ The resolved connection is injected at execution time as an
|
|
|
46861
47536
|
\`PLUGIN_CONNECTION_FILE\` (never inline; credentials stay out of argv). It is never hardcoded.
|
|
46862
47537
|
|
|
46863
47538
|
## test handler
|
|
46864
|
-
Print \`{ ok, message }\`:
|
|
47539
|
+
Print the two-level result envelope \`{ ok, result: { ok, message } }\`: the outer
|
|
47540
|
+
\`ok\` means the handler ran, the inner \`ok\`/\`message\` is the connection verdict.
|
|
47541
|
+
Put extra diagnostics under \`result.details\`. A single-level \`{ ok, message }\`
|
|
47542
|
+
is rejected as an invalid result.
|
|
46865
47543
|
\`\`\`js
|
|
46866
47544
|
const data = JSON.parse(require("fs").readFileSync(process.env.PLUGIN_CONNECTION_FILE, "utf8"));
|
|
46867
47545
|
const conn = data.connection ?? data.connections?.[0]?.config ?? {};
|
|
46868
|
-
process.stdout.write(JSON.stringify({ ok: Boolean(conn.apiKey), message: conn.apiKey ? "ok" : "missing apiKey" }));
|
|
47546
|
+
process.stdout.write(JSON.stringify({ ok: true, result: { ok: Boolean(conn.apiKey), message: conn.apiKey ? "ok" : "missing apiKey" } }));
|
|
46869
47547
|
\`\`\`
|
|
46870
47548
|
|
|
46871
47549
|
## discover handler
|
|
46872
|
-
Print
|
|
47550
|
+
Print \`{ ok: true, result: [ ...resources ] }\`:
|
|
46873
47551
|
\`\`\`js
|
|
46874
|
-
process.stdout.write(JSON.stringify([{ id: "a", name: "A", group: "G" }]));
|
|
47552
|
+
process.stdout.write(JSON.stringify({ ok: true, result: [{ id: "a", name: "A", group: "G" }] }));
|
|
46875
47553
|
\`\`\`
|
|
46876
47554
|
|
|
46877
47555
|
Set \`connection.resourceLabel\` for the selection panel title.
|
|
@@ -46928,6 +47606,70 @@ Fall back to \`execute_code\`/\`shell\` to run \`node handlers/x.js\` manually w
|
|
|
46928
47606
|
\`PLUGIN_INPUT\` and a synthetic \`PLUGIN_CONNECTION_FILE\` to reproduce logic bugs.
|
|
46929
47607
|
A synthetic fixture only verifies plumbing \u2014 a real upstream call requires a real
|
|
46930
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.
|
|
46931
47673
|
`,
|
|
46932
47674
|
resources: {}
|
|
46933
47675
|
}
|
|
@@ -46964,9 +47706,197 @@ the connector and give you its key. Only after a real \`connectionKey\` test
|
|
|
46964
47706
|
passes may you call the tool verified; without it, report functionality as
|
|
46965
47707
|
unverified (missing credentials), not as passing. Use \`shell_exec\` (the tool
|
|
46966
47708
|
provided by the \`code_eval\` middleware) to run \`node handlers/x.js\` for manual
|
|
46967
|
-
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 { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[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
|
+
`;
|
|
46968
47892
|
|
|
46969
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
|
+
};
|
|
46970
47900
|
var PLUGIN_BUILDER_SKILLS = [...Object.keys(SANDBOX_PLUGIN_SKILLS), "task-tracking"];
|
|
46971
47901
|
function tenantIdFrom(exeConfig) {
|
|
46972
47902
|
const c = exeConfig;
|
|
@@ -46984,6 +47914,15 @@ function collectSecretValues(connection, fields) {
|
|
|
46984
47914
|
function errorText(err) {
|
|
46985
47915
|
return err instanceof Error ? err.message : String(err);
|
|
46986
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
|
+
}
|
|
46987
47926
|
function redactString(value, secrets) {
|
|
46988
47927
|
let out = value;
|
|
46989
47928
|
for (const secret of secrets) out = out.split(secret).join("[redacted]");
|
|
@@ -47004,10 +47943,24 @@ function redactSecretValues(payload, secrets) {
|
|
|
47004
47943
|
return walk(payload);
|
|
47005
47944
|
}
|
|
47006
47945
|
var managePluginSchema = z73.object({
|
|
47007
|
-
action: z73.enum([
|
|
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
|
+
]),
|
|
47008
47960
|
type: z73.string().optional(),
|
|
47009
47961
|
manifest: z73.record(z73.unknown()).optional(),
|
|
47010
47962
|
handlers: z73.record(z73.string()).optional(),
|
|
47963
|
+
mode: z73.enum(["onDemand", "alwaysOn"]).optional(),
|
|
47011
47964
|
target: z73.object({
|
|
47012
47965
|
kind: z73.enum(["tool", "connection-test", "connection-discover"]),
|
|
47013
47966
|
name: z73.string().optional(),
|
|
@@ -47020,9 +47973,18 @@ var sandboxPluginsPlugin = {
|
|
|
47020
47973
|
type: "sandbox-plugins",
|
|
47021
47974
|
category: "execution",
|
|
47022
47975
|
name: "Sandbox Plugins",
|
|
47023
|
-
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.",
|
|
47024
47977
|
recommends: ["skill"],
|
|
47025
|
-
tools: [
|
|
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
|
+
},
|
|
47026
47988
|
configSchema: { type: "object", properties: {} },
|
|
47027
47989
|
defaultConfig: {}
|
|
47028
47990
|
},
|
|
@@ -47043,7 +48005,7 @@ var sandboxPluginsPlugin = {
|
|
|
47043
48005
|
id: "sandbox-plugins",
|
|
47044
48006
|
type: "sandbox-plugins",
|
|
47045
48007
|
name: "Sandbox Plugins",
|
|
47046
|
-
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)",
|
|
47047
48009
|
enabled: true,
|
|
47048
48010
|
config: {}
|
|
47049
48011
|
},
|
|
@@ -47239,13 +48201,74 @@ var sandboxPluginsPlugin = {
|
|
|
47239
48201
|
return JSON.stringify(redactSecretValues({ ok: false, error: errorText(err) }, secrets));
|
|
47240
48202
|
}
|
|
47241
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
|
+
}
|
|
47242
48265
|
default:
|
|
47243
48266
|
return `unsupported action: ${input.action}`;
|
|
47244
48267
|
}
|
|
47245
48268
|
},
|
|
47246
48269
|
{
|
|
47247
48270
|
name: "manage_plugin",
|
|
47248
|
-
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.",
|
|
47249
48272
|
schema: managePluginSchema
|
|
47250
48273
|
}
|
|
47251
48274
|
)
|
|
@@ -47300,6 +48323,526 @@ function buildBundleFromDist(distDir, version) {
|
|
|
47300
48323
|
};
|
|
47301
48324
|
}
|
|
47302
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
|
+
|
|
47303
48846
|
// src/personal_assistant/PersonalAssistantConfig.ts
|
|
47304
48847
|
function deepClone(obj) {
|
|
47305
48848
|
return JSON.parse(JSON.stringify(obj));
|
|
@@ -47684,16 +49227,16 @@ var IdRemapper = class {
|
|
|
47684
49227
|
};
|
|
47685
49228
|
|
|
47686
49229
|
// src/util/directRoomId.ts
|
|
47687
|
-
import { createHash as
|
|
49230
|
+
import { createHash as createHash9 } from "crypto";
|
|
47688
49231
|
function generateDirectHumanProjectId(tenantId2, workspaceId, userId1, userId2) {
|
|
47689
49232
|
const sortedUserIds = [userId1, userId2].sort();
|
|
47690
49233
|
const parts = [tenantId2, workspaceId, ...sortedUserIds];
|
|
47691
|
-
const hash =
|
|
49234
|
+
const hash = createHash9("sha256").update(parts.join(":")).digest("hex");
|
|
47692
49235
|
return `direct_human_${hash}`;
|
|
47693
49236
|
}
|
|
47694
49237
|
function generateDirectBotProjectId(tenantId2, workspaceId, userId, assistantId) {
|
|
47695
49238
|
const parts = [tenantId2, workspaceId, userId, assistantId];
|
|
47696
|
-
const hash =
|
|
49239
|
+
const hash = createHash9("sha256").update(parts.join(":")).digest("hex");
|
|
47697
49240
|
return `direct_bot_${hash}`;
|
|
47698
49241
|
}
|
|
47699
49242
|
function parseDirectRoomType(projectId) {
|
|
@@ -47722,6 +49265,7 @@ export {
|
|
|
47722
49265
|
CapabilityBundleValidationError,
|
|
47723
49266
|
CapabilityToolNameConflictError,
|
|
47724
49267
|
CapabilityToolRegistry,
|
|
49268
|
+
ChannelConnectionLeader,
|
|
47725
49269
|
ChunkBuffer,
|
|
47726
49270
|
ChunkBufferLatticeManager,
|
|
47727
49271
|
CollectionLatticeManager,
|
|
@@ -47733,7 +49277,9 @@ export {
|
|
|
47733
49277
|
DEFAULT_CALIBRATION_PROBES,
|
|
47734
49278
|
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
47735
49279
|
DEFAULT_DELAY_MS,
|
|
49280
|
+
DEFAULT_HANDLER_TIMEOUT_MS,
|
|
47736
49281
|
DEFAULT_MAX_ATTEMPTS,
|
|
49282
|
+
DEFAULT_RESIDENT_INVOKE_TIMEOUT_MS,
|
|
47737
49283
|
DaytonaInstance,
|
|
47738
49284
|
DaytonaProvider,
|
|
47739
49285
|
DefaultScheduleClient,
|
|
@@ -47751,6 +49297,7 @@ export {
|
|
|
47751
49297
|
HEALTH_CHECK_INTERVAL_MS,
|
|
47752
49298
|
HEALTH_CHECK_TIMEOUT_MS,
|
|
47753
49299
|
HumanMessage7 as HumanMessage,
|
|
49300
|
+
INLINE_INPUT_LIMIT_BYTES2 as INLINE_INPUT_LIMIT_BYTES,
|
|
47754
49301
|
IdRemapper,
|
|
47755
49302
|
InMemoryA2AApiKeyStore,
|
|
47756
49303
|
InMemoryAgentWebAppStore,
|
|
@@ -47791,10 +49338,12 @@ export {
|
|
|
47791
49338
|
LocalSandboxProvider,
|
|
47792
49339
|
LoggerLatticeManager,
|
|
47793
49340
|
MAX_BACKOFF_MS,
|
|
49341
|
+
MAX_HANDLER_TIMEOUT_MS,
|
|
47794
49342
|
MAX_LINE_LENGTH,
|
|
47795
49343
|
MAX_PACK_ENTRIES,
|
|
47796
49344
|
MAX_PACK_FILE_BYTES,
|
|
47797
49345
|
MAX_PACK_TOTAL_BYTES,
|
|
49346
|
+
MIN_HANDLER_TIMEOUT_MS,
|
|
47798
49347
|
ManagedTaskStartError,
|
|
47799
49348
|
McpLatticeManager,
|
|
47800
49349
|
MemoryBackend,
|
|
@@ -47813,8 +49362,12 @@ export {
|
|
|
47813
49362
|
PACK_DIRECTORY_TIMEOUT_SECONDS,
|
|
47814
49363
|
PLATFORM_SECRET_ENV_VARS,
|
|
47815
49364
|
PLUGIN_CATALOG_CHANGED_TOPIC,
|
|
49365
|
+
PLUGIN_HOST_DIR,
|
|
49366
|
+
PLUGIN_HOST_SERVER_SOURCE,
|
|
49367
|
+
PLUGIN_HOST_VERSION,
|
|
47816
49368
|
PersonalAssistantConfig,
|
|
47817
49369
|
PinoLoggerClient,
|
|
49370
|
+
PluginDeploymentRuntimeRegistry,
|
|
47818
49371
|
PluginRegistry,
|
|
47819
49372
|
PostgresDatabase,
|
|
47820
49373
|
ProjectBotMembershipIdConflictError,
|
|
@@ -47876,7 +49429,9 @@ export {
|
|
|
47876
49429
|
appendUiFence,
|
|
47877
49430
|
assertSafeUpstreamUrl,
|
|
47878
49431
|
buildBundleFromDist,
|
|
49432
|
+
buildConnectionPayload2 as buildConnectionPayload,
|
|
47879
49433
|
buildGrepResultsDict,
|
|
49434
|
+
buildHandlerCommand,
|
|
47880
49435
|
buildInput,
|
|
47881
49436
|
buildNamedVolumeName,
|
|
47882
49437
|
buildRemotePackDirectoryCommand,
|
|
@@ -47891,6 +49446,7 @@ export {
|
|
|
47891
49446
|
clearEncryptionKeyCache,
|
|
47892
49447
|
clearEvalRunService,
|
|
47893
49448
|
clearSandboxOpenCredentialProviderForTests,
|
|
49449
|
+
collectPluginSources,
|
|
47894
49450
|
collectionLatticeManager,
|
|
47895
49451
|
compileInternal,
|
|
47896
49452
|
compileSandboxPlugin,
|
|
@@ -47943,9 +49499,11 @@ export {
|
|
|
47943
49499
|
decrypt,
|
|
47944
49500
|
decryptConfigFields,
|
|
47945
49501
|
describeCronExpression,
|
|
49502
|
+
disconnectAllChannels,
|
|
47946
49503
|
documentLearningPlugin,
|
|
47947
49504
|
documentParserPlugin,
|
|
47948
49505
|
embeddingsLatticeManager,
|
|
49506
|
+
encodeInput,
|
|
47949
49507
|
encrypt,
|
|
47950
49508
|
encryptConfigFields,
|
|
47951
49509
|
ensureBuiltinAgentsForTenant,
|
|
@@ -47989,6 +49547,7 @@ export {
|
|
|
47989
49547
|
getModelLattice,
|
|
47990
49548
|
getNextCronTime,
|
|
47991
49549
|
getOrCreateCollectionVectorStore,
|
|
49550
|
+
getPluginDeploymentService,
|
|
47992
49551
|
getPluginSkillManifestVersion,
|
|
47993
49552
|
getQueueLattice,
|
|
47994
49553
|
getReplicaId,
|
|
@@ -48017,6 +49576,8 @@ export {
|
|
|
48017
49576
|
hasChunkBuffer,
|
|
48018
49577
|
hasDelegatedWorkerToken,
|
|
48019
49578
|
hasRuntimeCapabilityPolicy,
|
|
49579
|
+
installPluginHost,
|
|
49580
|
+
invokeResident,
|
|
48020
49581
|
invokeWithRetry,
|
|
48021
49582
|
isBuiltInSkill,
|
|
48022
49583
|
isDirectRoom,
|
|
@@ -48030,6 +49591,7 @@ export {
|
|
|
48030
49591
|
jsonSchemaToZod,
|
|
48031
49592
|
listCollectionEntries,
|
|
48032
49593
|
listSandboxProviderTypes,
|
|
49594
|
+
listTenantPlugins,
|
|
48033
49595
|
loggerLatticeManager,
|
|
48034
49596
|
mcpManager,
|
|
48035
49597
|
mergeCapabilityBundles,
|
|
@@ -48043,6 +49605,8 @@ export {
|
|
|
48043
49605
|
parallelLimit,
|
|
48044
49606
|
parseCronExpression,
|
|
48045
49607
|
parseDirectRoomType,
|
|
49608
|
+
parseHandlerEnvelope,
|
|
49609
|
+
parseHostArgs,
|
|
48046
49610
|
parseJudgeVerdict,
|
|
48047
49611
|
parsePackSize,
|
|
48048
49612
|
parseQueuedExecutionMode,
|
|
@@ -48051,6 +49615,7 @@ export {
|
|
|
48051
49615
|
parseTrustedRunContext,
|
|
48052
49616
|
parseYaml,
|
|
48053
49617
|
performStringReplacement,
|
|
49618
|
+
pluginHostContentHash,
|
|
48054
49619
|
postProcessTaskMutationResponse,
|
|
48055
49620
|
prepareTaskDispatch,
|
|
48056
49621
|
projectMutationMutex,
|
|
@@ -48089,6 +49654,7 @@ export {
|
|
|
48089
49654
|
renderTemplate,
|
|
48090
49655
|
resetReplicaIdForTests,
|
|
48091
49656
|
resolveEnabledPluginSkill,
|
|
49657
|
+
resolveHandlerTimeoutMs,
|
|
48092
49658
|
resolveJudgeModelKey,
|
|
48093
49659
|
resolvePath,
|
|
48094
49660
|
resolvePluginConnections,
|
|
@@ -48108,6 +49674,7 @@ export {
|
|
|
48108
49674
|
setBindingRegistry,
|
|
48109
49675
|
setEvalRunService,
|
|
48110
49676
|
setMenuRegistry,
|
|
49677
|
+
setPluginDeploymentService,
|
|
48111
49678
|
setSandboxPluginCatalog,
|
|
48112
49679
|
skillLatticeManager,
|
|
48113
49680
|
sqlDatabaseManager,
|