pi-harness-runtime 1.1.395 → 1.1.396

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 (116) hide show
  1. package/AGENTS.md +10 -1
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/dist/workspace-env-format.d.ts +24 -0
  25. package/packages/contracts/dist/workspace-env-format.js +41 -1
  26. package/packages/contracts/dist/workspace-env.js +7 -4
  27. package/packages/contracts/package.json +1 -1
  28. package/packages/contracts/src/workspace-env-format.ts +38 -1
  29. package/packages/contracts/src/workspace-env.ts +8 -3
  30. package/packages/contracts/test/workspace-env.test.ts +48 -0
  31. package/packages/cookie-sanitizer/package.json +1 -1
  32. package/packages/cost-optimizer/package.json +1 -1
  33. package/packages/dependency-analyzer/package.json +1 -1
  34. package/packages/django-plugin/package.json +1 -1
  35. package/packages/doc-generator/package.json +1 -1
  36. package/packages/durable-review/package.json +1 -1
  37. package/packages/e2e-tester/package.json +1 -1
  38. package/packages/evaluation-engine/package.json +1 -1
  39. package/packages/evaluation-runner/package.json +1 -1
  40. package/packages/event-bus/package.json +1 -1
  41. package/packages/event-sink/package.json +1 -1
  42. package/packages/event-store/package.json +1 -1
  43. package/packages/experience-replay/package.json +1 -1
  44. package/packages/feedback-collector/package.json +1 -1
  45. package/packages/file-copy-helper/package.json +1 -1
  46. package/packages/framework-detector/package.json +1 -1
  47. package/packages/framework-plugin-sdk/package.json +1 -1
  48. package/packages/frappe-plugin/package.json +1 -1
  49. package/packages/generic-web-plugin/package.json +1 -1
  50. package/packages/health-monitor/package.json +1 -1
  51. package/packages/intent-analyzer/package.json +1 -1
  52. package/packages/jev-judge/package.json +1 -1
  53. package/packages/knowledge-graph/package.json +1 -1
  54. package/packages/knowledge-retrieval/package.json +1 -1
  55. package/packages/laravel-plugin/package.json +1 -1
  56. package/packages/learning-engine/package.json +1 -1
  57. package/packages/logging/package.json +1 -1
  58. package/packages/mcp-adapter/package.json +1 -1
  59. package/packages/memory-engine/package.json +1 -1
  60. package/packages/milestone-manager/package.json +1 -1
  61. package/packages/model-registry/package.json +1 -1
  62. package/packages/nextjs-plugin/package.json +1 -1
  63. package/packages/notification/package.json +1 -1
  64. package/packages/observability/package.json +1 -1
  65. package/packages/okf-indexer/package.json +1 -1
  66. package/packages/paperclip-integration/package.json +1 -1
  67. package/packages/performance-optimizer/package.json +1 -1
  68. package/packages/permission-mode/package.json +1 -1
  69. package/packages/privilege-broker/package.json +1 -1
  70. package/packages/project-analyzer/package.json +1 -1
  71. package/packages/project-bootstrap/package.json +1 -1
  72. package/packages/projection-engine/package.json +1 -1
  73. package/packages/prompt-compiler/package.json +1 -1
  74. package/packages/prompt-versioning/package.json +1 -1
  75. package/packages/provider-adapter-sdk/package.json +1 -1
  76. package/packages/provider-router/package.json +1 -1
  77. package/packages/provider-selector/package.json +1 -1
  78. package/packages/providers/package.json +1 -1
  79. package/packages/qdrant-skills/package.json +1 -1
  80. package/packages/quota-manager/package.json +1 -1
  81. package/packages/rate-limiter/package.json +1 -1
  82. package/packages/react-vite-plugin/package.json +1 -1
  83. package/packages/relay/package.json +1 -1
  84. package/packages/release-manager/package.json +1 -1
  85. package/packages/requirement-compiler/package.json +1 -1
  86. package/packages/runtime/package.json +1 -1
  87. package/packages/scheduler/package.json +1 -1
  88. package/packages/scheduler-adapter/package.json +1 -1
  89. package/packages/session/package.json +1 -1
  90. package/packages/session-api/package.json +1 -1
  91. package/packages/session-export/package.json +1 -1
  92. package/packages/shared-context/package.json +1 -1
  93. package/packages/skill-mcp-client/package.json +1 -1
  94. package/packages/skill-registry/package.json +1 -1
  95. package/packages/skills/package.json +1 -1
  96. package/packages/sprint-planner/package.json +1 -1
  97. package/packages/subscription-engine/package.json +1 -1
  98. package/packages/task-compiler/package.json +1 -1
  99. package/packages/telegram/package.json +1 -1
  100. package/packages/tencentdb-memory/package.json +1 -1
  101. package/packages/tencentdb-sync/package.json +1 -1
  102. package/packages/test-data-generator/package.json +1 -1
  103. package/packages/test-generator/package.json +1 -1
  104. package/packages/todo-bd-sync/package.json +1 -1
  105. package/packages/token-estimation/package.json +1 -1
  106. package/packages/token-optimizer/package.json +1 -1
  107. package/packages/trajectory/package.json +1 -1
  108. package/packages/tui/package.json +1 -1
  109. package/packages/types/package.json +1 -1
  110. package/packages/voice-api/package.json +1 -1
  111. package/packages/webmcp-tester/package.json +1 -1
  112. package/packages/wiki/package.json +1 -1
  113. package/packages/workflow-events/package.json +1 -1
  114. package/packages/workspace-scanner/package.json +1 -1
  115. package/packages/worktree/package.json +1 -1
  116. package/packages/write-review/package.json +1 -1
package/AGENTS.md CHANGED
@@ -555,7 +555,10 @@ bun run build && bun run start # Server deployment
555
555
  - The facts about the repository (only when NO variable names the environment
556
556
  at all) → a published source repository without a runtime of its own is a
557
557
  development checkout; a `develop`/`development` branch is the fallback
558
- - System RAM → Low RAM warning (<16GB total or <4GB available)
558
+ - System RAM → Low RAM warning (<4GB free, or a small machine (<16GB total)
559
+ with less than 6GB free). The warning is about the headroom that is
560
+ actually free, not about the size of the machine: a 15GB laptop with 6.5GB
561
+ free can build, a 32GB server with 3GB free cannot.
559
562
  - Running processes → dev/build server status
560
563
 
561
564
  The dotenv files are read from the WORKSPACE (not from the current working
@@ -616,6 +619,12 @@ command to use.
616
619
  | `Low RAM` | Never run parallel build + dev |
617
620
  | `Dev server running` | Reuse it, don't restart |
618
621
 
622
+ The status line states the build permission with its reason: `Build allowed:
623
+ yes`, `no - low memory` (not enough free memory) or `no - requires explicit
624
+ request` (the mode is unknown, so the environment has to be investigated
625
+ first). In development mode the permission follows the free memory (a build
626
+ needs ~2GB), not the size of the machine.
627
+
619
628
  **Decision flow:**
620
629
  ```text
621
630
  Check process.env.NODE_ENV first (it decides, also when it names no known mode)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "[BETA] Codex-style /usage status + autonomous coding harness for pi. Not production ready — expect breaking changes.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/a2a-adapter",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Google Agent-to-Agent (A2A) protocol adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/agent-policy",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Agent policy runtime for pi-harness - enforces mandatory rule delivery and mutation gating",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/architecture-generator",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Generate ADR and Mermaid diagrams from project analysis",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/auth",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "pi-harness-runtime — auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-refactor",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Autonomous refactoring engine",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-runtime",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Autonomous Operations Runtime — task inbox, lease management, and worker lifecycle (RFC-0101)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cache-strategy",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Smart caching with TTL and invalidation strategies (RFC-0022)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/capability-registry",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/checkpoint",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Enhanced checkpoint system with incremental snapshots, diff-based storage, and recovery strategies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cli-plugin-sdk",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "CLI Plugin SDK for pi-harness — install, list, remove, update, invoke plugins",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard-plugin",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-generation",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Code generation pipeline for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-review",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Automated code review engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/codex-adapter",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "OpenAI Codex provider adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/codex-watcher",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Watches Codex CLI sessions and emits visible messages for PING-PONG monitoring",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/config-capture",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Auto-capture API-fetched configuration values to AGENTS.md",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/connection-runtime",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Connection management with jittered exponential backoff for pi-harness",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-compiler",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Bounded context selection engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-discovery",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/context-manager",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Context Window Manager — prevents model context overflow via compaction and resume prompts (RFC-0010)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -82,6 +82,30 @@ export interface WorkspaceEnvResult {
82
82
  * without the SQLite cache.
83
83
  */
84
84
  export declare function isLauncherCommand(cmdline: string): boolean;
85
+ /**
86
+ * The RAM rule behind the "low memory" warning.
87
+ *
88
+ * The warning exists to avoid running a build AND a dev server in parallel,
89
+ * so it is about the headroom that is actually available - not about the size
90
+ * of the machine. A 15 GB laptop with 6.5 GB free can build; a 32 GB server
91
+ * with 3 GB free cannot.
92
+ *
93
+ * - Below 4 GB free: always low.
94
+ * - Below 16 GB total: a small machine needs real headroom (6 GB) before a
95
+ * parallel build and dev server are safe.
96
+ */
97
+ export declare function isLowRamWarning(totalGb: number | null, availableGb: number | null): boolean;
98
+ /**
99
+ * The headroom a build needs before it is allowed in development mode.
100
+ */
101
+ export declare const MIN_BUILD_HEADROOM_GB = 2;
102
+ /**
103
+ * The reason the build permission is (not) given.
104
+ *
105
+ * The agent has to know WHY a build is not allowed: an unknown environment
106
+ * needs an explicit request, a memory shortage needs a lighter build.
107
+ */
108
+ export declare function buildAllowanceReason(env: WorkspaceEnvResult): string;
85
109
  export type ServerState = "running" | "not-detected" | "not-checked";
86
110
  /**
87
111
  * The name of the operating system the harness runs on.
@@ -31,6 +31,46 @@
31
31
  export function isLauncherCommand(cmdline) {
32
32
  return /^(npm|bun|yarn|pnpm)\b.*\b(run\s+)?(dev|start)\b/.test(cmdline.trim());
33
33
  }
34
+ /**
35
+ * The RAM rule behind the "low memory" warning.
36
+ *
37
+ * The warning exists to avoid running a build AND a dev server in parallel,
38
+ * so it is about the headroom that is actually available - not about the size
39
+ * of the machine. A 15 GB laptop with 6.5 GB free can build; a 32 GB server
40
+ * with 3 GB free cannot.
41
+ *
42
+ * - Below 4 GB free: always low.
43
+ * - Below 16 GB total: a small machine needs real headroom (6 GB) before a
44
+ * parallel build and dev server are safe.
45
+ */
46
+ export function isLowRamWarning(totalGb, availableGb) {
47
+ if (availableGb === null)
48
+ return false;
49
+ if (availableGb < 4)
50
+ return true;
51
+ if (totalGb === null)
52
+ return false;
53
+ return totalGb < 16 && availableGb < 6;
54
+ }
55
+ /**
56
+ * The headroom a build needs before it is allowed in development mode.
57
+ */
58
+ export const MIN_BUILD_HEADROOM_GB = 2;
59
+ /**
60
+ * The reason the build permission is (not) given.
61
+ *
62
+ * The agent has to know WHY a build is not allowed: an unknown environment
63
+ * needs an explicit request, a memory shortage needs a lighter build.
64
+ */
65
+ export function buildAllowanceReason(env) {
66
+ if (env.buildAllowed)
67
+ return "yes";
68
+ if (env.mode === "unknown")
69
+ return "no - requires explicit request";
70
+ if (env.lowRamWarning)
71
+ return "no - low memory";
72
+ return "no - requires explicit request";
73
+ }
34
74
  /**
35
75
  * The name of the operating system the harness runs on.
36
76
  *
@@ -609,7 +649,7 @@ export function formatEnvForAgentResult(env) {
609
649
  }
610
650
  }
611
651
  message += `\n`;
612
- message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
652
+ message += `Build allowed: ${buildAllowanceReason(env)}\n`;
613
653
  if (env.mode === "unknown") {
614
654
  message += `\nNOTE: Environment unknown - a dev/build script only means a server CAN be started, it does not establish the workspace mode. Explicitly ask the user before build.\n`;
615
655
  }
@@ -16,7 +16,7 @@ import { execSync } from "child_process";
16
16
  import { existsSync, readFileSync, readdirSync, mkdirSync, readlinkSync, realpathSync } from "fs";
17
17
  import { join } from "path";
18
18
  import { platform, totalmem, freemem } from "os";
19
- import { canShowModeValue, collectDotenvVars, collectProcessEnvVars, describeCwdCommand, describeOs, describeServerState, describeWorkspaceMode, detectModeHint, DEPLOYMENT_MARKER_FILES, filterOwnPids, formatEnvForAgentResult, formatEnvStatusLine as formatStatusLine, formatWorkspacePath, isLauncherCommand, MASKED_MODE_VALUE, modeFromRepositoryFacts, modeFromValue, orderDotenvFileNames, parseDotenvVars, resolveWorkspaceMode, } from "./workspace-env-format.js";
19
+ import { canShowModeValue, collectDotenvVars, collectProcessEnvVars, describeCwdCommand, describeOs, describeServerState, describeWorkspaceMode, detectModeHint, DEPLOYMENT_MARKER_FILES, filterOwnPids, formatEnvForAgentResult, formatEnvStatusLine as formatStatusLine, formatWorkspacePath, isLauncherCommand, isLowRamWarning, MASKED_MODE_VALUE, MIN_BUILD_HEADROOM_GB, modeFromRepositoryFacts, modeFromValue, orderDotenvFileNames, parseDotenvVars, resolveWorkspaceMode, } from "./workspace-env-format.js";
20
20
  import { readDotenvFiles } from "./workspace-dotenv.js";
21
21
  const STORE_DIR = join(process.env.HOME || "/home/frappe", ".pi-harness-runtime");
22
22
  /**
@@ -95,11 +95,10 @@ function detectRamInfo() {
95
95
  const freeBytes = freemem();
96
96
  const totalGb = Math.round((totalBytes / (1024 ** 3)) * 10) / 10;
97
97
  const availableGb = Math.round((freeBytes / (1024 ** 3)) * 10) / 10;
98
- const LOW_RAM_GB = 16;
99
98
  return {
100
99
  total: totalGb,
101
100
  available: availableGb,
102
- lowWarning: totalGb < LOW_RAM_GB || availableGb < 4,
101
+ lowWarning: isLowRamWarning(totalGb, availableGb),
103
102
  };
104
103
  }
105
104
  catch {
@@ -709,7 +708,11 @@ export function detectWorkspaceEnv(workspacePath) {
709
708
  buildAllowed = true; // Production build is allowed
710
709
  }
711
710
  else if (mode === "development") {
712
- buildAllowed = !ramInfo.lowWarning; // Allow if not low RAM
711
+ // A build needs headroom, not a big machine. The low-memory warning is
712
+ // about running a build AND a dev server in parallel; a build on its
713
+ // own only needs enough free memory to finish.
714
+ buildAllowed =
715
+ ramInfo.available === null ? false : ramInfo.available >= MIN_BUILD_HEADROOM_GB;
713
716
  }
714
717
  else {
715
718
  buildAllowed = false; // Unknown requires investigation
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/contracts",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "RPC schema definitions for pi-harness runtime",
6
6
  "main": "./dist/index.js",
@@ -90,6 +90,43 @@ export function isLauncherCommand(cmdline: string): boolean {
90
90
  return /^(npm|bun|yarn|pnpm)\b.*\b(run\s+)?(dev|start)\b/.test(cmdline.trim());
91
91
  }
92
92
 
93
+ /**
94
+ * The RAM rule behind the "low memory" warning.
95
+ *
96
+ * The warning exists to avoid running a build AND a dev server in parallel,
97
+ * so it is about the headroom that is actually available - not about the size
98
+ * of the machine. A 15 GB laptop with 6.5 GB free can build; a 32 GB server
99
+ * with 3 GB free cannot.
100
+ *
101
+ * - Below 4 GB free: always low.
102
+ * - Below 16 GB total: a small machine needs real headroom (6 GB) before a
103
+ * parallel build and dev server are safe.
104
+ */
105
+ export function isLowRamWarning(totalGb: number | null, availableGb: number | null): boolean {
106
+ if (availableGb === null) return false;
107
+ if (availableGb < 4) return true;
108
+ if (totalGb === null) return false;
109
+ return totalGb < 16 && availableGb < 6;
110
+ }
111
+
112
+ /**
113
+ * The headroom a build needs before it is allowed in development mode.
114
+ */
115
+ export const MIN_BUILD_HEADROOM_GB = 2;
116
+
117
+ /**
118
+ * The reason the build permission is (not) given.
119
+ *
120
+ * The agent has to know WHY a build is not allowed: an unknown environment
121
+ * needs an explicit request, a memory shortage needs a lighter build.
122
+ */
123
+ export function buildAllowanceReason(env: WorkspaceEnvResult): string {
124
+ if (env.buildAllowed) return "yes";
125
+ if (env.mode === "unknown") return "no - requires explicit request";
126
+ if (env.lowRamWarning) return "no - low memory";
127
+ return "no - requires explicit request";
128
+ }
129
+
93
130
  export type ServerState = "running" | "not-detected" | "not-checked";
94
131
 
95
132
  /**
@@ -756,7 +793,7 @@ export function formatEnvForAgentResult(env: WorkspaceEnvResult): string {
756
793
  }
757
794
 
758
795
  message += `\n`;
759
- message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
796
+ message += `Build allowed: ${buildAllowanceReason(env)}\n`;
760
797
 
761
798
  if (env.mode === "unknown") {
762
799
  message += `\nNOTE: Environment unknown - a dev/build script only means a server CAN be started, it does not establish the workspace mode. Explicitly ask the user before build.\n`;
@@ -32,7 +32,9 @@ import {
32
32
  formatEnvStatusLine as formatStatusLine,
33
33
  formatWorkspacePath,
34
34
  isLauncherCommand,
35
+ isLowRamWarning,
35
36
  MASKED_MODE_VALUE,
37
+ MIN_BUILD_HEADROOM_GB,
36
38
  modeFromRepositoryFacts,
37
39
  modeFromValue,
38
40
  orderDotenvFileNames,
@@ -148,12 +150,11 @@ function detectRamInfo(): {
148
150
  const freeBytes = freemem();
149
151
  const totalGb = Math.round((totalBytes / (1024 ** 3)) * 10) / 10;
150
152
  const availableGb = Math.round((freeBytes / (1024 ** 3)) * 10) / 10;
151
- const LOW_RAM_GB = 16;
152
153
 
153
154
  return {
154
155
  total: totalGb,
155
156
  available: availableGb,
156
- lowWarning: totalGb < LOW_RAM_GB || availableGb < 4,
157
+ lowWarning: isLowRamWarning(totalGb, availableGb),
157
158
  };
158
159
  } catch {
159
160
  return { total: null, available: null, lowWarning: false };
@@ -778,7 +779,11 @@ export function detectWorkspaceEnv(workspacePath: string): WorkspaceEnvResult {
778
779
  if (mode === "production") {
779
780
  buildAllowed = true; // Production build is allowed
780
781
  } else if (mode === "development") {
781
- buildAllowed = !ramInfo.lowWarning; // Allow if not low RAM
782
+ // A build needs headroom, not a big machine. The low-memory warning is
783
+ // about running a build AND a dev server in parallel; a build on its
784
+ // own only needs enough free memory to finish.
785
+ buildAllowed =
786
+ ramInfo.available === null ? false : ramInfo.available >= MIN_BUILD_HEADROOM_GB;
782
787
  } else {
783
788
  buildAllowed = false; // Unknown requires investigation
784
789
  }
@@ -3,6 +3,7 @@ import { mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "n
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import {
6
+ buildAllowanceReason,
6
7
  canShowModeValue,
7
8
  collectDotenvVars,
8
9
  collectProcessEnvVars,
@@ -16,6 +17,7 @@ import {
16
17
  formatEnvStatusLine,
17
18
  formatWorkspacePath,
18
19
  isLauncherCommand,
20
+ isLowRamWarning,
19
21
  isSessionVariableKey,
20
22
  MASKED_MODE_VALUE,
21
23
  modeFromRepositoryFacts,
@@ -752,3 +754,49 @@ describe("the mode detection of a source repository", () => {
752
754
  expect(describeWorkspaceMode(env)).toBe("DEVELOPMENT (source-only repository)");
753
755
  });
754
756
  });
757
+
758
+ describe("the low memory rule", () => {
759
+ test("flags a machine with almost no free memory", () => {
760
+ expect(isLowRamWarning(64, 2)).toBe(true);
761
+ expect(isLowRamWarning(16, 3.9)).toBe(true);
762
+ });
763
+
764
+ test("does not flag a small machine with real headroom", () => {
765
+ expect(isLowRamWarning(15, 6.6)).toBe(false);
766
+ expect(isLowRamWarning(8, 8)).toBe(false);
767
+ });
768
+
769
+ test("flags a big machine that ran out of memory", () => {
770
+ expect(isLowRamWarning(64, 3)).toBe(true);
771
+ });
772
+
773
+ test("does not flag an unknown memory state", () => {
774
+ expect(isLowRamWarning(null, null)).toBe(false);
775
+ expect(isLowRamWarning(null, 12)).toBe(false);
776
+ });
777
+ });
778
+
779
+ describe("the build permission", () => {
780
+ test("allows a build when the environment allows it", () => {
781
+ expect(buildAllowanceReason(makeEnv({ mode: "development", buildAllowed: true }))).toBe("yes");
782
+ });
783
+
784
+ test("asks for an explicit request when the mode is unknown", () => {
785
+ expect(
786
+ buildAllowanceReason(makeEnv({ mode: "unknown", buildAllowed: false, lowRamWarning: false })),
787
+ ).toBe("no - requires explicit request");
788
+ });
789
+
790
+ test("names the memory shortage when the memory is the reason", () => {
791
+ expect(
792
+ buildAllowanceReason(makeEnv({ mode: "development", buildAllowed: false, lowRamWarning: true })),
793
+ ).toBe("no - low memory");
794
+ });
795
+
796
+ test("appears in the agent context", () => {
797
+ const message = formatEnvForAgentResult(
798
+ makeEnv({ mode: "development", buildAllowed: false, lowRamWarning: true }),
799
+ );
800
+ expect(message).toContain("Build allowed: no - low memory");
801
+ });
802
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "pi-harness-runtime — cookie sanitizer (provider-quota cookie file normaliser)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cost-optimizer",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Cost Optimizer for pi-harness-runtime — budget tracking, forecasting, and model selection optimization",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/dependency-analyzer",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/django-plugin",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Django framework analysis — models, management commands, settings",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/doc-generator",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Auto-Doc Generator — generate documentation from source code (RFC-0014)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/durable-review",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "SQLite-backed durable reviewer using @earendil-works/pi-durable",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/e2e-tester",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/evaluation-engine",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Evaluation engine for pi-harness runtime (RFC-0057)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/evaluation-runner",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic evaluation framework for AI outputs (RFC-0020)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-bus",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/event-sink",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "description": "Atomic event sourcing with SQLite transactions for pi-harness",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-store",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/experience-replay",
3
- "version": "1.1.395",
3
+ "version": "1.1.396",
4
4
  "description": "Experience replay for debugging and reproducibility (RFC-0059)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",