pi-harness-runtime 1.1.387 → 1.1.388

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 (127) hide show
  1. package/AGENTS.md +9 -4
  2. package/index.ts +20 -18
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/package.json +1 -1
  16. package/packages/code-generation/package.json +1 -1
  17. package/packages/code-review/package.json +1 -1
  18. package/packages/codex-adapter/package.json +1 -1
  19. package/packages/codex-watcher/package.json +1 -1
  20. package/packages/config-capture/package.json +1 -1
  21. package/packages/connection-runtime/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/contracts/dist/workspace-env-format.d.ts +71 -0
  26. package/packages/contracts/dist/workspace-env-format.js +120 -0
  27. package/packages/contracts/dist/workspace-env.d.ts +7 -30
  28. package/packages/contracts/dist/workspace-env.js +18 -57
  29. package/packages/contracts/package.json +1 -1
  30. package/packages/contracts/src/workspace-env-format.ts +165 -0
  31. package/packages/contracts/src/workspace-env.ts +27 -99
  32. package/packages/contracts/test/workspace-env.test.ts +144 -0
  33. package/packages/cookie-sanitizer/package.json +1 -1
  34. package/packages/cost-optimizer/package.json +1 -1
  35. package/packages/dependency-analyzer/package.json +1 -1
  36. package/packages/django-plugin/package.json +1 -1
  37. package/packages/doc-generator/package.json +1 -1
  38. package/packages/durable-review/package.json +1 -1
  39. package/packages/e2e-tester/package.json +1 -1
  40. package/packages/evaluation-engine/package.json +1 -1
  41. package/packages/evaluation-runner/package.json +1 -1
  42. package/packages/event-bus/package.json +1 -1
  43. package/packages/event-sink/package.json +1 -1
  44. package/packages/event-store/package.json +1 -1
  45. package/packages/experience-replay/package.json +1 -1
  46. package/packages/feedback-collector/package.json +1 -1
  47. package/packages/file-copy-helper/package.json +1 -1
  48. package/packages/framework-detector/package.json +1 -1
  49. package/packages/framework-plugin-sdk/package.json +1 -1
  50. package/packages/frappe-plugin/package.json +1 -1
  51. package/packages/generic-web-plugin/package.json +1 -1
  52. package/packages/health-monitor/package.json +1 -1
  53. package/packages/intent-analyzer/package.json +1 -1
  54. package/packages/jev-judge/package.json +1 -1
  55. package/packages/knowledge-graph/package.json +1 -1
  56. package/packages/knowledge-retrieval/package.json +1 -1
  57. package/packages/laravel-plugin/package.json +1 -1
  58. package/packages/learning-engine/package.json +1 -1
  59. package/packages/mcp-adapter/package.json +1 -1
  60. package/packages/memory-engine/package.json +1 -1
  61. package/packages/milestone-manager/package.json +1 -1
  62. package/packages/model-registry/package.json +1 -1
  63. package/packages/nextjs-plugin/package.json +1 -1
  64. package/packages/notification/package.json +1 -1
  65. package/packages/observability/package.json +1 -1
  66. package/packages/okf-indexer/package.json +1 -1
  67. package/packages/paperclip-integration/package.json +1 -1
  68. package/packages/performance-optimizer/package.json +1 -1
  69. package/packages/permission-mode/package.json +1 -1
  70. package/packages/privilege-broker/package.json +1 -1
  71. package/packages/project-analyzer/package.json +1 -1
  72. package/packages/project-bootstrap/package.json +1 -1
  73. package/packages/projection-engine/package.json +1 -1
  74. package/packages/prompt-compiler/package.json +1 -1
  75. package/packages/prompt-versioning/package.json +1 -1
  76. package/packages/provider-adapter-sdk/package.json +1 -1
  77. package/packages/provider-router/package.json +1 -1
  78. package/packages/provider-selector/package.json +1 -1
  79. package/packages/providers/package.json +1 -1
  80. package/packages/qdrant-skills/package.json +1 -1
  81. package/packages/quota-manager/package.json +1 -1
  82. package/packages/rate-limiter/package.json +1 -1
  83. package/packages/react-vite-plugin/package.json +1 -1
  84. package/packages/relay/dist/index.d.ts +3 -0
  85. package/packages/relay/dist/index.js +3 -0
  86. package/packages/relay/dist/relay-endpoint.d.ts +94 -0
  87. package/packages/relay/dist/relay-endpoint.js +239 -0
  88. package/packages/relay/package.json +1 -1
  89. package/packages/relay/src/index.ts +17 -0
  90. package/packages/relay/src/relay-endpoint.ts +304 -0
  91. package/packages/relay/test/relay-endpoint.test.ts +278 -0
  92. package/packages/release-manager/package.json +1 -1
  93. package/packages/requirement-compiler/package.json +1 -1
  94. package/packages/runtime/package.json +1 -1
  95. package/packages/scheduler/package.json +1 -1
  96. package/packages/scheduler-adapter/package.json +1 -1
  97. package/packages/session/package.json +1 -1
  98. package/packages/session-api/package.json +1 -1
  99. package/packages/session-export/package.json +1 -1
  100. package/packages/shared-context/package.json +1 -1
  101. package/packages/skill-mcp-client/package.json +1 -1
  102. package/packages/skill-registry/package.json +1 -1
  103. package/packages/skills/package.json +1 -1
  104. package/packages/sprint-planner/package.json +1 -1
  105. package/packages/subscription-engine/package.json +1 -1
  106. package/packages/task-compiler/package.json +1 -1
  107. package/packages/telegram/package.json +1 -1
  108. package/packages/tencentdb-memory/package.json +1 -1
  109. package/packages/tencentdb-sync/package.json +1 -1
  110. package/packages/test-data-generator/package.json +1 -1
  111. package/packages/test-generator/package.json +1 -1
  112. package/packages/todo-bd-sync/package.json +1 -1
  113. package/packages/token-estimation/package.json +1 -1
  114. package/packages/token-optimizer/package.json +1 -1
  115. package/packages/trajectory/package.json +1 -1
  116. package/packages/tui/package.json +1 -1
  117. package/packages/tui/test/harness-footer.test.ts +1 -1
  118. package/packages/types/package.json +1 -1
  119. package/packages/voice-api/package.json +1 -1
  120. package/packages/webmcp-tester/package.json +1 -1
  121. package/packages/wiki/package.json +1 -1
  122. package/packages/workflow-events/package.json +1 -1
  123. package/packages/workspace-scanner/package.json +1 -1
  124. package/packages/worktree/package.json +1 -1
  125. package/packages/write-review/package.json +1 -1
  126. package/scripts/relay-client.ts +24 -4
  127. package/scripts/unified-webhook-server.ts +29 -22
package/AGENTS.md CHANGED
@@ -502,16 +502,21 @@ bun run build && bun run start # Server deployment
502
502
 
503
503
  **This info is shown in UI and delivered to agent context:**
504
504
  ```
505
- Workspace: DEV | dev server: running | build: explicit request only | RAM: 10/16
505
+ Workspace: DEVELOPMENT | dev server: running | build: not running (script available) | RAM: 10/16
506
506
  ```
507
507
 
508
+ The mode words are exactly what the detector emits: `DEVELOPMENT`, `PRODUCTION`,
509
+ `TEST` or `UNKNOWN` (with its reason, e.g. `UNKNOWN (no explicit NODE_ENV)`).
510
+ A dev/build script is a CAPABILITY, not a state: `not running (script available)`
511
+ means the script exists, not that a server runs.
512
+
508
513
  **IMPORTANT Rules:**
509
514
 
510
515
  | State | Action |
511
516
  |-------|--------|
512
- | `Workspace: UNKNOWN` | Explicitly ask user before build |
513
- | `Workspace: DEV` | Use `dev` mode, don't auto-build |
514
- | `Workspace: PROD` | Build allowed, but confirm with user |
517
+ | `Workspace: UNKNOWN (…)` | Explicitly ask user before build |
518
+ | `Workspace: DEVELOPMENT` | Use `dev` mode, don't auto-build |
519
+ | `Workspace: PRODUCTION` | Build allowed, but confirm with user |
515
520
  | `Low RAM` | Never run parallel build + dev |
516
521
  | `Dev server running` | Reuse it, don't restart |
517
522
 
package/index.ts CHANGED
@@ -58,6 +58,7 @@ import { aggregateWindows } from "./windows.ts";
58
58
  // Relay client for streaming callbacks (t3code-style two-way communication)
59
59
  import { RelayClient } from "./packages/relay/src/client.js";
60
60
  import type { CallbackPayload } from "./packages/relay/src/types.js";
61
+ import { describeRelayEndpoint, resolveRelayEndpoint } from "./packages/relay/src/relay-endpoint.js";
61
62
  import { renderStatus } from "./renderer.ts";
62
63
  import {
63
64
  JobStateMachine,
@@ -1559,32 +1560,33 @@ function setRelayTelegramAdapter(adapter: any) {
1559
1560
  * to the adapter for proper greeting verification and handling.
1560
1561
  */
1561
1562
  async function initRelayStream(): Promise<void> {
1562
- const homedir = process.env.HOME || process.env.USERPROFILE || "/home/frappe";
1563
- const keysDir = join(homedir, ".pi-harness-runtime", "keys");
1564
-
1565
- // Check for relay WebSocket URL
1566
- const relayWsUrlPath = join(keysDir, "relay-ws-url.txt");
1567
- const relayTokenPath = join(keysDir, "relay-auth-token.txt");
1563
+ // Use the OS home directory - the relay server resolves its key files
1564
+ // the same way, so both sides always look in the same place.
1565
+ const keysDir = join(homedir(), ".pi-harness-runtime", "keys");
1568
1566
 
1569
- if (!existsSync(relayWsUrlPath) || !existsSync(relayTokenPath)) {
1570
- logToFile("[RelayStream]", "Relay WebSocket URL or token not configured - skipping streaming");
1571
- return;
1572
- }
1567
+ // Resolve the relay endpoint from the SAME sources the relay server uses
1568
+ // (env -> dedicated key file -> the telegram webhook url/secret), so the
1569
+ // client and the server always agree on the URL and the token.
1570
+ // The token value is never logged - only the source it came from.
1571
+ const endpoint = resolveRelayEndpoint({ keysDir });
1573
1572
 
1574
- const relayWsUrl = readFileSync(relayWsUrlPath, "utf-8").trim();
1575
- const relayToken = readFileSync(relayTokenPath, "utf-8").trim();
1576
-
1577
- if (!relayWsUrl || !relayToken) {
1578
- logToFile("[RelayStream]", "Relay URL or token is empty - skipping streaming");
1573
+ if (!endpoint.url || !endpoint.token) {
1574
+ logToFile(
1575
+ "[RelayStream]",
1576
+ `Relay endpoint not configured - skipping streaming (${endpoint.missing.join("; ")})`,
1577
+ );
1579
1578
  return;
1580
1579
  }
1581
1580
 
1582
- logToFile("[RelayStream]", `Connecting to relay at ${relayWsUrl}`);
1581
+ logToFile(
1582
+ "[RelayStream]",
1583
+ `Connecting to relay at ${endpoint.url} (${describeRelayEndpoint(endpoint)})`,
1584
+ );
1583
1585
 
1584
1586
  _relayClient = new RelayClient(
1585
1587
  {
1586
- serverUrl: relayWsUrl,
1587
- token: relayToken,
1588
+ serverUrl: endpoint.url,
1589
+ token: endpoint.token,
1588
1590
  // Keep retrying in the background: the relay server may not be up
1589
1591
  // yet when the harness starts.
1590
1592
  retryOnConnectFailure: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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.387",
3
+ "version": "1.1.388",
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",
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Workspace environment formatting helpers.
3
+ *
4
+ * Split out of workspace-env.ts so the wording can be unit tested without the
5
+ * node:sqlite dependency (bun 1.3.x has no node:sqlite module).
6
+ *
7
+ * Wording rules:
8
+ * - The mode is only reported when it is EXPLICIT (NODE_ENV). An unknown mode
9
+ * is always shown with its reason, never as a bare word next to a dev
10
+ * server state that could be read as proof of the mode.
11
+ * - A dev/build script is a CAPABILITY, not a state. The status line says
12
+ * "not running (script available)" so it cannot be mistaken for a running
13
+ * server or for evidence about the workspace mode.
14
+ */
15
+ /**
16
+ * Workspace mode with explicit states
17
+ */
18
+ export type WorkspaceMode = "development" | "production" | "test" | "unknown";
19
+ /**
20
+ * Evidence source for mode detection
21
+ */
22
+ export interface EnvEvidence {
23
+ source: "process.env" | ".env file" | "package.json" | "command" | "detection";
24
+ key: string;
25
+ value: string;
26
+ timestamp: number;
27
+ }
28
+ /**
29
+ * Workspace environment result
30
+ */
31
+ export interface WorkspaceEnvResult {
32
+ workspacePath: string;
33
+ mode: WorkspaceMode;
34
+ evidence: EnvEvidence[];
35
+ hasDevScript: boolean;
36
+ hasBuildScript: boolean;
37
+ packageManager: string | null;
38
+ devServerRunning: boolean;
39
+ buildServerRunning: boolean;
40
+ ramTotalGb: number | null;
41
+ ramAvailableGb: number | null;
42
+ ramEffectiveLimitGb: number | null;
43
+ lowRamWarning: boolean;
44
+ buildAllowed: boolean;
45
+ detectedAt: number;
46
+ }
47
+ /**
48
+ * Human readable mode label.
49
+ *
50
+ * "unknown" always carries the reason: the mode can only be proven by an
51
+ * explicit NODE_ENV (process env or .env file). When a NODE_ENV IS present
52
+ * but names a mode this detector does not know ("staging"), the label says
53
+ * exactly that instead of claiming there is no NODE_ENV at all.
54
+ */
55
+ export declare function describeWorkspaceMode(env: WorkspaceEnvResult): string;
56
+ /**
57
+ * Describe a dev/build server as a state.
58
+ *
59
+ * "not running (script available)" means the script EXISTS, i.e. a server
60
+ * could be started. It is not evidence that a server is running and not
61
+ * evidence about the workspace mode.
62
+ */
63
+ export declare function describeServerState(running: boolean, hasScript: boolean): string;
64
+ /**
65
+ * One line status for the footer / status bar.
66
+ */
67
+ export declare function formatEnvStatusLine(env: WorkspaceEnvResult): string;
68
+ /**
69
+ * Multi line description for the agent context.
70
+ */
71
+ export declare function formatEnvForAgentResult(env: WorkspaceEnvResult): string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Workspace environment formatting helpers.
3
+ *
4
+ * Split out of workspace-env.ts so the wording can be unit tested without the
5
+ * node:sqlite dependency (bun 1.3.x has no node:sqlite module).
6
+ *
7
+ * Wording rules:
8
+ * - The mode is only reported when it is EXPLICIT (NODE_ENV). An unknown mode
9
+ * is always shown with its reason, never as a bare word next to a dev
10
+ * server state that could be read as proof of the mode.
11
+ * - A dev/build script is a CAPABILITY, not a state. The status line says
12
+ * "not running (script available)" so it cannot be mistaken for a running
13
+ * server or for evidence about the workspace mode.
14
+ */
15
+ /**
16
+ * Human readable mode label.
17
+ *
18
+ * "unknown" always carries the reason: the mode can only be proven by an
19
+ * explicit NODE_ENV (process env or .env file). When a NODE_ENV IS present
20
+ * but names a mode this detector does not know ("staging"), the label says
21
+ * exactly that instead of claiming there is no NODE_ENV at all.
22
+ */
23
+ export function describeWorkspaceMode(env) {
24
+ if (env.mode !== "unknown") {
25
+ return env.mode.toUpperCase();
26
+ }
27
+ const nodeEnv = env.evidence.find((e) => e.key === "NODE_ENV" && (e.source === "process.env" || e.source === ".env file"));
28
+ if (nodeEnv) {
29
+ return `UNKNOWN (NODE_ENV=${nodeEnv.value} is not a known mode)`;
30
+ }
31
+ return "UNKNOWN (no explicit NODE_ENV)";
32
+ }
33
+ /**
34
+ * Describe a dev/build server as a state.
35
+ *
36
+ * "not running (script available)" means the script EXISTS, i.e. a server
37
+ * could be started. It is not evidence that a server is running and not
38
+ * evidence about the workspace mode.
39
+ */
40
+ export function describeServerState(running, hasScript) {
41
+ if (running)
42
+ return "running";
43
+ if (hasScript)
44
+ return "not running (script available)";
45
+ return "no script";
46
+ }
47
+ /**
48
+ * One line status for the footer / status bar.
49
+ */
50
+ export function formatEnvStatusLine(env) {
51
+ const mode = describeWorkspaceMode(env);
52
+ const devStatus = describeServerState(env.devServerRunning, env.hasDevScript);
53
+ const buildStatus = describeServerState(env.buildServerRunning, env.hasBuildScript);
54
+ let status = `Workspace: ${mode} | dev server: ${devStatus} | build: ${buildStatus}`;
55
+ const ramStr = formatRam(env);
56
+ if (ramStr) {
57
+ status += ` | RAM: ${ramStr}`;
58
+ }
59
+ return status;
60
+ }
61
+ /**
62
+ * RAM segment of the status line.
63
+ *
64
+ * Both values are optional - an unreadable /proc must never produce a
65
+ * "7.7/null" line.
66
+ */
67
+ function formatRam(env) {
68
+ const total = env.ramTotalGb;
69
+ const available = env.ramAvailableGb;
70
+ if (total === null && available === null)
71
+ return "";
72
+ if (total === null)
73
+ return `${available}GB available`;
74
+ if (available === null)
75
+ return `${total}GB total`;
76
+ return `${available}/${total}`;
77
+ }
78
+ /**
79
+ * RAM segment for the agent block.
80
+ */
81
+ function formatRamForAgent(env) {
82
+ const total = env.ramTotalGb;
83
+ const available = env.ramAvailableGb;
84
+ if (total !== null && available !== null)
85
+ return `${available}/${total}GB available`;
86
+ if (total !== null)
87
+ return `${total}GB total`;
88
+ return `${available}GB available`;
89
+ }
90
+ /**
91
+ * Multi line description for the agent context.
92
+ */
93
+ export function formatEnvForAgentResult(env) {
94
+ let message = `[Workspace Environment]\n`;
95
+ message += `Mode: ${describeWorkspaceMode(env)}\n`;
96
+ if (env.evidence.length > 0) {
97
+ const sources = env.evidence.map((e) => `${e.source}:${e.value}`).join(", ");
98
+ message += `Evidence: ${sources}\n`;
99
+ }
100
+ message += `Dev script: ${env.hasDevScript ? "yes" : "no"}\n`;
101
+ message += `Build script: ${env.hasBuildScript ? "yes" : "no"}\n`;
102
+ if (env.devServerRunning) {
103
+ message += `Dev server: running\n`;
104
+ }
105
+ if (env.buildServerRunning) {
106
+ message += `Build server: running\n`;
107
+ }
108
+ if (env.ramTotalGb !== null || env.ramAvailableGb !== null) {
109
+ message += `RAM: ${formatRamForAgent(env)}\n`;
110
+ if (env.lowRamWarning) {
111
+ message += `WARNING: Low RAM - avoid parallel build + dev\n`;
112
+ }
113
+ }
114
+ message += `\n`;
115
+ message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
116
+ if (env.mode === "unknown") {
117
+ 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`;
118
+ }
119
+ return message;
120
+ }
@@ -9,38 +9,15 @@
9
9
  * Stores detected environment in SQLite for agent reference.
10
10
  * Uses synchronous directory creation and exception-safe operations.
11
11
  */
12
+ import { describeServerState, describeWorkspaceMode, formatEnvForAgentResult } from "./workspace-env-format.js";
13
+ import type { EnvEvidence, WorkspaceEnvResult, WorkspaceMode } from "./workspace-env-format.js";
12
14
  /**
13
- * Workspace mode with explicit states
15
+ * The mode/evidence types and every human readable rendering of the workspace
16
+ * environment live in workspace-env-format.ts. They are re-exported here so
17
+ * the existing import paths keep working.
14
18
  */
15
- export type WorkspaceMode = "development" | "production" | "test" | "unknown";
16
- /**
17
- * Evidence source for mode detection
18
- */
19
- export interface EnvEvidence {
20
- source: "process.env" | ".env file" | "package.json" | "command" | "detection";
21
- key: string;
22
- value: string;
23
- timestamp: number;
24
- }
25
- /**
26
- * Workspace environment result
27
- */
28
- export interface WorkspaceEnvResult {
29
- workspacePath: string;
30
- mode: WorkspaceMode;
31
- evidence: EnvEvidence[];
32
- hasDevScript: boolean;
33
- hasBuildScript: boolean;
34
- packageManager: string | null;
35
- devServerRunning: boolean;
36
- buildServerRunning: boolean;
37
- ramTotalGb: number | null;
38
- ramAvailableGb: number | null;
39
- ramEffectiveLimitGb: number | null;
40
- lowRamWarning: boolean;
41
- buildAllowed: boolean;
42
- detectedAt: number;
43
- }
19
+ export { describeServerState, describeWorkspaceMode, formatEnvForAgentResult, };
20
+ export type { EnvEvidence, WorkspaceEnvResult, WorkspaceMode };
44
21
  /**
45
22
  * Detect and store workspace environment
46
23
  */
@@ -13,6 +13,7 @@ import { DatabaseSync } from "node:sqlite";
13
13
  import { existsSync, readFileSync, mkdirSync } from "fs";
14
14
  import { join } from "path";
15
15
  import { platform, totalmem, freemem } from "os";
16
+ import { describeServerState, describeWorkspaceMode, formatEnvForAgentResult, formatEnvStatusLine as formatStatusLine, } from "./workspace-env-format.js";
16
17
  const STORE_DIR = join(process.env.HOME || "/home/frappe", ".pi-harness-runtime");
17
18
  /**
18
19
  * Ensure store directory exists synchronously
@@ -56,6 +57,12 @@ function initDatabase() {
56
57
  `);
57
58
  return db;
58
59
  }
60
+ /**
61
+ * The mode/evidence types and every human readable rendering of the workspace
62
+ * environment live in workspace-env-format.ts. They are re-exported here so
63
+ * the existing import paths keep working.
64
+ */
65
+ export { describeServerState, describeWorkspaceMode, formatEnvForAgentResult, };
59
66
  /**
60
67
  * Parse dotenv file without loading secrets
61
68
  * Returns only NODE_ENV value, ignoring comments and other vars
@@ -143,31 +150,22 @@ function determineMode(processEnv, dotenvNodeEnv, evidence) {
143
150
  // Explicit process.env takes highest priority (for build processes)
144
151
  if (processEnv) {
145
152
  const env = processEnv.toLowerCase();
153
+ // The evidence is recorded for EVERY value, including one that names
154
+ // no known mode ("staging"): the label then says which NODE_ENV could
155
+ // not be interpreted instead of claiming there is none.
156
+ evidence.push({
157
+ source: "process.env",
158
+ key: "NODE_ENV",
159
+ value: env,
160
+ timestamp: Date.now(),
161
+ });
146
162
  if (env === "production") {
147
- evidence.push({
148
- source: "process.env",
149
- key: "NODE_ENV",
150
- value: env,
151
- timestamp: Date.now(),
152
- });
153
163
  return "production";
154
164
  }
155
165
  if (env === "development" || env === "dev") {
156
- evidence.push({
157
- source: "process.env",
158
- key: "NODE_ENV",
159
- value: env,
160
- timestamp: Date.now(),
161
- });
162
166
  return "development";
163
167
  }
164
168
  if (env === "test") {
165
- evidence.push({
166
- source: "process.env",
167
- key: "NODE_ENV",
168
- value: env,
169
- timestamp: Date.now(),
170
- });
171
169
  return "test";
172
170
  }
173
171
  }
@@ -385,48 +383,11 @@ export function revalidateWorkspaceEnv(workspacePath) {
385
383
  * Format workspace env as agent-readable message
386
384
  */
387
385
  export function formatEnvForAgent(workspacePath) {
388
- const env = getWorkspaceEnv(workspacePath);
389
- let message = `[Workspace Environment]\n`;
390
- message += `Mode: ${env.mode.toUpperCase()}\n`;
391
- if (env.evidence.length > 0) {
392
- const sources = env.evidence.map((e) => `${e.source}:${e.value}`).join(", ");
393
- message += `Evidence: ${sources}\n`;
394
- }
395
- message += `Dev script: ${env.hasDevScript ? "yes" : "no"}\n`;
396
- message += `Build script: ${env.hasBuildScript ? "yes" : "no"}\n`;
397
- if (env.devServerRunning) {
398
- message += `Dev server: running\n`;
399
- }
400
- if (env.buildServerRunning) {
401
- message += `Build server: running\n`;
402
- }
403
- if (env.ramTotalGb !== null) {
404
- const ramStr = env.ramAvailableGb !== null
405
- ? `${env.ramAvailableGb}/${env.ramTotalGb}GB available`
406
- : `${env.ramTotalGb}GB total`;
407
- message += `RAM: ${ramStr}\n`;
408
- if (env.lowRamWarning) {
409
- message += `WARNING: Low RAM - avoid parallel build + dev\n`;
410
- }
411
- }
412
- message += `\n`;
413
- message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
414
- if (env.mode === "unknown") {
415
- message += `\nNOTE: Environment unknown. Explicitly ask user before build.\n`;
416
- }
417
- return message;
386
+ return formatEnvForAgentResult(getWorkspaceEnv(workspacePath));
418
387
  }
419
388
  /**
420
389
  * Format workspace env as compact status line for UI
421
390
  */
422
391
  export function formatEnvStatusLine(env) {
423
- const mode = env.mode.toUpperCase();
424
- const devStatus = env.devServerRunning ? "running" : (env.hasDevScript ? "available" : "none");
425
- const buildStatus = env.buildServerRunning ? "running" : (env.hasBuildScript ? "explicit request" : "none");
426
- const ramStr = env.ramAvailableGb !== null ? `${env.ramAvailableGb}/${env.ramTotalGb}` : "";
427
- let status = `Workspace: ${mode} | dev server: ${devStatus} | build: ${buildStatus}`;
428
- if (ramStr) {
429
- status += ` | RAM: ${ramStr}`;
430
- }
431
- return status;
392
+ return formatStatusLine(env);
432
393
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/contracts",
3
- "version": "1.1.387",
3
+ "version": "1.1.388",
4
4
  "type": "module",
5
5
  "description": "RPC schema definitions for pi-harness runtime",
6
6
  "main": "./dist/index.js",