@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
@@ -0,0 +1,142 @@
1
+ /*
2
+ * 文件定位:Agent 运行态实现层 / 沙箱策略中间件。
3
+ * 核心目的:把 Praxis policy/governance/approval 的结果翻译给可插拔沙箱 provider。
4
+ * 边界:本文件不替代 policy matrix,不实现沙箱 backend;provider 只报告环境事实并执行。
5
+ */
6
+ export const sandboxPolicyMiddlewareDescriptor = {
7
+ surface: "runtime.sandboxPlane.sandboxPolicyMiddleware",
8
+ policyOwner: "praxis",
9
+ providerRole: "environment-and-execution",
10
+ publicSafe: true,
11
+ };
12
+ function appendGrant(request, grants) {
13
+ return {
14
+ ...request,
15
+ policyGrants: [...request.policyGrants, ...grants],
16
+ };
17
+ }
18
+ function prepareFailureMessage(prepared) {
19
+ return prepared.environmentGap?.publicSafeMessage
20
+ ?? prepared.denial?.message
21
+ ?? "sandbox provider prepareRun failed";
22
+ }
23
+ async function audit(input) {
24
+ await input.audit?.({
25
+ type: input.type,
26
+ actionId: input.request.action.actionId,
27
+ sessionId: input.request.action.sessionId,
28
+ toolId: input.request.action.toolId,
29
+ providerId: input.provider.providerId,
30
+ providerFamily: input.provider.providerFamily,
31
+ payload: input.payload,
32
+ });
33
+ }
34
+ export async function runSandboxPolicyMiddleware(input) {
35
+ let request = input.request;
36
+ const events = [];
37
+ let prepared = await input.provider.prepareRun(request);
38
+ events.push("runtime.sandbox.middleware.prepareRun");
39
+ await audit({
40
+ ...input,
41
+ request,
42
+ type: "runtime.sandbox.middleware.prepareRun",
43
+ payload: {
44
+ ok: prepared.ok,
45
+ environmentGap: prepared.environmentGap ?? null,
46
+ denial: prepared.denial ?? null,
47
+ filesystemLowering: prepared.filesystemLowering ?? null,
48
+ backendArtifacts: prepared.backendArtifacts,
49
+ },
50
+ });
51
+ if (!prepared.ok && prepared.environmentGap !== undefined && prepared.environmentGap !== null) {
52
+ const decision = await input.decideEnvironmentGap?.({
53
+ request,
54
+ prepared,
55
+ environmentGap: prepared.environmentGap,
56
+ }) ?? { type: "deny", reason: prepared.environmentGap.publicSafeMessage };
57
+ events.push(`runtime.sandbox.middleware.policyApplied.${decision.type}`);
58
+ await audit({
59
+ ...input,
60
+ request,
61
+ type: "runtime.sandbox.middleware.policyApplied",
62
+ payload: {
63
+ decision: decision.type,
64
+ reason: "reason" in decision ? decision.reason : undefined,
65
+ grants: "grants" in decision ? decision.grants : undefined,
66
+ },
67
+ });
68
+ if (decision.type === "deny") {
69
+ return {
70
+ ok: false,
71
+ request,
72
+ prepared,
73
+ error: {
74
+ code: "SANDBOX_DENIED",
75
+ message: decision.reason,
76
+ publicSafe: true,
77
+ denial: prepared.denial,
78
+ },
79
+ events,
80
+ };
81
+ }
82
+ request = decision.type === "grant" ? appendGrant(request, decision.grants) : decision.request;
83
+ prepared = await input.provider.prepareRun(request);
84
+ events.push("runtime.sandbox.middleware.prepareRun.afterPolicy");
85
+ await audit({
86
+ ...input,
87
+ request,
88
+ type: "runtime.sandbox.middleware.prepareRun.afterPolicy",
89
+ payload: {
90
+ ok: prepared.ok,
91
+ environmentGap: prepared.environmentGap ?? null,
92
+ denial: prepared.denial ?? null,
93
+ filesystemLowering: prepared.filesystemLowering ?? null,
94
+ backendArtifacts: prepared.backendArtifacts,
95
+ },
96
+ });
97
+ }
98
+ if (!prepared.ok) {
99
+ return {
100
+ ok: false,
101
+ request,
102
+ prepared,
103
+ error: {
104
+ code: "SANDBOX_PREPARE_FAILED",
105
+ message: prepareFailureMessage(prepared),
106
+ publicSafe: true,
107
+ denial: prepared.denial,
108
+ },
109
+ events,
110
+ };
111
+ }
112
+ const result = await input.provider.run(request);
113
+ events.push("runtime.sandbox.provider.run");
114
+ await audit({
115
+ ...input,
116
+ request,
117
+ type: "runtime.sandbox.provider.run",
118
+ payload: {
119
+ ok: result.ok,
120
+ exitCode: result.exitCode,
121
+ timedOut: result.timedOut,
122
+ denial: result.denial ?? null,
123
+ environmentGap: result.environmentGap ?? null,
124
+ filesystemLowering: result.filesystemLowering ?? null,
125
+ },
126
+ });
127
+ if (!result.ok) {
128
+ return {
129
+ ok: false,
130
+ request,
131
+ prepared,
132
+ error: {
133
+ code: "SANDBOX_RUN_FAILED",
134
+ message: result.denial?.message ?? "sandbox provider run failed",
135
+ publicSafe: true,
136
+ denial: result.denial,
137
+ },
138
+ events,
139
+ };
140
+ }
141
+ return { ok: true, request, prepared, result, events };
142
+ }
@@ -74,6 +74,7 @@ export type SandboxRuntimeProvider = {
74
74
  prepare(spec: SandboxSpec, input?: {
75
75
  cwd?: string;
76
76
  runSmoke?: boolean;
77
+ providerReady?: boolean;
77
78
  }): Promise<SandboxRuntimePrepareResult>;
78
79
  runSmoke(spec: SandboxSpec, input?: {
79
80
  cwd?: string;
@@ -87,8 +88,16 @@ export declare const sandboxRuntimeProviderDescriptor: {
87
88
  readonly linuxLiveProvider: "linux-bubblewrap";
88
89
  readonly unsafeSideEffects: false;
89
90
  };
91
+ export declare function resolveRaxcellBinaryPath(input?: {
92
+ env?: Readonly<Record<string, string | undefined>>;
93
+ pathEnv?: string;
94
+ platform?: NodeJS.Platform;
95
+ fileExists?: (filePath: string) => boolean;
96
+ resolvePackage?: (packageName: string) => string | undefined;
97
+ }): string | undefined;
90
98
  export declare function createSandboxRuntimeProvider(providerFamily: SandboxProviderFamily): SandboxRuntimeProvider;
91
99
  export declare function prepareSandboxRuntime(spec: SandboxSpec, input?: {
92
100
  cwd?: string;
93
101
  runSmoke?: boolean;
102
+ providerReady?: boolean;
94
103
  }): Promise<SandboxRuntimePrepareResult>;
@@ -4,11 +4,14 @@
4
4
  * 边界:本文件不替代 BaseTool 语义;工具执行仍然必须走 registry/handler/executor。
5
5
  */
6
6
  import { execFile } from "node:child_process";
7
- import { mkdir, readFile } from "node:fs/promises";
7
+ import { existsSync } from "node:fs";
8
+ import { readFile } from "node:fs/promises";
9
+ import { createRequire } from "node:module";
8
10
  import path from "node:path";
9
11
  import { promisify } from "node:util";
10
12
  import { canonicalDependencyId } from "../runtime.dependencyPlane/index.js";
11
13
  const execFileAsync = promisify(execFile);
14
+ const requireFromHere = createRequire(import.meta.url);
12
15
  export const sandboxRuntimeProviderDescriptor = {
13
16
  surface: "runtime.sandboxPlane",
14
17
  capability: "sandboxRuntimeProvider",
@@ -28,12 +31,51 @@ function providerFamilyFor(spec) {
28
31
  function dependencyRefsFor(spec) {
29
32
  const refs = spec.dependencyRefs ?? [];
30
33
  if (providerFamilyFor(spec) === "linux-bubblewrap" && refs.length === 0) {
31
- return ["dependency.binary.bwrap"];
34
+ return ["dependency.binary.raxcell"];
32
35
  }
33
36
  return refs.map(canonicalDependencyId);
34
37
  }
38
+ function executableNames(name, platform) {
39
+ if (platform !== "win32")
40
+ return [name];
41
+ const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
42
+ .split(";")
43
+ .filter(Boolean);
44
+ return [name, ...extensions.map((extension) => `${name}${extension.toLowerCase()}`), ...extensions.map((extension) => `${name}${extension.toUpperCase()}`)];
45
+ }
46
+ function defaultResolvePackage(packageName) {
47
+ try {
48
+ return requireFromHere.resolve(packageName);
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ }
54
+ export function resolveRaxcellBinaryPath(input = {}) {
55
+ const explicitBinary = (input.env?.RAXCELL_BIN ?? process.env.RAXCELL_BIN)?.trim();
56
+ const fileExists = input.fileExists ?? existsSync;
57
+ if (explicitBinary !== undefined && explicitBinary.length > 0) {
58
+ return fileExists(explicitBinary) ? explicitBinary : undefined;
59
+ }
60
+ const platform = input.platform ?? process.platform;
61
+ const entries = (input.pathEnv ?? process.env.PATH ?? "").split(path.delimiter).filter(Boolean);
62
+ for (const entry of entries) {
63
+ for (const name of executableNames("raxcell", platform)) {
64
+ const candidate = path.join(entry, name);
65
+ if (fileExists(candidate))
66
+ return candidate;
67
+ }
68
+ }
69
+ const resolvedPackage = (input.resolvePackage ?? defaultResolvePackage)("@praxis-ai/raxcell/package.json");
70
+ if (resolvedPackage === undefined)
71
+ return undefined;
72
+ const packageBinary = path.resolve(path.dirname(resolvedPackage), "dist/cli.js");
73
+ return fileExists(packageBinary) ? packageBinary : undefined;
74
+ }
35
75
  function dependencyBinary(ref) {
36
76
  const canonical = canonicalDependencyId(ref);
77
+ if (canonical === "dependency.binary.raxcell")
78
+ return resolveRaxcellBinaryPath();
37
79
  if (canonical === "dependency.binary.bwrap")
38
80
  return "bwrap";
39
81
  if (canonical === "dependency.binary.rg")
@@ -158,14 +200,14 @@ function repairHints(input) {
158
200
  requiresApproval: false,
159
201
  }];
160
202
  }
161
- if (input.missingDependencies.includes("dependency.binary.bwrap")) {
203
+ if (input.missingDependencies.includes("dependency.binary.raxcell")) {
162
204
  return [{
163
- hintId: "linux-bubblewrap:install-bwrap",
205
+ hintId: "linux-bubblewrap:configure-raxcell",
164
206
  severity: "warning",
165
- action: "installDependency",
166
- message: "Install bubblewrap through the system package manager, then rerun rax test.",
167
- commandPreview: ["apt install bubblewrap", "dnf install bubblewrap", "pacman -S bubblewrap"],
168
- requiresApproval: true,
207
+ action: "manualProviderSetup",
208
+ message: "Configure the Raxcell CLI binary path through RAXCELL_BIN or inject RaxcellSandboxProvider at runtime.",
209
+ commandPreview: ["export RAXCELL_BIN=/absolute/path/to/raxcell"],
210
+ requiresApproval: false,
169
211
  }];
170
212
  }
171
213
  return [{
@@ -182,9 +224,9 @@ function dependencyInstallEnvelopes(input) {
182
224
  dependencyId,
183
225
  providerFamily: input.providerFamily,
184
226
  action: "installDependency",
185
- installTarget: dependencyId === "dependency.binary.bwrap" ? "system-global" : "manual",
186
- commandPreview: dependencyId === "dependency.binary.bwrap"
187
- ? ["apt install bubblewrap", "dnf install bubblewrap", "pacman -S bubblewrap"]
227
+ installTarget: "manual",
228
+ commandPreview: dependencyId === "dependency.binary.raxcell"
229
+ ? ["export RAXCELL_BIN=/absolute/path/to/raxcell"]
188
230
  : [],
189
231
  requiresApproval: true,
190
232
  approvalSurface: "interface/application",
@@ -305,12 +347,45 @@ async function probeContractOnly(spec) {
305
347
  },
306
348
  };
307
349
  }
308
- async function probeLinuxBubblewrap(spec) {
350
+ async function probeLinuxBubblewrap(spec, input = {}) {
309
351
  const providerFamily = providerFamilyFor(spec);
310
352
  if (process.platform !== "linux") {
311
353
  return unsupported(providerFamily, spec, "linux-bubblewrap sandbox only runs on Linux");
312
354
  }
313
355
  const dependencyRefs = dependencyRefsFor(spec);
356
+ if (input.providerReady === true) {
357
+ const dependencyChecks = [
358
+ {
359
+ dependencyId: "dependency.binary.raxcell",
360
+ required: true,
361
+ status: "available",
362
+ source: "custom",
363
+ installTarget: "manual",
364
+ publicSafeMessage: "RaxcellSandboxProvider is injected by the Praxis runtime",
365
+ },
366
+ ...(await linuxOptionalChecks()),
367
+ ];
368
+ return {
369
+ providerFamily,
370
+ profile: spec.profile,
371
+ status: "available",
372
+ platform: process.platform,
373
+ dependencyRefs,
374
+ availableDependencies: dependencyRefs,
375
+ missingDependencies: [],
376
+ dependencyChecks,
377
+ dependencyInstallEnvelopes: [],
378
+ selfRepairHints: repairHints({ providerFamily, missingDependencies: [], status: "available" }),
379
+ nextAction: "none",
380
+ publicSafeMessage: "Injected Raxcell linux-bubblewrap sandbox provider is available",
381
+ metadata: {
382
+ sandboxId: spec.sandboxId,
383
+ isolationLevel: spec.isolationLevel ?? "process-namespace",
384
+ provider: "raxcell",
385
+ injectedProvider: true,
386
+ },
387
+ };
388
+ }
314
389
  const dependencies = await dependencyAvailability(dependencyRefs);
315
390
  const dependencyChecks = [
316
391
  ...(await Promise.all(dependencyRefs.map((ref) => binaryCheck(ref, true)))),
@@ -332,11 +407,12 @@ async function probeLinuxBubblewrap(spec) {
332
407
  missingDependencies: dependencies.missing,
333
408
  status: "missingDependency",
334
409
  }),
335
- nextAction: "installDependency",
336
- publicSafeMessage: "linux-bubblewrap sandbox requires the bwrap binary before it can run",
410
+ nextAction: "manualProviderSetup",
411
+ publicSafeMessage: "linux-bubblewrap sandbox requires a configured Raxcell provider before it can run",
337
412
  metadata: {
338
- installHint: "Install bubblewrap through the system package manager, then rerun rax test.",
413
+ installHint: "Set RAXCELL_BIN or inject RaxcellSandboxProvider through runtime sandbox options.",
339
414
  sandboxId: spec.sandboxId,
415
+ requiredProvider: "raxcell",
340
416
  },
341
417
  };
342
418
  }
@@ -352,10 +428,12 @@ async function probeLinuxBubblewrap(spec) {
352
428
  dependencyInstallEnvelopes: [],
353
429
  selfRepairHints: repairHints({ providerFamily, missingDependencies: [], status: "available" }),
354
430
  nextAction: "none",
355
- publicSafeMessage: "linux-bubblewrap sandbox dependencies are available",
431
+ publicSafeMessage: "Raxcell linux-bubblewrap sandbox provider is available",
356
432
  metadata: {
357
433
  sandboxId: spec.sandboxId,
358
434
  isolationLevel: spec.isolationLevel ?? "process-namespace",
435
+ provider: "raxcell",
436
+ binaryPath: resolveRaxcellBinaryPath() ?? "raxcell",
359
437
  },
360
438
  };
361
439
  }
@@ -447,193 +525,23 @@ async function probeWindowsRestricted(spec) {
447
525
  },
448
526
  };
449
527
  }
450
- async function ensureLinuxBubblewrapPaths(cwd) {
451
- const workspace = path.resolve(cwd);
452
- const raxWorkspace = path.join(workspace, ".rax_workspace");
453
- const sandboxRoot = path.join(raxWorkspace, "sandbox");
454
- const home = path.join(sandboxRoot, "home");
455
- const tmp = path.join(sandboxRoot, "tmp");
456
- const artifacts = path.join(sandboxRoot, "artifacts");
457
- await Promise.all([
458
- mkdir(home, { recursive: true }),
459
- mkdir(tmp, { recursive: true }),
460
- mkdir(artifacts, { recursive: true }),
461
- ]);
462
- return { workspace, raxWorkspace, sandboxRoot, home, tmp, artifacts };
463
- }
464
- function linuxBubblewrapSystemMounts() {
465
- const args = [];
466
- for (const dir of ["/usr", "/bin", "/lib", "/lib64", "/etc", "/opt", "/nix"]) {
467
- args.push("--ro-bind-try", dir, dir);
468
- }
469
- return args;
470
- }
471
- function minimalDeviceMounts() {
472
- return [
473
- "--dir",
474
- "/dev",
475
- "--dev-bind",
476
- "/dev/null",
477
- "/dev/null",
478
- "--dev-bind",
479
- "/dev/zero",
480
- "/dev/zero",
481
- "--dev-bind",
482
- "/dev/random",
483
- "/dev/random",
484
- "--dev-bind",
485
- "/dev/urandom",
486
- "/dev/urandom",
487
- ];
488
- }
489
- function smokeScript() {
490
- return [
491
- "test \"$(pwd)\" = /workspace",
492
- "test \"$HOME\" = /sandbox-home",
493
- "test -d /workspace",
494
- "test -r /workspace",
495
- "test -w /workspace/.rax_workspace/sandbox",
496
- "test -w /tmp",
497
- "test -w /artifacts",
498
- "echo praxis-smoke >/workspace/.rax_workspace/sandbox/tmp/praxis-bwrap-smoke.txt",
499
- "test ! -e /home/proview/.ssh",
500
- "test ! -e /host-home",
501
- "test -d /proc",
502
- "echo check:cwd",
503
- "echo check:home",
504
- "echo check:workspace",
505
- "echo check:scratch",
506
- "echo check:tmp",
507
- "echo check:artifacts",
508
- "echo check:host-home-hidden",
509
- "echo check:proc",
510
- ].join(" && ");
511
- }
512
- function smokeCommand(paths) {
513
- return [
514
- "bwrap",
515
- "--unshare-pid",
516
- "--unshare-ipc",
517
- "--unshare-uts",
518
- "--unshare-net",
519
- "--die-with-parent",
520
- ...linuxBubblewrapSystemMounts(),
521
- "--proc",
522
- "/proc",
523
- ...minimalDeviceMounts(),
524
- "--ro-bind",
525
- paths.workspace,
526
- "/workspace",
527
- "--bind",
528
- paths.raxWorkspace,
529
- "/workspace/.rax_workspace",
530
- "--bind",
531
- paths.home,
532
- "/sandbox-home",
533
- "--bind",
534
- paths.tmp,
535
- "/tmp",
536
- "--bind",
537
- paths.artifacts,
538
- "/artifacts",
539
- "--setenv",
540
- "HOME",
541
- "/sandbox-home",
542
- "--setenv",
543
- "TMPDIR",
544
- "/tmp",
545
- "--setenv",
546
- "PRAXIS_SANDBOX",
547
- "linux-bubblewrap",
548
- "--chdir",
549
- "/workspace",
550
- "/usr/bin/env",
551
- "sh",
552
- "-lc",
553
- smokeScript(),
554
- ];
555
- }
556
- async function runLinuxBubblewrapSmoke(spec, input = {}) {
557
- const cwd = input.cwd ?? process.cwd();
558
- const paths = await ensureLinuxBubblewrapPaths(cwd);
559
- const command = smokeCommand(paths);
560
- if (process.platform !== "linux") {
561
- return {
562
- providerFamily: providerFamilyFor(spec),
563
- profile: spec.profile,
564
- status: "skipped",
565
- commandPreview: command,
566
- checks: [{ checkId: "platform", status: "skipped", publicSafeMessage: "linux-bubblewrap smoke only runs on Linux" }],
567
- publicSafeMessage: "linux-bubblewrap smoke is skipped outside Linux",
568
- metadata: { cwd, sandboxRoot: paths.sandboxRoot },
569
- };
570
- }
571
- try {
572
- const result = await execFileAsync(command[0] ?? "bwrap", [...command.slice(1)], {
573
- cwd,
574
- timeout: spec.resourceLimits.timeoutMs ?? 5_000,
575
- maxBuffer: spec.resourceLimits.maxOutputBytes ?? 64_000,
576
- env: {
577
- ...process.env,
578
- HOME: paths.home,
579
- TMPDIR: paths.tmp,
580
- },
581
- });
582
- const stdout = result.stdout ?? "";
583
- const passedChecks = [
584
- "cwd",
585
- "home",
586
- "workspace",
587
- "scratch",
588
- "tmp",
589
- "artifacts",
590
- "host-home-hidden",
591
- "proc",
592
- ].map((checkId) => ({
593
- checkId,
594
- status: stdout.includes(`check:${checkId}`) ? "passed" : "failed",
595
- publicSafeMessage: stdout.includes(`check:${checkId}`)
596
- ? `${checkId} boundary check passed`
597
- : `${checkId} boundary check did not report success`,
598
- }));
599
- return {
600
- providerFamily: providerFamilyFor(spec),
601
- profile: spec.profile,
602
- status: "passed",
603
- commandPreview: command,
604
- stdout: result.stdout,
605
- stderr: result.stderr,
606
- checks: passedChecks,
607
- publicSafeMessage: "linux-bubblewrap workspace-only smoke passed",
608
- metadata: {
609
- cwd,
610
- sandboxRoot: paths.sandboxRoot,
611
- home: paths.home,
612
- tmp: paths.tmp,
613
- artifacts: paths.artifacts,
614
- networkMode: "denied",
615
- deviceExposure: "minimal",
616
- },
617
- };
618
- }
619
- catch (error) {
620
- const err = error;
621
- return {
622
- providerFamily: providerFamilyFor(spec),
623
- profile: spec.profile,
624
- status: "failed",
625
- commandPreview: command,
626
- stdout: err.stdout,
627
- stderr: err.stderr,
628
- checks: [{ checkId: "linux-bubblewrap", status: "failed", publicSafeMessage: "bubblewrap command did not complete all boundary checks" }],
629
- publicSafeMessage: "linux-bubblewrap smoke failed; user namespaces or bwrap policy may be unavailable",
630
- metadata: {
631
- cwd,
632
- sandboxRoot: paths.sandboxRoot,
633
- error: err.message ?? "unknown bwrap failure",
634
- },
635
- };
636
- }
528
+ function runLinuxBubblewrapSmoke(spec, input = {}) {
529
+ return Promise.resolve({
530
+ providerFamily: providerFamilyFor(spec),
531
+ profile: spec.profile,
532
+ status: "skipped",
533
+ commandPreview: [],
534
+ checks: [{
535
+ checkId: "raxcell-provider",
536
+ status: "skipped",
537
+ publicSafeMessage: "Raxcell owns Linux backend execution checks; Praxis runtime provider only verifies provider availability.",
538
+ }],
539
+ publicSafeMessage: "Raxcell linux-bubblewrap smoke is provider-owned and skipped by Praxis runtime.",
540
+ metadata: {
541
+ cwd: input.cwd ?? process.cwd(),
542
+ provider: "raxcell",
543
+ },
544
+ });
637
545
  }
638
546
  export function createSandboxRuntimeProvider(providerFamily) {
639
547
  return {
@@ -651,12 +559,14 @@ export function createSandboxRuntimeProvider(providerFamily) {
651
559
  return probeContractOnly(spec);
652
560
  },
653
561
  async prepare(spec, input = {}) {
654
- const probe = await this.probe(spec);
562
+ const probe = providerFamilyFor(spec) === "linux-bubblewrap" && input.providerReady === true
563
+ ? await probeLinuxBubblewrap(spec, { providerReady: true })
564
+ : await this.probe(spec);
655
565
  const shouldSmoke = input.runSmoke === true && probe.status === "available" && providerFamilyFor(spec) === "linux-bubblewrap";
656
566
  const smoke = shouldSmoke
657
567
  ? await this.runSmoke(spec, { cwd: input.cwd })
658
568
  : undefined;
659
- const ready = probe.status === "available" && (smoke === undefined || smoke.status === "passed");
569
+ const ready = probe.status === "available" && smoke?.status !== "failed";
660
570
  return {
661
571
  providerFamily: providerFamilyFor(spec),
662
572
  profile: spec.profile,
@@ -11,6 +11,7 @@ import { createHash } from "node:crypto";
11
11
  import { createBaseToolSupportCatalog } from "./runtime.execEngine/baseToolSupportCatalog.js";
12
12
  import { canonicalDependencyId, dependencyKindFromId, } from "./runtime.dependencyPlane/index.js";
13
13
  import { capability as capabilityAuthoring, } from "./runtime.provisionPlane/index.js";
14
+ import { mcpHarnessModuleFrom, runtimeRequirementsForMcpModule, } from "./runtime.mcpPlane/index.js";
14
15
  export class PromptPack {
15
16
  promptPackId;
16
17
  base;
@@ -352,7 +353,7 @@ export const sandbox = {
352
353
  profile: "linux-bubblewrap",
353
354
  providerFamily: "linux-bubblewrap",
354
355
  isolationLevel: "process-namespace",
355
- dependencyRefs: input.dependencyRefs ?? ["binary:bwrap"],
356
+ dependencyRefs: input.dependencyRefs ?? ["dependency.binary.raxcell"],
356
357
  mountPolicy: input.mountPolicy ?? {
357
358
  workspaceRootRef: "rax.workspace",
358
359
  allowedReadRoots: ["workspace", ".rax_workspace"],
@@ -367,7 +368,7 @@ export const sandbox = {
367
368
  windows: "unsupported",
368
369
  },
369
370
  metadata: {
370
- provider: "bubblewrap",
371
+ provider: "raxcell",
371
372
  providerVersion: "v2",
372
373
  flatpakCompatible: true,
373
374
  fallback: "explicit-only",
@@ -1268,7 +1269,10 @@ function normalizeHarness(input, authoring, normalizedTools) {
1268
1269
  statePlane: authoring.statePlane,
1269
1270
  frameworkCore: authoring.frameworkCore,
1270
1271
  modules: input.modules ?? {},
1271
- runtimeRequirements: cleanList(input.runtimeRequirements),
1272
+ runtimeRequirements: cleanList([
1273
+ ...(input.runtimeRequirements ?? []),
1274
+ ...runtimeRequirementsForMcpModule(mcpHarnessModuleFrom({ modules: input.modules })),
1275
+ ]),
1272
1276
  capabilities: authoring.capabilities,
1273
1277
  dependencies: authoring.dependencies,
1274
1278
  metadata: input.metadata ?? {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxis-ai/praxis",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Praxis agentCore architecture scaffold, tests, and engineering contracts.",
@@ -154,6 +154,8 @@
154
154
  "@lancedb/lancedb": "^0.27.2",
155
155
  "@modelcontextprotocol/sdk": "^1.29.0",
156
156
  "@openai/agents": "^0.7.2",
157
+ "@praxis-ai/mcp-plus": "^1.0.0",
158
+ "@praxis-ai/raxcell": "^0.1.5",
157
159
  "@types/react": "^18.3.28",
158
160
  "apache-arrow": "^18.1.0",
159
161
  "effect": "^3.21.2",
@@ -97,15 +97,15 @@ function createRaxodeProvisioning(options) {
97
97
  source: "raxode-backend",
98
98
  },
99
99
  }),
100
- praxis.dependency.binary("bwrap", {
100
+ praxis.dependency.binary("raxcell", {
101
101
  required: options.sandboxProfile === "linuxBubblewrap",
102
102
  install: "manual",
103
- reason: "Prepare the Linux bubblewrap sandbox provider; Praxis degrades to workspace rollback when it is unavailable.",
103
+ reason: "Prepare the Raxcell Linux sandbox provider; Praxis degrades to workspace rollback when it is unavailable.",
104
104
  metadata: {
105
105
  source: "raxode-sandbox",
106
106
  providerFamily: "linux-bubblewrap",
107
107
  defaultWithSandbox: true,
108
- installHint: "Install bubblewrap with the OS package manager, for example apt install bubblewrap.",
108
+ installHint: "Set RAXCELL_BIN or put the raxcell CLI on PATH.",
109
109
  },
110
110
  }),
111
111
  praxis.dependency.secretRef("provider.core.main", {
@@ -118,7 +118,7 @@ export function createRaxodeBackendModuleInventory(input) {
118
118
  hasModuleMode(manifest, "dependencyPlane"),
119
119
  dependencyIds.has("dependency.binary.node"),
120
120
  dependencyIds.has("dependency.npm.tsx"),
121
- dependencyIds.has("dependency.binary.bwrap"),
121
+ dependencyIds.has("dependency.binary.raxcell"),
122
122
  ],
123
123
  }),
124
124
  surface: "manifest.dependencies",
@@ -191,7 +191,7 @@ export function createRaxodeBackendModuleInventory(input) {
191
191
  hasRequirement(manifest, "praxis.sandboxPlane.declaredCapabilities"),
192
192
  capabilityIds.has("capability.raxode.sandbox"),
193
193
  typeof manifest.sandbox.profile === "string",
194
- dependencyIds.has("dependency.binary.bwrap"),
194
+ dependencyIds.has("dependency.binary.raxcell"),
195
195
  ],
196
196
  }),
197
197
  surface: "manifest.sandbox",
@@ -200,7 +200,7 @@ export function createRaxodeBackendModuleInventory(input) {
200
200
  evidence: [
201
201
  `profile=${manifest.sandbox.profile}`,
202
202
  `providerFamily=${manifest.sandbox.providerFamily ?? "auto"}`,
203
- `hasBwrapDependency=${dependencyIds.has("dependency.binary.bwrap")}`,
203
+ `hasRaxcellDependency=${dependencyIds.has("dependency.binary.raxcell")}`,
204
204
  ],
205
205
  },
206
206
  {