@praxis-ai/praxis 0.1.1 → 0.1.2

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.
Files changed (48) hide show
  1. package/dist/agentCore/index.d.ts +45 -6
  2. package/dist/agentCore/index.js +14 -2
  3. package/dist/applicationLayer/applicationContract.d.ts +2 -0
  4. package/dist/applicationLayer/applicationRuntime.d.ts +6 -1
  5. package/dist/applicationLayer/applicationRuntime.js +37 -3
  6. package/dist/applicationLayer/index.d.ts +1 -0
  7. package/dist/basetool/core/shellRun.js +6 -1
  8. package/dist/rax_packageManager/raxCli.js +42 -1
  9. package/dist/runtimeImplementation/praxisRuntimeKernel.d.ts +6 -0
  10. package/dist/runtimeImplementation/praxisRuntimeKernel.js +165 -14
  11. package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.d.ts +1 -1
  12. package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.js +2 -2
  13. package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.d.ts +1 -1
  14. package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.js +12 -0
  15. package/dist/runtimeImplementation/runtime.dependencyPlane/dependencyTypes.js +2 -0
  16. package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.d.ts +3 -0
  17. package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.js +45 -7
  18. package/dist/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.js +56 -0
  19. package/dist/runtimeImplementation/runtime.mcpPlane/index.d.ts +114 -0
  20. package/dist/runtimeImplementation/runtime.mcpPlane/index.js +167 -0
  21. package/dist/runtimeImplementation/runtime.sandboxPlane/baseToolSandboxPlanner.js +0 -2
  22. package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.d.ts +19 -0
  23. package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.js +172 -0
  24. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.d.ts +13 -1
  25. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.js +230 -186
  26. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.d.ts +175 -0
  27. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.js +142 -0
  28. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.d.ts +9 -0
  29. package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.js +115 -205
  30. package/dist/runtimeImplementation/runtimeAgentManifest.js +7 -3
  31. package/package.json +3 -1
  32. package/raxode-tui/dist/raxode-cli/backend/agents/codingAgent/agent.js +3 -3
  33. package/raxode-tui/dist/raxode-cli/backend/application/backendModuleInventory.js +3 -3
  34. package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.d.ts +1 -0
  35. package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.js +50 -4
  36. package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.d.ts +12 -0
  37. package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.js +58 -0
  38. package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.d.ts +1 -0
  39. package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.js +3 -1
  40. package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.d.ts +2 -0
  41. package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.js +7 -0
  42. package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.d.ts +2 -0
  43. package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.js +21 -1
  44. package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.d.ts +1 -1
  45. package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.js +8 -0
  46. package/raxode-tui/dist/raxode-cli/frontend/tui/cli/raxode-cli.js +19 -1
  47. package/raxode-tui/package.json +2 -1
  48. 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, mcp, 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,77 @@ function defaultMcpServerId(toolId, args) {
289
290
  return undefined;
290
291
  return readString(args.serverId) ?? "local-mcp";
291
292
  }
293
+ function withRuntimeHarnessTools(manifest, dynamicTools) {
294
+ if (dynamicTools.length === 0)
295
+ return manifest;
296
+ const byId = new Map();
297
+ for (const tool of [...manifest.harness.tools, ...dynamicTools]) {
298
+ byId.set(tool.toolId, tool);
299
+ }
300
+ return {
301
+ ...manifest,
302
+ harness: {
303
+ ...manifest.harness,
304
+ tools: [...byId.values()],
305
+ metadata: {
306
+ ...manifest.harness.metadata,
307
+ runtimeMcpDynamicToolCount: dynamicTools.length,
308
+ },
309
+ },
310
+ };
311
+ }
312
+ function withRuntimeMcpModule(manifest, module) {
313
+ if (module === undefined || manifest.harness.modules.mcp !== undefined)
314
+ return manifest;
315
+ const byId = new Map();
316
+ for (const tool of [...manifest.harness.tools, ...mcp.recommendedTools()]) {
317
+ byId.set(tool.toolId, tool);
318
+ }
319
+ return {
320
+ ...manifest,
321
+ harness: {
322
+ ...manifest.harness,
323
+ modules: {
324
+ ...manifest.harness.modules,
325
+ mcp: module,
326
+ },
327
+ tools: [...byId.values()],
328
+ runtimeRequirements: manifest.harness.runtimeRequirements.includes("runtime.mcp")
329
+ ? manifest.harness.runtimeRequirements
330
+ : [...manifest.harness.runtimeRequirements, "runtime.mcp"],
331
+ metadata: {
332
+ ...manifest.harness.metadata,
333
+ runtimeMcpModuleSource: "runtime.options.mcpModule",
334
+ },
335
+ },
336
+ };
337
+ }
338
+ function normalizeMcpNativeToolDeclaration(tool) {
339
+ if (!isRecord(tool))
340
+ return undefined;
341
+ const name = readString(tool.name);
342
+ if (name === undefined)
343
+ return undefined;
344
+ const description = readString(tool.description) ?? readString(tool.title) ?? name;
345
+ const schema = isRecord(tool.inputSchema) ? tool.inputSchema : isRecord(tool.input_schema) ? tool.input_schema : {};
346
+ return { name, description, inputSchema: schema };
347
+ }
348
+ async function discoverRuntimeMcpDynamicTools(manifest, executor) {
349
+ const profiles = buildMcpServerProfilesFromManifest(manifest);
350
+ if (profiles.length === 0 || executor.mcp?.listTools === undefined)
351
+ return [];
352
+ const inventory = {};
353
+ for (const profile of profiles) {
354
+ const listed = await executor.mcp.listTools({ serverId: profile.serverId });
355
+ if (listed?.ok !== true)
356
+ continue;
357
+ const rawTools = Array.isArray(listed.output?.tools) ? listed.output.tools : [];
358
+ inventory[profile.serverId] = rawTools
359
+ .map(normalizeMcpNativeToolDeclaration)
360
+ .filter((tool) => tool !== undefined);
361
+ }
362
+ return planMcpHarnessExposure(manifest, inventory).servers.flatMap((server) => [...server.dynamicToolSpecs]);
363
+ }
292
364
  function providerToolMappings(manifest) {
293
365
  return createProviderToolMappings(manifest.harness.tools);
294
366
  }
@@ -523,30 +595,54 @@ function shellCommandPathScanSource(command) {
523
595
  function isShellCommandAllowedSystemPath(token) {
524
596
  return token === "/dev/null";
525
597
  }
598
+ function escapeRegExp(value) {
599
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
600
+ }
526
601
  function shellCommandAbsolutePathTokens(command) {
527
602
  const matches = shellCommandPathScanSource(command).matchAll(/(^|[\s"'`=({[,;|&<>])\/(?!\/)[^\s"'`$;&|<>()[\]{}]*/gu);
528
603
  return [...matches]
529
604
  .map((match) => stripShellPathToken((match[0] ?? "").slice(match[1]?.length ?? 0)))
530
605
  .filter((token) => token.length > 1 && !token.includes("\0") && !isShellCommandAllowedSystemPath(token));
531
606
  }
607
+ function shellCommandPathAccess(command, token) {
608
+ const source = shellCommandPathScanSource(command);
609
+ const escaped = escapeRegExp(token);
610
+ const quotedToken = `["']?${escaped}["']?`;
611
+ if (new RegExp(`(?:^|[\\s;|&])(?:\\d?>|\\d?>>|>|>>|<>)\\s*${quotedToken}(?:\\s|$|[;&|])`, "u").test(source)) {
612
+ return "write";
613
+ }
614
+ if (new RegExp(`\\btee\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
615
+ return "write";
616
+ if (new RegExp(`\\bsed\\b[^\\n;&|]*\\s-i\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
617
+ return "write";
618
+ if (new RegExp(`\\b(?:touch|mkdir|rm|rmdir|chmod|chown)\\b[^\\n;&|]*${quotedToken}`, "u").test(source))
619
+ return "write";
620
+ if (new RegExp(`Path\\(\\s*["']${escaped}["']\\s*\\)[\\s\\S]*\\b(?:write_text|append_text)\\s*\\(`, "u").test(source)) {
621
+ return "write";
622
+ }
623
+ return "read";
624
+ }
532
625
  function shellCommandOutsideAllowedRootsMetadata(input) {
533
626
  const output = [];
534
627
  for (const token of shellCommandAbsolutePathTokens(input.command)) {
535
628
  const normalizedPath = path.resolve(token);
536
629
  if (isInsideAllowedRoots(normalizedPath, input.allowedRoots))
537
630
  continue;
538
- output.push(workspacePathMetadata({
539
- ok: false,
540
- reason: "OUTSIDE_ALLOWED_ROOTS",
541
- message: "shell command references an absolute path outside runtime allowed roots",
542
- requestedPath: token,
543
- normalizedPath,
544
- workspaceRoot: input.workspaceRoot,
545
- allowedRoots: input.allowedRoots,
546
- pathWasMapped: false,
547
- mappingSource: "absolute",
548
- suggestedCwd: input.workspaceRoot,
549
- }, "path"));
631
+ output.push({
632
+ ...workspacePathMetadata({
633
+ ok: false,
634
+ reason: "OUTSIDE_ALLOWED_ROOTS",
635
+ message: "shell command references an absolute path outside runtime allowed roots",
636
+ requestedPath: token,
637
+ normalizedPath,
638
+ workspaceRoot: input.workspaceRoot,
639
+ allowedRoots: input.allowedRoots,
640
+ pathWasMapped: false,
641
+ mappingSource: "absolute",
642
+ suggestedCwd: input.workspaceRoot,
643
+ }, "path"),
644
+ workspacePathAccess: shellCommandPathAccess(input.command, token),
645
+ });
550
646
  }
551
647
  return output;
552
648
  }
@@ -557,7 +653,6 @@ function normalizeWorkspacePathContract(input) {
557
653
  const allowedRoots = normalizeAllowedRoots({ workspaceRoot, allowedRoots: input.allowedRoots });
558
654
  const nextArgs = { ...input.args };
559
655
  const normalizations = [];
560
- const pathAccess = input.toolId === "patch.apply" ? "write" : "read";
561
656
  const allowOutsideAllowedRoots = input.profile !== undefined;
562
657
  if (input.toolId === "file.read" || input.toolId === "skill.load" || input.toolId === "media.viewImage") {
563
658
  for (const field of ["path", "filePath", "imagePath"]) {
@@ -596,12 +691,15 @@ function normalizeWorkspacePathContract(input) {
596
691
  }
597
692
  }
598
693
  const workspaceOutsideAllowedRoots = normalizations.some((item) => item.workspaceOutsideAllowedRoots === true || item.reason === "OUTSIDE_ALLOWED_ROOTS");
694
+ const workspacePathAccess = input.toolId === "patch.apply" || normalizations.some((item) => item.workspacePathAccess === "write")
695
+ ? "write"
696
+ : "read";
599
697
  return {
600
698
  ok: true,
601
699
  args: withWorkspaceNormalizationMetadata(nextArgs, normalizations, workspaceOutsideAllowedRoots
602
700
  ? {
603
701
  workspaceOutsideAllowedRoots: true,
604
- workspacePathAccess: pathAccess,
702
+ workspacePathAccess,
605
703
  }
606
704
  : {}),
607
705
  metadata: normalizations[0] === undefined
@@ -2132,6 +2230,7 @@ async function prepareKernelSandbox(input) {
2132
2230
  const sandbox = await prepareSandboxRuntime(input.manifest.sandbox, {
2133
2231
  cwd: input.options.sandbox?.cwd ?? input.storageRuntime.layout.workspace.root,
2134
2232
  runSmoke: input.options.sandbox?.runSmoke ?? false,
2233
+ providerReady: input.options.sandbox?.provider !== undefined,
2135
2234
  });
2136
2235
  input.events.push(...sandbox.events);
2137
2236
  await input.store.appendEvent(event(input.sessionId, "event:sandbox.prepared", "runtime.sandboxPlane.prepared", input.now(), { sandbox }));
@@ -2456,6 +2555,48 @@ function compactRequestMaterials(input) {
2456
2555
  return [...originalMaterials, ...governedSessionSummaryMaterial, ...governedProjectContext];
2457
2556
  }
2458
2557
  async function executeBaseToolDecision(input) {
2558
+ const dynamicToolSpec = input.manifest.harness.tools.find((tool) => tool.toolId === input.toolId);
2559
+ const dynamicRuntimeToolId = typeof dynamicToolSpec?.metadata?.runtimeToolId === "string"
2560
+ ? dynamicToolSpec.metadata.runtimeToolId
2561
+ : undefined;
2562
+ const dynamicRuntimeArguments = isRecord(dynamicToolSpec?.metadata?.runtimeArguments)
2563
+ ? dynamicToolSpec.metadata.runtimeArguments
2564
+ : undefined;
2565
+ if (dynamicRuntimeToolId !== undefined && dynamicRuntimeToolId !== input.toolId) {
2566
+ const delegatedArgs = dynamicRuntimeToolId === "mcp.use"
2567
+ ? {
2568
+ ...(dynamicRuntimeArguments ?? {}),
2569
+ arguments: input.args,
2570
+ }
2571
+ : {
2572
+ ...(dynamicRuntimeArguments ?? {}),
2573
+ ...input.args,
2574
+ };
2575
+ const delegated = await executeBaseToolDecision({
2576
+ ...input,
2577
+ toolId: dynamicRuntimeToolId,
2578
+ args: delegatedArgs,
2579
+ });
2580
+ return {
2581
+ ...delegated,
2582
+ record: {
2583
+ ...delegated.record,
2584
+ toolId: input.toolId,
2585
+ arguments: input.args,
2586
+ },
2587
+ observation: {
2588
+ ...delegated.observation,
2589
+ material: {
2590
+ ...delegated.observation.material,
2591
+ metadata: {
2592
+ ...(delegated.observation.material.metadata ?? {}),
2593
+ dynamicRuntimeToolId,
2594
+ dynamicMcpToolId: input.toolId,
2595
+ },
2596
+ },
2597
+ },
2598
+ };
2599
+ }
2459
2600
  let toolArguments = enrichToolArguments(input.manifest, input.toolId, input.args, {
2460
2601
  runtimeId: input.runtimeId,
2461
2602
  sessionId: input.sessionId,
@@ -3383,6 +3524,7 @@ export class PraxisRuntimeKernel {
3383
3524
  }),
3384
3525
  });
3385
3526
  const dryRun = options.dryRun !== false;
3527
+ manifest = withRuntimeMcpModule(manifest, options.mcpModule);
3386
3528
  const defaultBaseToolPolicy = {
3387
3529
  workspaceRoot: toolWorkspaceRoot,
3388
3530
  allowedRoots: toolAllowedRoots,
@@ -3413,7 +3555,15 @@ export class PraxisRuntimeKernel {
3413
3555
  sandboxSpec: manifest.sandbox,
3414
3556
  preparedSandbox: sandboxPrepared.sandbox,
3415
3557
  policyProfile: manifest.toolPolicy.profile,
3558
+ sandboxProvider: options.sandbox?.provider,
3559
+ sandboxAudit: async (sandboxEvent) => {
3560
+ await store.appendEvent(event(sessionId, `event:sandbox:${sandboxEvent.actionId}:${sandboxEvent.type}`, sandboxEvent.type, now(), { sandbox: sandboxEvent }));
3561
+ },
3416
3562
  remoteSandboxWorker: options.sandbox?.remoteWorker,
3563
+ mcpServers: [
3564
+ ...buildMcpServerProfilesFromManifest(manifest),
3565
+ ...(options.mcpServers ?? []),
3566
+ ],
3417
3567
  emitEvent: (runtimeEvent) => {
3418
3568
  events.push(runtimeEvent.type);
3419
3569
  },
@@ -3423,6 +3573,7 @@ export class PraxisRuntimeKernel {
3423
3573
  id: "praxis-runtime-kernel",
3424
3574
  sessionId,
3425
3575
  };
3576
+ manifest = withRuntimeHarnessTools(manifest, await discoverRuntimeMcpDynamicTools(manifest, executor));
3426
3577
  const maxModelTurns = manifest.harness.loop.maxModelTurns ?? 2;
3427
3578
  const maxToolCalls = manifest.harness.loop.maxToolCalls ?? 4;
3428
3579
  const toolMappings = providerToolMappings(manifest);
@@ -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 bubblewrap sandbox";
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"];
@@ -9,8 +9,8 @@ export const officialRuntimeComponents = [
9
9
  {
10
10
  componentId: "component.sandbox.bubblewrap",
11
11
  kind: "sandbox",
12
- title: "Linux bubblewrap sandbox",
13
- dependencies: [dependency("dependency.binary.bwrap", { kind: "binary", required: true })],
12
+ title: "Linux Raxcell sandbox provider",
13
+ dependencies: [dependency("dependency.binary.raxcell", { kind: "binary", required: true })],
14
14
  fallbackComponentIds: ["component.sandbox.workspaceRollback"],
15
15
  supportedPlatforms: ["linux"],
16
16
  },
@@ -10,7 +10,7 @@ export type DependencySourceRegistry = {
10
10
  sources: readonly DependencySource[];
11
11
  warnings: readonly string[];
12
12
  };
13
- export declare const officialDependencySources: readonly [DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource];
13
+ export declare const officialDependencySources: readonly [DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource, DependencySource];
14
14
  export declare function defaultManagedRoot(input?: {
15
15
  raxToolDepsRoot?: string;
16
16
  env?: Readonly<Record<string, string | undefined>>;
@@ -125,6 +125,18 @@ export const officialDependencySources = [
125
125
  executableName: "rg",
126
126
  versionCommand: { command: "rg", args: ["--version"] },
127
127
  }),
128
+ source({
129
+ dependencyId: "dependency.binary.raxcell",
130
+ sourceId: "official:detect:raxcell",
131
+ displayName: "Raxcell sandbox provider",
132
+ kind: "binary",
133
+ safety: "trusted-detect-only",
134
+ packageManager: "detect-only",
135
+ executableName: "raxcell",
136
+ versionCommand: { command: "raxcell", args: ["--version"] },
137
+ supportedPlatforms: ["linux"],
138
+ installInstructions: "Install @praxis-ai/raxcell with Praxis/Raxode, or set RAXCELL_BIN to an explicit provider binary path.",
139
+ }),
128
140
  source({
129
141
  dependencyId: "dependency.binary.bwrap",
130
142
  sourceId: "official:detect:bwrap",
@@ -7,6 +7,7 @@ export function canonicalDependencyId(input) {
7
7
  const value = input.trim();
8
8
  const legacy = {
9
9
  "binary:bwrap": "dependency.binary.bwrap",
10
+ "binary:raxcell": "dependency.binary.raxcell",
10
11
  "binary:podman|docker": "dependency.binary.podmanOrDocker",
11
12
  "windows:Windows-Sandbox": "dependency.windows.sandbox",
12
13
  "macos:containerization": "dependency.macos.containerization",
@@ -60,6 +61,7 @@ export function legacyDependencyIds(input) {
60
61
  const canonical = canonicalDependencyId(input);
61
62
  const reverse = {
62
63
  "dependency.binary.bwrap": ["binary:bwrap"],
64
+ "dependency.binary.raxcell": ["binary:raxcell"],
63
65
  "dependency.binary.podmanOrDocker": ["binary:podman|docker"],
64
66
  "dependency.windows.sandbox": ["windows:Windows-Sandbox"],
65
67
  "dependency.macos.containerization": ["macos:containerization"],
@@ -2,6 +2,7 @@ import type { BaseToolExecutorPort } from "../../basetool/types.js";
2
2
  import { type BaseToolPolicyProfile, type SandboxSpec } from "../runtimeAgentManifest.js";
3
3
  import type { SandboxRuntimePrepareResult } from "../runtime.sandboxPlane/sandboxRuntimeProvider.js";
4
4
  import { type SandboxRemoteWorkerAdapter } from "../runtime.sandboxPlane/sandboxCommandRunner.js";
5
+ import type { SandboxExecutionProviderPort, SandboxPolicyMiddlewareAuditEvent } from "../runtime.sandboxPlane/sandboxPolicyMiddleware.js";
5
6
  import { type McpRuntimeServerProfile } from "./mcpRuntimeAdapter.js";
6
7
  export type RuntimeBaseToolExecutorEvent = {
7
8
  type: string;
@@ -60,6 +61,8 @@ export type RuntimeBaseToolExecutorContext = {
60
61
  sandboxSpec?: SandboxSpec;
61
62
  preparedSandbox?: SandboxRuntimePrepareResult;
62
63
  policyProfile?: BaseToolPolicyProfile;
64
+ sandboxProvider?: SandboxExecutionProviderPort;
65
+ sandboxAudit?: (event: SandboxPolicyMiddlewareAuditEvent) => Promise<void> | void;
63
66
  remoteSandboxWorker?: SandboxRemoteWorkerAdapter;
64
67
  mcpServers?: readonly McpRuntimeServerProfile[];
65
68
  environment?: Readonly<Record<string, string | undefined>>;
@@ -159,8 +159,6 @@ function legacyPreparedSandbox(value) {
159
159
  }
160
160
  function sandboxModeForContext(context) {
161
161
  const profile = policyProfileForContext(context);
162
- if (profile === "bapr")
163
- return "none";
164
162
  const explicitProviderFamily = context.sandboxSpec?.providerFamily ?? context.sandbox?.providerFamily;
165
163
  const prepared = context.preparedSandbox ?? legacyPreparedSandbox(context.sandbox);
166
164
  if (strongSandboxFamily(explicitProviderFamily)) {
@@ -314,6 +312,10 @@ async function runCommand(context, command, args = [], cwd, input = {}) {
314
312
  if (!resolvedCwd.ok)
315
313
  return resolvedCwd;
316
314
  const commandCwd = String(resolvedCwd.output);
315
+ const root = workspaceRoot(context);
316
+ const approvedWriteRoots = input.approved === true
317
+ ? context.policy?.allowedWriteRoots ?? [root]
318
+ : context.policy?.allowedWriteRoots;
317
319
  if (spec !== undefined) {
318
320
  const result = await runSandboxCommand({
319
321
  runtimeId: context.runtimeId,
@@ -331,13 +333,17 @@ async function runCommand(context, command, args = [], cwd, input = {}) {
331
333
  policyProfile: profile,
332
334
  sandboxMode: sandboxModeForContext(context),
333
335
  filesystem: {
334
- workspaceRoot: workspaceRoot(context),
335
- allowedReadRoots: context.policy?.allowedRoots ?? [workspaceRoot(context)],
336
- ...(context.policy?.allowedWriteRoots === undefined ? {} : { allowedWriteRoots: context.policy.allowedWriteRoots }),
336
+ workspaceRoot: root,
337
+ allowedReadRoots: context.policy?.allowedRoots ?? [root],
338
+ ...(approvedWriteRoots === undefined ? {} : { allowedWriteRoots: approvedWriteRoots }),
339
+ ...(input.approved === true ? { readonlyRoot: false } : {}),
337
340
  },
338
341
  network: input.network,
342
+ approval: input.approved === true ? { accepted: true, grantedBy: "praxis-human-approval" } : undefined,
339
343
  }, {
340
344
  remoteWorker: context.remoteSandboxWorker,
345
+ sandboxProvider: context.sandboxProvider,
346
+ audit: context.sandboxAudit,
341
347
  });
342
348
  if (!result.ok) {
343
349
  return fail(result.error.code, result.error.message, {
@@ -423,6 +429,7 @@ function createShellExecutor(context) {
423
429
  invocationId: typeof request?.toolCallId === "string" ? request.toolCallId : undefined,
424
430
  shellScript: true,
425
431
  timeoutMs: typeof request?.timeoutMs === "number" ? request.timeoutMs : undefined,
432
+ approved: approvedByRuntimeContext(request?.context),
426
433
  });
427
434
  }),
428
435
  };
@@ -436,6 +443,7 @@ function createProcessExecutor(context) {
436
443
  toolId: "process.run",
437
444
  invocationId: typeof request?.toolCallId === "string" ? request.toolCallId : undefined,
438
445
  timeoutMs: typeof request?.timeoutMs === "number" ? request.timeoutMs : undefined,
446
+ approved: approvedByRuntimeContext(request?.context),
439
447
  });
440
448
  }),
441
449
  wait: withAdapter(context, "process", "wait", async (request) => {
@@ -630,7 +638,22 @@ function createMcpExecutor(context) {
630
638
  const configured = context.mcpServers !== undefined && context.mcpServers.length > 0
631
639
  ? createMcpRuntimeAdapter({ servers: context.mcpServers })
632
640
  : undefined;
633
- const base = configured ?? createUnavailableNamespace("mcp", ["connect", "ping", "listTools", "call", "stream", "listResources", "readResource"]);
641
+ const base = configured ?? createUnavailableNamespace("mcp", [
642
+ "connect",
643
+ "ping",
644
+ "listTools",
645
+ "call",
646
+ "stream",
647
+ "listResources",
648
+ "readResource",
649
+ "listPrompts",
650
+ "getPrompt",
651
+ "setRoots",
652
+ "reportProgress",
653
+ "createSamplingMessage",
654
+ "elicit",
655
+ "setLoggingLevel",
656
+ ]);
634
657
  const callTool = base.callTool ?? base.call;
635
658
  const streamTool = base.streamTool ?? base.stream;
636
659
  return {
@@ -643,7 +666,21 @@ function createMcpExecutor(context) {
643
666
  export function listRuntimeBaseToolImplementedPortPaths(context = {}) {
644
667
  const ports = new Set(baseToolExecutorPortFactoryDescriptor.implementedAdapters);
645
668
  if (context.mcpServers !== undefined && context.mcpServers.length > 0) {
646
- for (const portPath of ["mcp.connect", "mcp.call", "mcp.callTool", "mcp.listTools", "mcp.listResources", "mcp.readResource"]) {
669
+ for (const portPath of [
670
+ "mcp.connect",
671
+ "mcp.call",
672
+ "mcp.callTool",
673
+ "mcp.listTools",
674
+ "mcp.listResources",
675
+ "mcp.readResource",
676
+ "mcp.listPrompts",
677
+ "mcp.getPrompt",
678
+ "mcp.setRoots",
679
+ "mcp.reportProgress",
680
+ "mcp.createSamplingMessage",
681
+ "mcp.elicit",
682
+ "mcp.setLoggingLevel",
683
+ ]) {
647
684
  ports.add(portPath);
648
685
  }
649
686
  }
@@ -694,6 +731,7 @@ export function createRuntimeBaseToolExecutorPort(context) {
694
731
  toolId: typeof request?.toolId === "string" ? request.toolId : "sandbox.run",
695
732
  invocationId: typeof request?.invocationId === "string" ? request.invocationId : undefined,
696
733
  network: request?.network === "allow" ? "allow" : undefined,
734
+ approved: approvedByRuntimeContext(request?.context),
697
735
  });
698
736
  }),
699
737
  },
@@ -324,6 +324,62 @@ export function createMcpRuntimeAdapter(options) {
324
324
  })) : [];
325
325
  return success({ uri: requestInput.resourceUri, contents, truncated: false, providerMetadata: metadata(connected.output.profile, { method: "resources/read" }), raw: read.output });
326
326
  },
327
+ async listPrompts(requestInput) {
328
+ const connected = await getConnection(requestInput.serverId);
329
+ if (!connected.ok)
330
+ return connected;
331
+ const listed = await request(connected.output, "prompts/list", requestInput.cursor === undefined ? {} : { cursor: requestInput.cursor });
332
+ if (!listed.ok)
333
+ return listed;
334
+ const raw = resultObject(listed.output);
335
+ const prompts = Array.isArray(raw.prompts) ? raw.prompts.filter(isObject).map((prompt) => ({
336
+ name: String(prompt.name ?? ""),
337
+ title: typeof prompt.title === "string" ? prompt.title : undefined,
338
+ description: typeof prompt.description === "string" ? prompt.description : undefined,
339
+ arguments: Array.isArray(prompt.arguments) ? prompt.arguments : undefined,
340
+ raw: prompt,
341
+ })).filter((prompt) => prompt.name.length > 0) : [];
342
+ return success({ prompts, nextCursor: typeof raw.nextCursor === "string" ? raw.nextCursor : undefined, providerMetadata: metadata(connected.output.profile, { method: "prompts/list" }), raw: listed.output });
343
+ },
344
+ async getPrompt(requestInput) {
345
+ const connected = await getConnection(requestInput.serverId);
346
+ if (!connected.ok)
347
+ return connected;
348
+ const read = await request(connected.output, "prompts/get", { name: requestInput.name, arguments: requestInput.arguments ?? {} });
349
+ if (!read.ok)
350
+ return read;
351
+ return success(read.output, metadata(connected.output.profile, { method: "prompts/get", promptName: requestInput.name }));
352
+ },
353
+ async setRoots(requestInput) {
354
+ const profile = getProfile(requestInput.serverId);
355
+ if (profile === undefined)
356
+ return failure("MCP_SERVER_NOT_CONFIGURED", `MCP server '${requestInput.serverId}' is not configured.`);
357
+ return success({ serverId: requestInput.serverId, roots: requestInput.roots ?? [], status: "registered", providerMetadata: metadata(profile, { hostSemantic: "roots" }) });
358
+ },
359
+ async reportProgress(requestInput) {
360
+ const profile = getProfile(requestInput.serverId);
361
+ if (profile === undefined)
362
+ return failure("MCP_SERVER_NOT_CONFIGURED", `MCP server '${requestInput.serverId}' is not configured.`);
363
+ return success({ serverId: requestInput.serverId, progressToken: requestInput.progressToken, progress: requestInput.progress, total: requestInput.total, status: "reported", providerMetadata: metadata(profile, { hostSemantic: "progress" }) });
364
+ },
365
+ async createSamplingMessage(requestInput) {
366
+ const profile = getProfile(requestInput.serverId);
367
+ if (profile === undefined)
368
+ return failure("MCP_SERVER_NOT_CONFIGURED", `MCP server '${requestInput.serverId}' is not configured.`);
369
+ return success({ serverId: requestInput.serverId, status: "accepted", request: requestInput, providerMetadata: metadata(profile, { hostSemantic: "sampling" }) });
370
+ },
371
+ async elicit(requestInput) {
372
+ const profile = getProfile(requestInput.serverId);
373
+ if (profile === undefined)
374
+ return failure("MCP_SERVER_NOT_CONFIGURED", `MCP server '${requestInput.serverId}' is not configured.`);
375
+ return success({ serverId: requestInput.serverId, status: "pending", request: requestInput, providerMetadata: metadata(profile, { hostSemantic: "elicitation" }) });
376
+ },
377
+ async setLoggingLevel(requestInput) {
378
+ const profile = getProfile(requestInput.serverId);
379
+ if (profile === undefined)
380
+ return failure("MCP_SERVER_NOT_CONFIGURED", `MCP server '${requestInput.serverId}' is not configured.`);
381
+ return success({ serverId: requestInput.serverId, level: requestInput.level ?? "info", status: "configured", providerMetadata: metadata(profile, { hostSemantic: "logging" }) });
382
+ },
327
383
  async createResource(requestInput) {
328
384
  const profile = getProfile(requestInput.serverId);
329
385
  if (profile === undefined)
@@ -0,0 +1,114 @@
1
+ import { type ExposureState, type McpCompatibleSurface, type McpPlusManifest, type NativeToolDeclaration } from "@praxis-ai/mcp-plus";
2
+ import type { ToolSpec } from "../runtimeAgentManifest.js";
3
+ import type { McpRuntimeServerProfile } from "../runtime.execEngine/mcpRuntimeAdapter.js";
4
+ export type McpHarnessServerMode = "native" | "mcp-plus";
5
+ export type McpTransportSpec = {
6
+ transport: "stdio";
7
+ command: string;
8
+ args?: readonly string[];
9
+ cwd?: string;
10
+ env?: Readonly<Record<string, string | undefined>>;
11
+ timeoutMs?: number;
12
+ framing?: "content-length" | "line-json";
13
+ } | {
14
+ transport: "http" | "sse";
15
+ url: string;
16
+ sseUrl?: string;
17
+ headers?: Readonly<Record<string, string>>;
18
+ timeoutMs?: number;
19
+ };
20
+ export type McpHarnessServerSpec = McpTransportSpec & {
21
+ serverId: string;
22
+ mode: McpHarnessServerMode;
23
+ title?: string;
24
+ summary?: string;
25
+ manifest?: McpPlusManifest;
26
+ metadata?: Readonly<Record<string, unknown>>;
27
+ };
28
+ export type McpApplicationServerInput = McpTransportSpec & {
29
+ serverId: string;
30
+ mode?: McpHarnessServerMode;
31
+ title?: string;
32
+ summary?: string;
33
+ manifest?: McpPlusManifest;
34
+ metadata?: Readonly<Record<string, unknown>>;
35
+ };
36
+ type HttpMcpHelperInput = {
37
+ url: string;
38
+ sseUrl?: string;
39
+ headers?: Readonly<Record<string, string>>;
40
+ timeoutMs?: number;
41
+ } & Partial<Pick<McpHarnessServerSpec, "mode" | "title" | "summary" | "manifest" | "metadata">>;
42
+ export type McpHarnessModuleSpec = {
43
+ kind: "praxis.mcp.module";
44
+ version: "praxis.mcp.v1";
45
+ servers: readonly McpHarnessServerSpec[];
46
+ recommended: true;
47
+ metadata?: Readonly<Record<string, unknown>>;
48
+ };
49
+ export type McpApplicationServerView = {
50
+ serverId: string;
51
+ mode: McpHarnessServerMode;
52
+ transport: McpHarnessServerSpec["transport"];
53
+ title?: string;
54
+ summary?: string;
55
+ manifestPresent: boolean;
56
+ status: "declared" | "mounted" | "error";
57
+ toolCount?: number;
58
+ visibleToolCount?: number;
59
+ indexedToolCount?: number;
60
+ publicSafe: true;
61
+ };
62
+ export type McpApplicationStateView = {
63
+ servers: readonly McpApplicationServerView[];
64
+ recommendedMode: "mcp-plus";
65
+ nativeCompatible: true;
66
+ publicSafe: true;
67
+ };
68
+ export type McpExposurePlanServer = {
69
+ serverId: string;
70
+ mode: McpHarnessServerMode;
71
+ surface: McpCompatibleSurface;
72
+ dynamicToolSpecs: readonly ToolSpec[];
73
+ };
74
+ export type McpHarnessExposurePlan = {
75
+ servers: readonly McpExposurePlanServer[];
76
+ };
77
+ export type McpPlusApplicationServerInput = McpTransportSpec & {
78
+ serverId: string;
79
+ manifest: McpPlusManifest;
80
+ title?: string;
81
+ summary?: string;
82
+ metadata?: Readonly<Record<string, unknown>>;
83
+ };
84
+ export declare function mcpServer(serverId: string, input: McpTransportSpec & Partial<Pick<McpHarnessServerSpec, "mode" | "title" | "summary" | "manifest" | "metadata">>): McpHarnessServerSpec;
85
+ export declare const mcp: {
86
+ readonly module: (input: {
87
+ servers: readonly McpHarnessServerSpec[];
88
+ metadata?: Readonly<Record<string, unknown>>;
89
+ }) => McpHarnessModuleSpec;
90
+ readonly stdio: (serverId: string, input: Omit<Extract<McpTransportSpec, {
91
+ transport: "stdio";
92
+ }>, "transport"> & Partial<Pick<McpHarnessServerSpec, "mode" | "title" | "summary" | "manifest" | "metadata">>) => McpHarnessServerSpec;
93
+ readonly http: (serverId: string, input: HttpMcpHelperInput) => McpHarnessServerSpec;
94
+ readonly sse: (serverId: string, input: HttpMcpHelperInput) => McpHarnessServerSpec;
95
+ readonly recommendedTools: () => readonly ToolSpec[];
96
+ };
97
+ export declare function isMcpHarnessModuleSpec(value: unknown): value is McpHarnessModuleSpec;
98
+ export declare function mcpHarnessModuleFrom(input: {
99
+ modules?: Readonly<Record<string, unknown>>;
100
+ }): McpHarnessModuleSpec | undefined;
101
+ export declare function runtimeRequirementsForMcpModule(module: McpHarnessModuleSpec | undefined): readonly string[];
102
+ export declare function toMcpRuntimeServerProfile(server: McpHarnessServerSpec): McpRuntimeServerProfile;
103
+ export declare function buildMcpServerProfilesFromManifest(input: {
104
+ harness: {
105
+ modules: Readonly<Record<string, unknown>>;
106
+ };
107
+ }): readonly McpRuntimeServerProfile[];
108
+ export declare function createMcpApplicationStateView(module: McpHarnessModuleSpec | undefined): McpApplicationStateView;
109
+ export declare function planMcpHarnessExposure(manifest: {
110
+ harness: {
111
+ modules: Readonly<Record<string, unknown>>;
112
+ };
113
+ }, nativeToolInventoryByServerId: Readonly<Record<string, readonly NativeToolDeclaration[]>>, stateByServerId?: Readonly<Record<string, Partial<ExposureState>>>): McpHarnessExposurePlan;
114
+ export {};