@praxis-ai/praxis 0.1.1 → 0.1.3
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/agentCore/index.d.ts +45 -6
- package/dist/agentCore/index.js +14 -2
- package/dist/applicationLayer/applicationContract.d.ts +2 -0
- package/dist/applicationLayer/applicationRuntime.d.ts +13 -1
- package/dist/applicationLayer/applicationRuntime.js +39 -3
- package/dist/applicationLayer/index.d.ts +2 -0
- package/dist/applicationLayer/index.js +1 -0
- package/dist/basetool/core/shellRun.js +6 -1
- package/dist/rax_packageManager/raxCli.js +42 -1
- package/dist/runtimeImplementation/praxisRuntimeKernel.d.ts +13 -0
- package/dist/runtimeImplementation/praxisRuntimeKernel.js +550 -15
- package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.d.ts +1 -1
- package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.js +2 -2
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.d.ts +1 -1
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.js +12 -0
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencyTypes.js +2 -0
- package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.d.ts +3 -0
- package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.js +45 -7
- package/dist/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.js +56 -0
- package/dist/runtimeImplementation/runtime.mcpPlane/index.d.ts +225 -0
- package/dist/runtimeImplementation/runtime.mcpPlane/index.js +549 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/baseToolSandboxPlanner.js +0 -2
- package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.d.ts +19 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.js +172 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.d.ts +13 -1
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.js +230 -186
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.d.ts +175 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.js +142 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.d.ts +9 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.js +115 -205
- package/dist/runtimeImplementation/runtimeAgentManifest.js +7 -3
- package/package.json +3 -1
- package/raxode-tui/dist/raxode-cli/backend/agents/codingAgent/agent.js +3 -3
- package/raxode-tui/dist/raxode-cli/backend/application/backendModuleInventory.js +3 -3
- package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.d.ts +1 -0
- package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.js +50 -4
- package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.d.ts +12 -0
- package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.js +58 -0
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.d.ts +1 -0
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.js +3 -1
- package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.d.ts +2 -0
- package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.js +7 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.d.ts +2 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.js +21 -1
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.d.ts +1 -1
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.js +8 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/cli/raxode-cli.js +19 -1
- package/raxode-tui/package.json +2 -1
- package/tsconfig.json +16 -1
|
@@ -42,6 +42,7 @@ import { approvalInterfaceEnvelope, } from "../interfaceAdapter/interfaceEnvelop
|
|
|
42
42
|
import { createInMemorySessionStateEventStore, createSqliteSessionStateEventStore, } from "./runtimeSessionStateEventStore.js";
|
|
43
43
|
import { applyRaxStorageInitPlan, createStoragePlaneRuntime, } from "./runtime.storagePlane/storagePlaneRuntime.js";
|
|
44
44
|
import { prepareSandboxRuntime, } from "./runtime.sandboxPlane/sandboxRuntimeProvider.js";
|
|
45
|
+
import { buildMcpServerProfilesFromManifest, createInMemoryMcpPlusOverlayStore, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, learnedProfileFromProposal, mcp, mcpHarnessModuleFrom, planMcpHarnessExposure, } from "./runtime.mcpPlane/index.js";
|
|
45
46
|
async function inferFilesystemActionForTool(input) {
|
|
46
47
|
if (input.toolId !== "patch.apply")
|
|
47
48
|
return undefined;
|
|
@@ -289,6 +290,316 @@ function defaultMcpServerId(toolId, args) {
|
|
|
289
290
|
return undefined;
|
|
290
291
|
return readString(args.serverId) ?? "local-mcp";
|
|
291
292
|
}
|
|
293
|
+
function withRuntimeHarnessToolLayer(manifest, dynamicTools, reason) {
|
|
294
|
+
const byId = new Map();
|
|
295
|
+
for (const tool of [...manifest.harness.tools, ...dynamicTools]) {
|
|
296
|
+
byId.set(tool.toolId, tool);
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
...manifest,
|
|
300
|
+
harness: {
|
|
301
|
+
...manifest.harness,
|
|
302
|
+
tools: [...byId.values()],
|
|
303
|
+
metadata: {
|
|
304
|
+
...manifest.harness.metadata,
|
|
305
|
+
runtimeMcpDynamicToolCount: dynamicTools.length,
|
|
306
|
+
runtimeMcpDynamicToolRefreshReason: reason,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function withRuntimeMcpModule(manifest, module) {
|
|
312
|
+
if (module === undefined || manifest.harness.modules.mcp !== undefined)
|
|
313
|
+
return manifest;
|
|
314
|
+
const byId = new Map();
|
|
315
|
+
for (const tool of [...manifest.harness.tools, ...mcp.recommendedTools()]) {
|
|
316
|
+
byId.set(tool.toolId, tool);
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
...manifest,
|
|
320
|
+
harness: {
|
|
321
|
+
...manifest.harness,
|
|
322
|
+
modules: {
|
|
323
|
+
...manifest.harness.modules,
|
|
324
|
+
mcp: module,
|
|
325
|
+
},
|
|
326
|
+
tools: [...byId.values()],
|
|
327
|
+
runtimeRequirements: manifest.harness.runtimeRequirements.includes("runtime.mcp")
|
|
328
|
+
? manifest.harness.runtimeRequirements
|
|
329
|
+
: [...manifest.harness.runtimeRequirements, "runtime.mcp"],
|
|
330
|
+
metadata: {
|
|
331
|
+
...manifest.harness.metadata,
|
|
332
|
+
runtimeMcpModuleSource: "runtime.options.mcpModule",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function normalizeMcpNativeToolDeclaration(tool) {
|
|
338
|
+
if (!isRecord(tool))
|
|
339
|
+
return undefined;
|
|
340
|
+
const name = readString(tool.name);
|
|
341
|
+
if (name === undefined)
|
|
342
|
+
return undefined;
|
|
343
|
+
const description = readString(tool.description) ?? readString(tool.title) ?? name;
|
|
344
|
+
const schema = isRecord(tool.inputSchema) ? tool.inputSchema : isRecord(tool.input_schema) ? tool.input_schema : {};
|
|
345
|
+
return { name, description, inputSchema: schema };
|
|
346
|
+
}
|
|
347
|
+
function runtimeMcpPlusProjectId(input) {
|
|
348
|
+
if (typeof input.explicit === "string" && input.explicit.trim().length > 0)
|
|
349
|
+
return input.explicit.trim();
|
|
350
|
+
return `project.${createHash("sha256").update(path.resolve(input.workspaceRoot)).digest("hex").slice(0, 16)}`;
|
|
351
|
+
}
|
|
352
|
+
function mcpPlusOverlayToExposureState(overlay) {
|
|
353
|
+
if (overlay === undefined)
|
|
354
|
+
return {};
|
|
355
|
+
return {
|
|
356
|
+
mode: overlay.mode,
|
|
357
|
+
activeTools: [
|
|
358
|
+
...overlay.activeTools,
|
|
359
|
+
...(overlay.pendingReprofile ? ["mcp_plus.reprofile"] : []),
|
|
360
|
+
],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function readStringList(value) {
|
|
364
|
+
if (!Array.isArray(value))
|
|
365
|
+
return [];
|
|
366
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
367
|
+
}
|
|
368
|
+
function proposalFromArgs(args, fallbackServerId) {
|
|
369
|
+
const rawToolCards = isRecord(args.toolCards) ? args.toolCards : undefined;
|
|
370
|
+
const toolCards = rawToolCards === undefined ? undefined : Object.fromEntries(Object.entries(rawToolCards).flatMap(([toolName, card]) => {
|
|
371
|
+
if (!isRecord(card))
|
|
372
|
+
return [];
|
|
373
|
+
return [[toolName, {
|
|
374
|
+
title: readString(card.title),
|
|
375
|
+
summary: readString(card.summary),
|
|
376
|
+
keywords: readStringList(card.keywords),
|
|
377
|
+
}]];
|
|
378
|
+
}));
|
|
379
|
+
const rawSkillChapters = Array.isArray(args.skillChapters) ? args.skillChapters : [];
|
|
380
|
+
const proposal = {
|
|
381
|
+
serverId: readString(args.serverId) ?? fallbackServerId,
|
|
382
|
+
pinnedTools: readStringList(args.pinnedTools),
|
|
383
|
+
warmTools: readStringList(args.warmTools),
|
|
384
|
+
indexedTools: readStringList(args.indexedTools),
|
|
385
|
+
alwaysIndexTools: readStringList(args.alwaysIndexTools),
|
|
386
|
+
toolCards,
|
|
387
|
+
skillChapters: rawSkillChapters.flatMap((chapter) => {
|
|
388
|
+
if (!isRecord(chapter))
|
|
389
|
+
return [];
|
|
390
|
+
const id = readString(chapter.id);
|
|
391
|
+
const title = readString(chapter.title);
|
|
392
|
+
const summary = readString(chapter.summary);
|
|
393
|
+
return id === undefined || title === undefined || summary === undefined ? [] : [{ id, title, summary }];
|
|
394
|
+
}),
|
|
395
|
+
rationale: readString(args.rationale),
|
|
396
|
+
};
|
|
397
|
+
if (Object.hasOwn(args, "modeHint")) {
|
|
398
|
+
return {
|
|
399
|
+
...proposal,
|
|
400
|
+
modeHint: args.modeHint,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return proposal;
|
|
404
|
+
}
|
|
405
|
+
function mcpPlusServerSpec(manifest, serverId) {
|
|
406
|
+
return mcpHarnessModuleFrom(manifest.harness)?.servers.find((server) => server.serverId === serverId);
|
|
407
|
+
}
|
|
408
|
+
function createRuntimeMcpPlusController(input) {
|
|
409
|
+
let nativeInventory = {};
|
|
410
|
+
async function loadOverlay(serverId, now) {
|
|
411
|
+
return await input.overlayStore.load({ sessionId: input.sessionId, serverId }) ?? {
|
|
412
|
+
serverId,
|
|
413
|
+
sessionId: input.sessionId,
|
|
414
|
+
mode: "expanded",
|
|
415
|
+
activeTools: [],
|
|
416
|
+
counters: { consecutiveIndexedToolCalls: {} },
|
|
417
|
+
updatedAt: now,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
async setNativeInventory(inventory) {
|
|
422
|
+
nativeInventory = inventory;
|
|
423
|
+
},
|
|
424
|
+
async learnedProfilesByServerId(manifest) {
|
|
425
|
+
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
426
|
+
const profiles = {};
|
|
427
|
+
for (const server of module?.servers ?? []) {
|
|
428
|
+
if (server.mode !== "mcp-plus" || server.manifest !== undefined)
|
|
429
|
+
continue;
|
|
430
|
+
profiles[server.serverId] = await input.profileStore.load({ projectId: input.projectId, serverId: server.serverId });
|
|
431
|
+
}
|
|
432
|
+
return profiles;
|
|
433
|
+
},
|
|
434
|
+
async exposureStateByServerId(manifest) {
|
|
435
|
+
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
436
|
+
const states = {};
|
|
437
|
+
for (const server of module?.servers ?? []) {
|
|
438
|
+
if (server.mode !== "mcp-plus")
|
|
439
|
+
continue;
|
|
440
|
+
states[server.serverId] = mcpPlusOverlayToExposureState(await input.overlayStore.load({ sessionId: input.sessionId, serverId: server.serverId }));
|
|
441
|
+
}
|
|
442
|
+
return states;
|
|
443
|
+
},
|
|
444
|
+
async callControlTool(control) {
|
|
445
|
+
if (control.controlName === "mcp_plus.init" || control.controlName === "mcp_plus.reprofile") {
|
|
446
|
+
const proposal = proposalFromArgs(control.args, control.serverId);
|
|
447
|
+
const nativeTools = nativeInventory[proposal.serverId] ?? [];
|
|
448
|
+
const existing = await input.profileStore.load({ projectId: input.projectId, serverId: proposal.serverId });
|
|
449
|
+
const learned = learnedProfileFromProposal({
|
|
450
|
+
proposal,
|
|
451
|
+
nativeTools,
|
|
452
|
+
projectId: input.projectId,
|
|
453
|
+
now: control.now,
|
|
454
|
+
existing,
|
|
455
|
+
});
|
|
456
|
+
if (!learned.ok)
|
|
457
|
+
return { ok: false, error: learned.error };
|
|
458
|
+
await input.profileStore.save({ projectId: input.projectId, serverId: proposal.serverId }, learned.profile);
|
|
459
|
+
const overlay = await loadOverlay(proposal.serverId, control.now);
|
|
460
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId: proposal.serverId }, {
|
|
461
|
+
...overlay,
|
|
462
|
+
mode: "expanded",
|
|
463
|
+
activeTools: [],
|
|
464
|
+
pendingReprofile: false,
|
|
465
|
+
counters: { consecutiveIndexedToolCalls: {} },
|
|
466
|
+
updatedAt: control.now,
|
|
467
|
+
metadata: {
|
|
468
|
+
...(overlay.metadata ?? {}),
|
|
469
|
+
lastProfileControl: control.controlName,
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
ok: true,
|
|
474
|
+
output: {
|
|
475
|
+
accepted: true,
|
|
476
|
+
serverId: proposal.serverId,
|
|
477
|
+
projectId: input.projectId,
|
|
478
|
+
schemaVersion: learned.profile.schemaVersion,
|
|
479
|
+
controlName: control.controlName,
|
|
480
|
+
},
|
|
481
|
+
metadata: { serverId: proposal.serverId, projectId: input.projectId, controlName: control.controlName },
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (control.controlName === "mcp_plus.skill_read") {
|
|
485
|
+
const serverId = readString(control.args.serverId) ?? control.serverId;
|
|
486
|
+
const notes = await input.skillStore.read({ serverId, projectId: input.projectId }, {
|
|
487
|
+
id: readString(control.args.id),
|
|
488
|
+
chapter: readString(control.args.chapter),
|
|
489
|
+
});
|
|
490
|
+
return { ok: true, output: { serverId, projectId: input.projectId, notes } };
|
|
491
|
+
}
|
|
492
|
+
if (control.controlName === "mcp_plus.skill_write" || control.controlName === "mcp_plus.finish") {
|
|
493
|
+
const rawSkill = control.controlName === "mcp_plus.finish" && isRecord(control.args.skill)
|
|
494
|
+
? control.args.skill
|
|
495
|
+
: control.args;
|
|
496
|
+
const serverId = readString(rawSkill.serverId) ?? readString(control.args.serverId) ?? control.serverId;
|
|
497
|
+
const chapter = readString(rawSkill.chapter);
|
|
498
|
+
const title = readString(rawSkill.title);
|
|
499
|
+
const summary = readString(rawSkill.summary);
|
|
500
|
+
if (chapter === undefined || title === undefined || summary === undefined) {
|
|
501
|
+
return {
|
|
502
|
+
ok: true,
|
|
503
|
+
output: {
|
|
504
|
+
accepted: false,
|
|
505
|
+
serverId,
|
|
506
|
+
projectId: input.projectId,
|
|
507
|
+
reason: "No complete skill note was provided.",
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const note = await input.skillStore.write({ serverId, projectId: input.projectId }, {
|
|
512
|
+
chapter,
|
|
513
|
+
title,
|
|
514
|
+
summary,
|
|
515
|
+
whenToUse: readString(rawSkill.whenToUse),
|
|
516
|
+
do: readStringList(rawSkill.do),
|
|
517
|
+
why: readString(rawSkill.why),
|
|
518
|
+
avoid: readStringList(rawSkill.avoid),
|
|
519
|
+
pitfalls: readStringList(rawSkill.pitfalls),
|
|
520
|
+
});
|
|
521
|
+
return { ok: true, output: { accepted: true, serverId, projectId: input.projectId, note } };
|
|
522
|
+
}
|
|
523
|
+
if (control.controlName === "mcp_plus.expand") {
|
|
524
|
+
const serverId = readString(control.args.server) ?? readString(control.args.serverId) ?? control.serverId;
|
|
525
|
+
const request = readString(control.args.request)?.toLowerCase() ?? "";
|
|
526
|
+
const server = mcpPlusServerSpec(control.manifest, serverId);
|
|
527
|
+
const profile = await input.profileStore.load({ projectId: input.projectId, serverId });
|
|
528
|
+
const indexedTools = server?.manifest?.exposure?.indexedTools ?? profile?.exposure.indexedTools ?? [];
|
|
529
|
+
const activatedTools = request.length === 0
|
|
530
|
+
? indexedTools
|
|
531
|
+
: indexedTools.filter((toolName) => toolName.toLowerCase().includes(request));
|
|
532
|
+
const overlay = await loadOverlay(serverId, control.now);
|
|
533
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId }, {
|
|
534
|
+
...overlay,
|
|
535
|
+
mode: "expanded",
|
|
536
|
+
activeTools: [...new Set([...overlay.activeTools, ...activatedTools])],
|
|
537
|
+
updatedAt: control.now,
|
|
538
|
+
});
|
|
539
|
+
return { ok: true, output: { serverId, activatedTools, mode: "expanded" } };
|
|
540
|
+
}
|
|
541
|
+
return { ok: false, error: { code: "MCP_PLUS_CONTROL_UNKNOWN", message: `Unknown MCP+ control tool: ${control.controlName}`, publicSafe: true } };
|
|
542
|
+
},
|
|
543
|
+
async recordToolCall(record) {
|
|
544
|
+
if (!record.ok)
|
|
545
|
+
return false;
|
|
546
|
+
const toolSpec = record.manifest.harness.tools.find((tool) => tool.toolId === record.toolId);
|
|
547
|
+
if (toolSpec?.metadata?.toolProviderKind !== "mcp-static")
|
|
548
|
+
return false;
|
|
549
|
+
const serverId = readString(toolSpec.metadata.serverId);
|
|
550
|
+
const nativeToolName = readString(toolSpec.metadata.nativeToolName);
|
|
551
|
+
if (serverId === undefined || nativeToolName === undefined)
|
|
552
|
+
return false;
|
|
553
|
+
const server = mcpPlusServerSpec(record.manifest, serverId);
|
|
554
|
+
if (server?.mode !== "mcp-plus")
|
|
555
|
+
return false;
|
|
556
|
+
const profile = server.manifest === undefined
|
|
557
|
+
? await input.profileStore.load({ projectId: input.projectId, serverId })
|
|
558
|
+
: undefined;
|
|
559
|
+
const indexedTools = new Set(server.manifest?.exposure?.indexedTools ?? profile?.exposure.indexedTools ?? []);
|
|
560
|
+
const overlay = await loadOverlay(serverId, record.now);
|
|
561
|
+
const consecutiveIndexedToolCalls = { ...overlay.counters.consecutiveIndexedToolCalls };
|
|
562
|
+
let pendingReprofile = overlay.pendingReprofile;
|
|
563
|
+
if (indexedTools.has(nativeToolName)) {
|
|
564
|
+
consecutiveIndexedToolCalls[nativeToolName] = (consecutiveIndexedToolCalls[nativeToolName] ?? 0) + 1;
|
|
565
|
+
if (consecutiveIndexedToolCalls[nativeToolName] >= input.reprofileConsecutiveIndexedCalls) {
|
|
566
|
+
pendingReprofile = true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
for (const toolName of Object.keys(consecutiveIndexedToolCalls)) {
|
|
571
|
+
consecutiveIndexedToolCalls[toolName] = 0;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId }, {
|
|
575
|
+
...overlay,
|
|
576
|
+
mode: "expanded",
|
|
577
|
+
activeTools: [...new Set([...overlay.activeTools, nativeToolName])],
|
|
578
|
+
pendingReprofile,
|
|
579
|
+
counters: { consecutiveIndexedToolCalls },
|
|
580
|
+
updatedAt: record.now,
|
|
581
|
+
});
|
|
582
|
+
return true;
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function discoverRuntimeMcpDynamicTools(manifest, executor, mcpPlusRuntime) {
|
|
587
|
+
const profiles = buildMcpServerProfilesFromManifest(manifest);
|
|
588
|
+
if (profiles.length === 0 || executor.mcp?.listTools === undefined)
|
|
589
|
+
return [];
|
|
590
|
+
const inventory = {};
|
|
591
|
+
for (const profile of profiles) {
|
|
592
|
+
const listed = await executor.mcp.listTools({ serverId: profile.serverId });
|
|
593
|
+
if (listed?.ok !== true)
|
|
594
|
+
continue;
|
|
595
|
+
const rawTools = Array.isArray(listed.output?.tools) ? listed.output.tools : [];
|
|
596
|
+
inventory[profile.serverId] = rawTools
|
|
597
|
+
.map(normalizeMcpNativeToolDeclaration)
|
|
598
|
+
.filter((tool) => tool !== undefined);
|
|
599
|
+
}
|
|
600
|
+
await mcpPlusRuntime?.setNativeInventory(inventory);
|
|
601
|
+
return planMcpHarnessExposure(manifest, inventory, await mcpPlusRuntime?.exposureStateByServerId(manifest) ?? {}, await mcpPlusRuntime?.learnedProfilesByServerId(manifest) ?? {}).servers.flatMap((server) => [...server.dynamicToolSpecs]);
|
|
602
|
+
}
|
|
292
603
|
function providerToolMappings(manifest) {
|
|
293
604
|
return createProviderToolMappings(manifest.harness.tools);
|
|
294
605
|
}
|
|
@@ -523,30 +834,54 @@ function shellCommandPathScanSource(command) {
|
|
|
523
834
|
function isShellCommandAllowedSystemPath(token) {
|
|
524
835
|
return token === "/dev/null";
|
|
525
836
|
}
|
|
837
|
+
function escapeRegExp(value) {
|
|
838
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
839
|
+
}
|
|
526
840
|
function shellCommandAbsolutePathTokens(command) {
|
|
527
841
|
const matches = shellCommandPathScanSource(command).matchAll(/(^|[\s"'`=({[,;|&<>])\/(?!\/)[^\s"'`$;&|<>()[\]{}]*/gu);
|
|
528
842
|
return [...matches]
|
|
529
843
|
.map((match) => stripShellPathToken((match[0] ?? "").slice(match[1]?.length ?? 0)))
|
|
530
844
|
.filter((token) => token.length > 1 && !token.includes("\0") && !isShellCommandAllowedSystemPath(token));
|
|
531
845
|
}
|
|
846
|
+
function shellCommandPathAccess(command, token) {
|
|
847
|
+
const source = shellCommandPathScanSource(command);
|
|
848
|
+
const escaped = escapeRegExp(token);
|
|
849
|
+
const quotedToken = `["']?${escaped}["']?`;
|
|
850
|
+
if (new RegExp(`(?:^|[\\s;|&])(?:\\d?>|\\d?>>|>|>>|<>)\\s*${quotedToken}(?:\\s|$|[;&|])`, "u").test(source)) {
|
|
851
|
+
return "write";
|
|
852
|
+
}
|
|
853
|
+
if (new RegExp(`\\btee\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
|
|
854
|
+
return "write";
|
|
855
|
+
if (new RegExp(`\\bsed\\b[^\\n;&|]*\\s-i\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
|
|
856
|
+
return "write";
|
|
857
|
+
if (new RegExp(`\\b(?:touch|mkdir|rm|rmdir|chmod|chown)\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
|
|
858
|
+
return "write";
|
|
859
|
+
if (new RegExp(`Path\\(\\s*["']${escaped}["']\\s*\\)[\\s\\S]*\\b(?:write_text|append_text)\\s*\\(`, "u").test(source)) {
|
|
860
|
+
return "write";
|
|
861
|
+
}
|
|
862
|
+
return "read";
|
|
863
|
+
}
|
|
532
864
|
function shellCommandOutsideAllowedRootsMetadata(input) {
|
|
533
865
|
const output = [];
|
|
534
866
|
for (const token of shellCommandAbsolutePathTokens(input.command)) {
|
|
535
867
|
const normalizedPath = path.resolve(token);
|
|
536
868
|
if (isInsideAllowedRoots(normalizedPath, input.allowedRoots))
|
|
537
869
|
continue;
|
|
538
|
-
output.push(
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
870
|
+
output.push({
|
|
871
|
+
...workspacePathMetadata({
|
|
872
|
+
ok: false,
|
|
873
|
+
reason: "OUTSIDE_ALLOWED_ROOTS",
|
|
874
|
+
message: "shell command references an absolute path outside runtime allowed roots",
|
|
875
|
+
requestedPath: token,
|
|
876
|
+
normalizedPath,
|
|
877
|
+
workspaceRoot: input.workspaceRoot,
|
|
878
|
+
allowedRoots: input.allowedRoots,
|
|
879
|
+
pathWasMapped: false,
|
|
880
|
+
mappingSource: "absolute",
|
|
881
|
+
suggestedCwd: input.workspaceRoot,
|
|
882
|
+
}, "path"),
|
|
883
|
+
workspacePathAccess: shellCommandPathAccess(input.command, token),
|
|
884
|
+
});
|
|
550
885
|
}
|
|
551
886
|
return output;
|
|
552
887
|
}
|
|
@@ -557,7 +892,6 @@ function normalizeWorkspacePathContract(input) {
|
|
|
557
892
|
const allowedRoots = normalizeAllowedRoots({ workspaceRoot, allowedRoots: input.allowedRoots });
|
|
558
893
|
const nextArgs = { ...input.args };
|
|
559
894
|
const normalizations = [];
|
|
560
|
-
const pathAccess = input.toolId === "patch.apply" ? "write" : "read";
|
|
561
895
|
const allowOutsideAllowedRoots = input.profile !== undefined;
|
|
562
896
|
if (input.toolId === "file.read" || input.toolId === "skill.load" || input.toolId === "media.viewImage") {
|
|
563
897
|
for (const field of ["path", "filePath", "imagePath"]) {
|
|
@@ -596,12 +930,15 @@ function normalizeWorkspacePathContract(input) {
|
|
|
596
930
|
}
|
|
597
931
|
}
|
|
598
932
|
const workspaceOutsideAllowedRoots = normalizations.some((item) => item.workspaceOutsideAllowedRoots === true || item.reason === "OUTSIDE_ALLOWED_ROOTS");
|
|
933
|
+
const workspacePathAccess = input.toolId === "patch.apply" || normalizations.some((item) => item.workspacePathAccess === "write")
|
|
934
|
+
? "write"
|
|
935
|
+
: "read";
|
|
599
936
|
return {
|
|
600
937
|
ok: true,
|
|
601
938
|
args: withWorkspaceNormalizationMetadata(nextArgs, normalizations, workspaceOutsideAllowedRoots
|
|
602
939
|
? {
|
|
603
940
|
workspaceOutsideAllowedRoots: true,
|
|
604
|
-
workspacePathAccess
|
|
941
|
+
workspacePathAccess,
|
|
605
942
|
}
|
|
606
943
|
: {}),
|
|
607
944
|
metadata: normalizations[0] === undefined
|
|
@@ -2132,6 +2469,7 @@ async function prepareKernelSandbox(input) {
|
|
|
2132
2469
|
const sandbox = await prepareSandboxRuntime(input.manifest.sandbox, {
|
|
2133
2470
|
cwd: input.options.sandbox?.cwd ?? input.storageRuntime.layout.workspace.root,
|
|
2134
2471
|
runSmoke: input.options.sandbox?.runSmoke ?? false,
|
|
2472
|
+
providerReady: input.options.sandbox?.provider !== undefined,
|
|
2135
2473
|
});
|
|
2136
2474
|
input.events.push(...sandbox.events);
|
|
2137
2475
|
await input.store.appendEvent(event(input.sessionId, "event:sandbox.prepared", "runtime.sandboxPlane.prepared", input.now(), { sandbox }));
|
|
@@ -2456,6 +2794,166 @@ function compactRequestMaterials(input) {
|
|
|
2456
2794
|
return [...originalMaterials, ...governedSessionSummaryMaterial, ...governedProjectContext];
|
|
2457
2795
|
}
|
|
2458
2796
|
async function executeBaseToolDecision(input) {
|
|
2797
|
+
const dynamicToolSpec = input.manifest.harness.tools.find((tool) => tool.toolId === input.toolId);
|
|
2798
|
+
const dynamicRuntimeToolId = typeof dynamicToolSpec?.metadata?.runtimeToolId === "string"
|
|
2799
|
+
? dynamicToolSpec.metadata.runtimeToolId
|
|
2800
|
+
: undefined;
|
|
2801
|
+
const dynamicRuntimeArguments = isRecord(dynamicToolSpec?.metadata?.runtimeArguments)
|
|
2802
|
+
? dynamicToolSpec.metadata.runtimeArguments
|
|
2803
|
+
: undefined;
|
|
2804
|
+
if (dynamicToolSpec?.metadata?.toolProviderKind === "mcp-plus-control") {
|
|
2805
|
+
const serverId = readString(dynamicToolSpec.metadata.serverId);
|
|
2806
|
+
const controlName = readString(dynamicToolSpec.metadata.controlName);
|
|
2807
|
+
if (serverId === undefined || controlName === undefined || input.mcpPlusRuntime === undefined) {
|
|
2808
|
+
const record = {
|
|
2809
|
+
callId: input.toolCallId,
|
|
2810
|
+
toolId: input.toolId,
|
|
2811
|
+
arguments: input.args,
|
|
2812
|
+
ok: false,
|
|
2813
|
+
error: {
|
|
2814
|
+
code: "MCP_PLUS_CONTROL_UNAVAILABLE",
|
|
2815
|
+
message: "MCP+ control tool is not available in this runtime.",
|
|
2816
|
+
publicSafe: true,
|
|
2817
|
+
},
|
|
2818
|
+
};
|
|
2819
|
+
const observation = createObservationMaterial({
|
|
2820
|
+
observationId: `${input.sessionId}:observation:${input.toolCallId}`,
|
|
2821
|
+
source: "baseTool",
|
|
2822
|
+
status: "failed",
|
|
2823
|
+
title: `MCP+ control ${controlName ?? input.toolId}`,
|
|
2824
|
+
summary: "MCP+ control tool is not available.",
|
|
2825
|
+
refs: [input.toolCallId, input.toolId],
|
|
2826
|
+
payload: record.error,
|
|
2827
|
+
metadata: metadataRecord({ toolCallId: input.toolCallId, toolId: input.toolId }),
|
|
2828
|
+
});
|
|
2829
|
+
return {
|
|
2830
|
+
record,
|
|
2831
|
+
observation,
|
|
2832
|
+
events: ["runtime.mcpPlus.control.unavailable"],
|
|
2833
|
+
governance: {
|
|
2834
|
+
kind: "runtime.execEngine.baseTool.governanceDecision",
|
|
2835
|
+
toolId: input.toolId,
|
|
2836
|
+
status: "deny",
|
|
2837
|
+
risk: "safe",
|
|
2838
|
+
policyProfile: input.manifest.toolPolicy.profile,
|
|
2839
|
+
policyMatrixId: input.manifest.toolPolicy.matrixId,
|
|
2840
|
+
approvalRequired: false,
|
|
2841
|
+
approvalReason: "MCP+ control tool is not available.",
|
|
2842
|
+
sandbox: {
|
|
2843
|
+
sandboxId: input.manifest.sandbox.sandboxId,
|
|
2844
|
+
profile: input.manifest.sandbox.profile,
|
|
2845
|
+
providerFamily: input.manifest.sandbox.providerFamily,
|
|
2846
|
+
isolationLevel: input.manifest.sandbox.isolationLevel,
|
|
2847
|
+
filesystem: input.manifest.sandbox.filesystem,
|
|
2848
|
+
network: input.manifest.sandbox.network,
|
|
2849
|
+
shell: input.manifest.sandbox.shell,
|
|
2850
|
+
hostObserved: input.manifest.sandbox.profile === "host-observed",
|
|
2851
|
+
dependencyRefs: input.manifest.sandbox.dependencyRefs ?? [],
|
|
2852
|
+
},
|
|
2853
|
+
resourceLimits: input.manifest.sandbox.resourceLimits,
|
|
2854
|
+
publicSafe: true,
|
|
2855
|
+
events: ["runtime.mcpPlus.control.unavailable"],
|
|
2856
|
+
metadata: { toolCallId: input.toolCallId },
|
|
2857
|
+
},
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
const controlResult = await input.mcpPlusRuntime.callControlTool({
|
|
2861
|
+
manifest: input.manifest,
|
|
2862
|
+
serverId,
|
|
2863
|
+
controlName,
|
|
2864
|
+
args: input.args,
|
|
2865
|
+
now: input.now(),
|
|
2866
|
+
});
|
|
2867
|
+
const record = {
|
|
2868
|
+
callId: input.toolCallId,
|
|
2869
|
+
toolId: input.toolId,
|
|
2870
|
+
arguments: input.args,
|
|
2871
|
+
ok: controlResult.ok,
|
|
2872
|
+
...(controlResult.ok ? { output: controlResult.output } : { error: controlResult.error }),
|
|
2873
|
+
};
|
|
2874
|
+
const controlFailureMessage = controlResult.ok ? undefined : controlResult.error?.message ?? "MCP+ control tool failed.";
|
|
2875
|
+
const observation = createObservationMaterial({
|
|
2876
|
+
observationId: `${input.sessionId}:observation:${input.toolCallId}`,
|
|
2877
|
+
source: "baseTool",
|
|
2878
|
+
status: controlResult.ok ? "completed" : "failed",
|
|
2879
|
+
title: `MCP+ control ${controlName}`,
|
|
2880
|
+
summary: controlResult.ok ? "MCP+ control tool completed." : controlFailureMessage ?? "MCP+ control tool failed.",
|
|
2881
|
+
refs: [input.toolCallId, input.toolId],
|
|
2882
|
+
payload: controlResult.ok ? controlResult.output : controlResult.error ?? { code: "MCP_PLUS_CONTROL_FAILED", message: controlFailureMessage, publicSafe: true },
|
|
2883
|
+
metadata: metadataRecord({
|
|
2884
|
+
toolCallId: input.toolCallId,
|
|
2885
|
+
toolId: input.toolId,
|
|
2886
|
+
serverId,
|
|
2887
|
+
controlName,
|
|
2888
|
+
observationStatus: controlResult.ok ? "completed" : "failed",
|
|
2889
|
+
}),
|
|
2890
|
+
});
|
|
2891
|
+
return {
|
|
2892
|
+
record,
|
|
2893
|
+
observation,
|
|
2894
|
+
events: [controlResult.ok ? "runtime.mcpPlus.control.completed" : "runtime.mcpPlus.control.failed"],
|
|
2895
|
+
governance: {
|
|
2896
|
+
kind: "runtime.execEngine.baseTool.governanceDecision",
|
|
2897
|
+
toolId: input.toolId,
|
|
2898
|
+
status: controlResult.ok ? "allow" : "deny",
|
|
2899
|
+
risk: "safe",
|
|
2900
|
+
policyProfile: input.manifest.toolPolicy.profile,
|
|
2901
|
+
policyMatrixId: input.manifest.toolPolicy.matrixId,
|
|
2902
|
+
approvalRequired: false,
|
|
2903
|
+
sandbox: {
|
|
2904
|
+
sandboxId: input.manifest.sandbox.sandboxId,
|
|
2905
|
+
profile: input.manifest.sandbox.profile,
|
|
2906
|
+
providerFamily: input.manifest.sandbox.providerFamily,
|
|
2907
|
+
isolationLevel: input.manifest.sandbox.isolationLevel,
|
|
2908
|
+
filesystem: input.manifest.sandbox.filesystem,
|
|
2909
|
+
network: input.manifest.sandbox.network,
|
|
2910
|
+
shell: input.manifest.sandbox.shell,
|
|
2911
|
+
hostObserved: input.manifest.sandbox.profile === "host-observed",
|
|
2912
|
+
dependencyRefs: input.manifest.sandbox.dependencyRefs ?? [],
|
|
2913
|
+
},
|
|
2914
|
+
resourceLimits: input.manifest.sandbox.resourceLimits,
|
|
2915
|
+
publicSafe: true,
|
|
2916
|
+
events: [controlResult.ok ? "runtime.mcpPlus.control.completed" : "runtime.mcpPlus.control.failed"],
|
|
2917
|
+
metadata: { serverId, controlName },
|
|
2918
|
+
},
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
if (dynamicRuntimeToolId !== undefined && dynamicRuntimeToolId !== input.toolId) {
|
|
2922
|
+
const delegatedArgs = dynamicRuntimeToolId === "mcp.use"
|
|
2923
|
+
? {
|
|
2924
|
+
...(dynamicRuntimeArguments ?? {}),
|
|
2925
|
+
arguments: input.args,
|
|
2926
|
+
}
|
|
2927
|
+
: {
|
|
2928
|
+
...(dynamicRuntimeArguments ?? {}),
|
|
2929
|
+
...input.args,
|
|
2930
|
+
};
|
|
2931
|
+
const delegated = await executeBaseToolDecision({
|
|
2932
|
+
...input,
|
|
2933
|
+
toolId: dynamicRuntimeToolId,
|
|
2934
|
+
args: delegatedArgs,
|
|
2935
|
+
mcpPlusRuntime: input.mcpPlusRuntime,
|
|
2936
|
+
});
|
|
2937
|
+
return {
|
|
2938
|
+
...delegated,
|
|
2939
|
+
record: {
|
|
2940
|
+
...delegated.record,
|
|
2941
|
+
toolId: input.toolId,
|
|
2942
|
+
arguments: input.args,
|
|
2943
|
+
},
|
|
2944
|
+
observation: {
|
|
2945
|
+
...delegated.observation,
|
|
2946
|
+
material: {
|
|
2947
|
+
...delegated.observation.material,
|
|
2948
|
+
metadata: {
|
|
2949
|
+
...(delegated.observation.material.metadata ?? {}),
|
|
2950
|
+
dynamicRuntimeToolId,
|
|
2951
|
+
dynamicMcpToolId: input.toolId,
|
|
2952
|
+
},
|
|
2953
|
+
},
|
|
2954
|
+
},
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2459
2957
|
let toolArguments = enrichToolArguments(input.manifest, input.toolId, input.args, {
|
|
2460
2958
|
runtimeId: input.runtimeId,
|
|
2461
2959
|
sessionId: input.sessionId,
|
|
@@ -3383,6 +3881,8 @@ export class PraxisRuntimeKernel {
|
|
|
3383
3881
|
}),
|
|
3384
3882
|
});
|
|
3385
3883
|
const dryRun = options.dryRun !== false;
|
|
3884
|
+
manifest = withRuntimeMcpModule(manifest, options.mcpModule);
|
|
3885
|
+
const runtimeMcpBaseManifest = manifest;
|
|
3386
3886
|
const defaultBaseToolPolicy = {
|
|
3387
3887
|
workspaceRoot: toolWorkspaceRoot,
|
|
3388
3888
|
allowedRoots: toolAllowedRoots,
|
|
@@ -3413,7 +3913,15 @@ export class PraxisRuntimeKernel {
|
|
|
3413
3913
|
sandboxSpec: manifest.sandbox,
|
|
3414
3914
|
preparedSandbox: sandboxPrepared.sandbox,
|
|
3415
3915
|
policyProfile: manifest.toolPolicy.profile,
|
|
3916
|
+
sandboxProvider: options.sandbox?.provider,
|
|
3917
|
+
sandboxAudit: async (sandboxEvent) => {
|
|
3918
|
+
await store.appendEvent(event(sessionId, `event:sandbox:${sandboxEvent.actionId}:${sandboxEvent.type}`, sandboxEvent.type, now(), { sandbox: sandboxEvent }));
|
|
3919
|
+
},
|
|
3416
3920
|
remoteSandboxWorker: options.sandbox?.remoteWorker,
|
|
3921
|
+
mcpServers: [
|
|
3922
|
+
...buildMcpServerProfilesFromManifest(manifest),
|
|
3923
|
+
...(options.mcpServers ?? []),
|
|
3924
|
+
],
|
|
3417
3925
|
emitEvent: (runtimeEvent) => {
|
|
3418
3926
|
events.push(runtimeEvent.type);
|
|
3419
3927
|
},
|
|
@@ -3423,9 +3931,25 @@ export class PraxisRuntimeKernel {
|
|
|
3423
3931
|
id: "praxis-runtime-kernel",
|
|
3424
3932
|
sessionId,
|
|
3425
3933
|
};
|
|
3934
|
+
const mcpPlusRuntime = createRuntimeMcpPlusController({
|
|
3935
|
+
sessionId,
|
|
3936
|
+
projectId: runtimeMcpPlusProjectId({
|
|
3937
|
+
explicit: options.mcpPlus?.projectId,
|
|
3938
|
+
workspaceRoot: toolWorkspaceRoot,
|
|
3939
|
+
}),
|
|
3940
|
+
profileStore: options.mcpPlus?.profileStore ?? createFileMcpPlusProfileStore(path.join(toolWorkspaceRoot, ".rax_workspace", "mcp-plus")),
|
|
3941
|
+
overlayStore: options.mcpPlus?.overlayStore ?? createInMemoryMcpPlusOverlayStore(),
|
|
3942
|
+
skillStore: options.mcpPlus?.skillStore ?? createFileMcpPlusSkillStore(path.join(toolWorkspaceRoot, ".rax_workspace", "mcp-plus")),
|
|
3943
|
+
reprofileConsecutiveIndexedCalls: options.mcpPlus?.reprofileConsecutiveIndexedCalls ?? 6,
|
|
3944
|
+
});
|
|
3426
3945
|
const maxModelTurns = manifest.harness.loop.maxModelTurns ?? 2;
|
|
3427
3946
|
const maxToolCalls = manifest.harness.loop.maxToolCalls ?? 4;
|
|
3428
|
-
|
|
3947
|
+
let toolMappings = [];
|
|
3948
|
+
async function refreshRuntimeMcpTools(reason) {
|
|
3949
|
+
manifest = withRuntimeHarnessToolLayer(runtimeMcpBaseManifest, await discoverRuntimeMcpDynamicTools(runtimeMcpBaseManifest, executor, mcpPlusRuntime), reason);
|
|
3950
|
+
toolMappings = providerToolMappings(manifest);
|
|
3951
|
+
}
|
|
3952
|
+
await refreshRuntimeMcpTools("session.checkpoint.start");
|
|
3429
3953
|
const providerFamily = providerToolSchemaFamilyForModel(manifest.model);
|
|
3430
3954
|
let toolContextSelection = {
|
|
3431
3955
|
families: normalizedSelection(options.toolContextSelection?.families),
|
|
@@ -4607,6 +5131,7 @@ export class PraxisRuntimeKernel {
|
|
|
4607
5131
|
store,
|
|
4608
5132
|
approvalResolver: options.approvalResolver,
|
|
4609
5133
|
agentReviewResolver: options.agentReviewResolver,
|
|
5134
|
+
mcpPlusRuntime,
|
|
4610
5135
|
preparedSandbox: sandboxPrepared.sandbox,
|
|
4611
5136
|
dependencyRuntime: options.baseToolDependencyRuntime,
|
|
4612
5137
|
now,
|
|
@@ -4619,6 +5144,16 @@ export class PraxisRuntimeKernel {
|
|
|
4619
5144
|
});
|
|
4620
5145
|
toolCalls.push(executed.record);
|
|
4621
5146
|
observations.push(executed.observation);
|
|
5147
|
+
const mcpPlusToolCallUpdated = await mcpPlusRuntime.recordToolCall({
|
|
5148
|
+
manifest,
|
|
5149
|
+
toolId: executed.record.toolId,
|
|
5150
|
+
ok: executed.record.ok,
|
|
5151
|
+
now: now(),
|
|
5152
|
+
});
|
|
5153
|
+
if (executed.record.ok && (mcpPlusToolCallUpdated ||
|
|
5154
|
+
manifest.harness.tools.find((tool) => tool.toolId === executed.record.toolId)?.metadata?.toolProviderKind === "mcp-plus-control")) {
|
|
5155
|
+
await refreshRuntimeMcpTools("session.checkpoint.after_mcp_tool");
|
|
5156
|
+
}
|
|
4622
5157
|
if (invalidatesFileReadCache(executed.record.toolId)) {
|
|
4623
5158
|
sameTurnFileReadCache.clear();
|
|
4624
5159
|
}
|
|
@@ -3,7 +3,7 @@ import type { RuntimeComponentSpec } from "./componentTypes.js";
|
|
|
3
3
|
export declare const officialRuntimeComponents: readonly [{
|
|
4
4
|
readonly componentId: "component.sandbox.bubblewrap";
|
|
5
5
|
readonly kind: "sandbox";
|
|
6
|
-
readonly title: "Linux
|
|
6
|
+
readonly title: "Linux Raxcell sandbox provider";
|
|
7
7
|
readonly dependencies: readonly [DependencyDeclaration];
|
|
8
8
|
readonly fallbackComponentIds: readonly ["component.sandbox.workspaceRollback"];
|
|
9
9
|
readonly supportedPlatforms: readonly ["linux"];
|