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