pi-harness-runtime 1.1.390 → 1.1.391

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 (144) hide show
  1. package/AGENTS.md +12 -6
  2. package/harness/startup-report.ts +36 -6
  3. package/index.ts +22 -2
  4. package/package.json +1 -1
  5. package/packages/a2a-adapter/package.json +1 -1
  6. package/packages/agent-policy/package.json +1 -1
  7. package/packages/architecture-generator/package.json +1 -1
  8. package/packages/auth/package.json +1 -1
  9. package/packages/autonomous-refactor/package.json +1 -1
  10. package/packages/autonomous-runtime/package.json +1 -1
  11. package/packages/cache-strategy/package.json +1 -1
  12. package/packages/capability-registry/package.json +1 -1
  13. package/packages/checkpoint/package.json +1 -1
  14. package/packages/cli-plugin-sdk/package.json +1 -1
  15. package/packages/clipboard/package.json +1 -1
  16. package/packages/clipboard-plugin/package.json +1 -1
  17. package/packages/code-generation/package.json +1 -1
  18. package/packages/code-review/package.json +1 -1
  19. package/packages/codex-adapter/package.json +1 -1
  20. package/packages/codex-watcher/package.json +1 -1
  21. package/packages/config-capture/package.json +1 -1
  22. package/packages/connection-runtime/package.json +1 -1
  23. package/packages/context-compiler/package.json +1 -1
  24. package/packages/context-discovery/package.json +1 -1
  25. package/packages/context-manager/package.json +1 -1
  26. package/packages/contracts/dist/workspace-env-format.d.ts +56 -3
  27. package/packages/contracts/dist/workspace-env-format.js +119 -22
  28. package/packages/contracts/dist/workspace-env.d.ts +4 -4
  29. package/packages/contracts/dist/workspace-env.js +426 -23
  30. package/packages/contracts/package.json +1 -1
  31. package/packages/contracts/src/workspace-env-format.ts +147 -17
  32. package/packages/contracts/src/workspace-env.ts +443 -24
  33. package/packages/contracts/test/workspace-env.test.ts +95 -7
  34. package/packages/cookie-sanitizer/package.json +1 -1
  35. package/packages/cost-optimizer/package.json +1 -1
  36. package/packages/dependency-analyzer/package.json +1 -1
  37. package/packages/django-plugin/package.json +1 -1
  38. package/packages/doc-generator/package.json +1 -1
  39. package/packages/durable-review/package.json +1 -1
  40. package/packages/e2e-tester/package.json +1 -1
  41. package/packages/evaluation-engine/package.json +1 -1
  42. package/packages/evaluation-runner/package.json +1 -1
  43. package/packages/event-bus/package.json +1 -1
  44. package/packages/event-sink/package.json +1 -1
  45. package/packages/event-store/package.json +1 -1
  46. package/packages/experience-replay/package.json +1 -1
  47. package/packages/feedback-collector/package.json +1 -1
  48. package/packages/file-copy-helper/package.json +1 -1
  49. package/packages/framework-detector/package.json +1 -1
  50. package/packages/framework-plugin-sdk/package.json +1 -1
  51. package/packages/frappe-plugin/package.json +1 -1
  52. package/packages/generic-web-plugin/package.json +1 -1
  53. package/packages/health-monitor/package.json +1 -1
  54. package/packages/intent-analyzer/package.json +1 -1
  55. package/packages/jev-judge/package.json +1 -1
  56. package/packages/knowledge-graph/package.json +1 -1
  57. package/packages/knowledge-retrieval/package.json +1 -1
  58. package/packages/laravel-plugin/package.json +1 -1
  59. package/packages/learning-engine/package.json +1 -1
  60. package/packages/mcp-adapter/package.json +1 -1
  61. package/packages/memory-engine/package.json +1 -1
  62. package/packages/milestone-manager/package.json +1 -1
  63. package/packages/model-registry/package.json +1 -1
  64. package/packages/nextjs-plugin/package.json +1 -1
  65. package/packages/notification/dist/adapters/line-adapter.d.ts +39 -0
  66. package/packages/notification/dist/adapters/line-adapter.d.ts.map +1 -1
  67. package/packages/notification/dist/adapters/line-adapter.js +119 -8
  68. package/packages/notification/dist/adapters/line-adapter.js.map +1 -1
  69. package/packages/notification/dist/base-adapter.d.ts +20 -0
  70. package/packages/notification/dist/base-adapter.d.ts.map +1 -1
  71. package/packages/notification/dist/base-adapter.js +17 -0
  72. package/packages/notification/dist/base-adapter.js.map +1 -1
  73. package/packages/notification/dist/notification-center.d.ts +8 -0
  74. package/packages/notification/dist/notification-center.d.ts.map +1 -1
  75. package/packages/notification/dist/notification-center.js +11 -0
  76. package/packages/notification/dist/notification-center.js.map +1 -1
  77. package/packages/notification/package.json +1 -1
  78. package/packages/notification/src/adapters/line-adapter.ts +145 -13
  79. package/packages/notification/src/base-adapter.ts +28 -0
  80. package/packages/notification/src/notification-center.ts +12 -0
  81. package/packages/observability/package.json +1 -1
  82. package/packages/okf-indexer/package.json +1 -1
  83. package/packages/paperclip-integration/package.json +1 -1
  84. package/packages/performance-optimizer/package.json +1 -1
  85. package/packages/permission-mode/package.json +1 -1
  86. package/packages/privilege-broker/package.json +1 -1
  87. package/packages/project-analyzer/package.json +1 -1
  88. package/packages/project-bootstrap/package.json +1 -1
  89. package/packages/projection-engine/package.json +1 -1
  90. package/packages/prompt-compiler/package.json +1 -1
  91. package/packages/prompt-versioning/package.json +1 -1
  92. package/packages/provider-adapter-sdk/package.json +1 -1
  93. package/packages/provider-router/package.json +1 -1
  94. package/packages/provider-selector/package.json +1 -1
  95. package/packages/providers/package.json +1 -1
  96. package/packages/qdrant-skills/package.json +1 -1
  97. package/packages/quota-manager/package.json +1 -1
  98. package/packages/rate-limiter/package.json +1 -1
  99. package/packages/react-vite-plugin/package.json +1 -1
  100. package/packages/relay/package.json +1 -1
  101. package/packages/release-manager/package.json +1 -1
  102. package/packages/requirement-compiler/package.json +1 -1
  103. package/packages/runtime/package.json +1 -1
  104. package/packages/scheduler/package.json +1 -1
  105. package/packages/scheduler-adapter/package.json +1 -1
  106. package/packages/session/package.json +1 -1
  107. package/packages/session-api/package.json +1 -1
  108. package/packages/session-export/package.json +1 -1
  109. package/packages/shared-context/package.json +1 -1
  110. package/packages/skill-mcp-client/package.json +1 -1
  111. package/packages/skill-registry/package.json +1 -1
  112. package/packages/skills/package.json +1 -1
  113. package/packages/sprint-planner/package.json +1 -1
  114. package/packages/subscription-engine/package.json +1 -1
  115. package/packages/task-compiler/package.json +1 -1
  116. package/packages/telegram/package.json +1 -1
  117. package/packages/tencentdb-memory/package.json +1 -1
  118. package/packages/tencentdb-sync/package.json +1 -1
  119. package/packages/test-data-generator/package.json +1 -1
  120. package/packages/test-generator/package.json +1 -1
  121. package/packages/todo-bd-sync/package.json +1 -1
  122. package/packages/token-estimation/package.json +1 -1
  123. package/packages/token-optimizer/package.json +1 -1
  124. package/packages/trajectory/package.json +1 -1
  125. package/packages/tui/dist/harness-footer.d.ts +23 -8
  126. package/packages/tui/dist/harness-footer.d.ts.map +1 -1
  127. package/packages/tui/dist/harness-footer.js +144 -2
  128. package/packages/tui/dist/harness-footer.js.map +1 -1
  129. package/packages/tui/dist/index.d.ts +1 -1
  130. package/packages/tui/dist/index.d.ts.map +1 -1
  131. package/packages/tui/dist/index.js +1 -1
  132. package/packages/tui/dist/index.js.map +1 -1
  133. package/packages/tui/package.json +1 -1
  134. package/packages/tui/src/harness-footer.ts +149 -2
  135. package/packages/tui/src/index.ts +1 -0
  136. package/packages/tui/test/harness-footer.test.ts +134 -15
  137. package/packages/types/package.json +1 -1
  138. package/packages/voice-api/package.json +1 -1
  139. package/packages/webmcp-tester/package.json +1 -1
  140. package/packages/wiki/package.json +1 -1
  141. package/packages/workflow-events/package.json +1 -1
  142. package/packages/workspace-scanner/package.json +1 -1
  143. package/packages/worktree/package.json +1 -1
  144. package/packages/write-review/package.json +1 -1
package/AGENTS.md CHANGED
@@ -502,21 +502,27 @@ 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: DEVELOPMENT | dev server: running | build: not running (script available) | RAM: 10/16
505
+ Workspace: ~/pi-harness-runtime | OS: Linux | mode: DEVELOPMENT | dev server: running | dev script: available | build script: available | RAM: 10/16
506
506
  ```
507
507
 
508
508
  The mode words are exactly what the detector emits: `DEVELOPMENT`, `PRODUCTION`,
509
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.
510
+ A dev/build script is a CAPABILITY, not a state: `dev script: available` means
511
+ the script exists, not that a server runs. The server state is separate and
512
+ never claims more than it knows: `running`, `not detected` (a check ran and
513
+ found nothing) or `not checked` (no check was possible, e.g. no `pgrep`).
514
+
515
+ The OS is part of the line because it decides which command prints the
516
+ working directory: `pwd` on Linux/macOS, `cd` or `echo %cd%` on Windows.
517
+ The agent context names the command to use.
512
518
 
513
519
  **IMPORTANT Rules:**
514
520
 
515
521
  | State | Action |
516
522
  |-------|--------|
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 |
523
+ | `mode: UNKNOWN (…)` | Explicitly ask user before build |
524
+ | `mode: DEVELOPMENT` | Use `dev` mode, don't auto-build |
525
+ | `mode: PRODUCTION` | Build allowed, but confirm with user |
520
526
  | `Low RAM` | Never run parallel build + dev |
521
527
  | `Dev server running` | Reuse it, don't restart |
522
528
 
@@ -59,7 +59,10 @@ export function oneLineError(error: unknown, maxLength = 160): string {
59
59
  "$1<redacted>",
60
60
  )
61
61
  // A url path: "/bot123456:ABC-DEF/", "/token/abc12345".
62
- .replace(/\bbot\d{4,}:[A-Za-z0-9_\-]{4,}/g, "<redacted>")
62
+ // The boundary is "not a digit" and not a word boundary, so a
63
+ // token that is glued to a word ("xbot123:ABC-DEF") is redacted
64
+ // as well.
65
+ .replace(/(^|[^0-9])\d{6,}:[A-Za-z0-9_\-]{4,}/g, "$1<redacted>")
63
66
  .replace(
64
67
  /(\/((?:access_token|api[-_]?key|token|secret)[=/]))[A-Za-z0-9_\-:]{4,}/gi,
65
68
  "$1<redacted>",
@@ -177,6 +180,21 @@ export function lineConfigurationReport(options: {
177
180
  telegramConfigured?: boolean;
178
181
  /** The fix commands for the missing LINE credentials. */
179
182
  fixes: string[];
183
+ /**
184
+ * The fix commands for the channel access token only.
185
+ *
186
+ * The startup check validates the ACCESS TOKEN. The user id is only the
187
+ * recipient of the messages, so a failed check must not point at
188
+ * line-user-id.txt.
189
+ */
190
+ accessTokenFixes?: string[];
191
+ /**
192
+ * Why the channel did not start (from the adapter).
193
+ *
194
+ * It says WHICH problem it is - an invalid or revoked token, a LINE API
195
+ * failure or a network failure - and it never contains a credential.
196
+ */
197
+ channelDiagnostic?: string | null;
180
198
  }): StartupReport | null {
181
199
  if (!options.clientKeyFilePresent) return null;
182
200
  const channelState = options.channelState ?? "not-started";
@@ -204,15 +222,27 @@ export function lineConfigurationReport(options: {
204
222
  }
205
223
  if (channelState === "failed") {
206
224
  // The credentials are there but the channel did not start - the
207
- // credentials themselves are the likely cause.
225
+ // channel access token is what the startup check validates, so
226
+ // the user id must not be named as the cause.
227
+ const details = [
228
+ ...(options.accessTokenFixes && options.accessTokenFixes.length > 0
229
+ ? options.accessTokenFixes
230
+ : options.fixes),
231
+ ];
232
+ if (options.channelDiagnostic) {
233
+ details.push(options.channelDiagnostic);
234
+ }
235
+ details.push(
236
+ "The startup check validates the channel access token - the user id is only the recipient of the messages.",
237
+ );
238
+ details.push(
239
+ "Fix the file, then reload Pi so the channel is checked again.",
240
+ );
208
241
  return {
209
242
  label: "LINE",
210
243
  text: "configured but the LINE channel did not start",
211
244
  status: "warn",
212
- details: [
213
- ...options.fixes,
214
- "Check the LINE credentials in ~/.pi-harness-runtime/keys/, then reload Pi.",
215
- ],
245
+ details,
216
246
  };
217
247
  }
218
248
  // The credentials are there but the channel was never started in this
package/index.ts CHANGED
@@ -163,6 +163,15 @@ let keyFileAuditDone = false;
163
163
  * startup line must not claim a green "configured".
164
164
  */
165
165
  let lineChannelState: "started" | "failed" | "not-started" = "not-started";
166
+ /**
167
+ * Why the LINE channel did not start (from the adapter).
168
+ *
169
+ * The adapter knows the difference between an invalid or revoked token
170
+ * (401/403), a LINE API failure and a network failure - the startup
171
+ * summary shows it so the user knows what to fix. It never contains a
172
+ * credential.
173
+ */
174
+ let lineChannelDiagnostic: string | null = null;
166
175
  let telegramChannelState: "started" | "failed" | "not-started" = "not-started";
167
176
  /** Did the relay client connect at least once in this extension load? */
168
177
  let relayStreamConnected = false;
@@ -1398,9 +1407,13 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
1398
1407
  if (lineChannelConfigured) {
1399
1408
  if (center.isChannelHealthy("line")) {
1400
1409
  lineChannelState = "started";
1410
+ lineChannelDiagnostic = null;
1401
1411
  addHarnessMessage("LINE", "configured and enabled (tap Toggle LINE to disable)", "ok");
1402
1412
  } else {
1403
1413
  lineChannelState = "failed";
1414
+ // Keep the reason: "the token is wrong" and "LINE is down"
1415
+ // need different actions.
1416
+ lineChannelDiagnostic = center.getChannelDiagnostic("line");
1404
1417
  }
1405
1418
  }
1406
1419
  if (telegramChannelState !== "started") {
@@ -1795,8 +1808,15 @@ function reportLineConfiguration(): void {
1795
1808
  channelState: lineChannelState,
1796
1809
  telegramConfigured: telegramIsConfigured(),
1797
1810
  // The fallback makes sure that a warning always carries a command,
1798
- // even when the audit could not answer.
1799
- fixes: keyFileFixesOrFallback("LINE", ["channel access token", "user id"]),
1811
+ // even when the audit could not answer. It names the ACCESS TOKEN
1812
+ // only - the user id is the recipient of the messages, not the cause
1813
+ // of a failed startup check.
1814
+ fixes: keyFileFixesOrFallback("LINE", ["channel access token"]),
1815
+ // The startup check validates the access token - the user id is only
1816
+ // the recipient, so it must not be named as the cause.
1817
+ accessTokenFixes: keyFileFixesOrFallback("LINE", ["channel access token"]),
1818
+ // The reason the channel did not start (invalid token, LINE down, ...).
1819
+ channelDiagnostic: lineChannelDiagnostic,
1800
1820
  // Without Telegram the LINE channel cannot start - the user needs
1801
1821
  // the Telegram commands, not only an explanation.
1802
1822
  telegramFixes: keyFileFixesOrFallback("Telegram", ["bot token", "chat id"]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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.390",
3
+ "version": "1.1.391",
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",
@@ -37,6 +37,16 @@ export interface WorkspaceEnvResult {
37
37
  packageManager: string | null;
38
38
  devServerRunning: boolean;
39
39
  buildServerRunning: boolean;
40
+ /**
41
+ * The tri-state state of the dev server.
42
+ *
43
+ * "not detected" means a check ran and found nothing; "not checked"
44
+ * means no check was possible (an unsupported platform or a failed
45
+ * check). Neither of them is evidence that a server is not running.
46
+ */
47
+ devServerState?: ServerState;
48
+ /** The tri-state state of the build server. */
49
+ buildServerState?: ServerState;
40
50
  ramTotalGb: number | null;
41
51
  ramAvailableGb: number | null;
42
52
  ramEffectiveLimitGb: number | null;
@@ -44,6 +54,47 @@ export interface WorkspaceEnvResult {
44
54
  buildAllowed: boolean;
45
55
  detectedAt: number;
46
56
  }
57
+ /**
58
+ * The state of a dev/build server.
59
+ */
60
+ /**
61
+ * Is this command line a package-manager launcher?
62
+ *
63
+ * A launcher ("npm run dev", "bun run start") only STARTS a server - the
64
+ * server is its child. Counting the launcher would report a running
65
+ * server for a script that does not start one at all ("sleep 300").
66
+ *
67
+ * The rule is pure, so it lives in the pure module and can be tested
68
+ * without the SQLite cache.
69
+ */
70
+ export declare function isLauncherCommand(cmdline: string): boolean;
71
+ export type ServerState = "running" | "not-detected" | "not-checked";
72
+ /**
73
+ * The name of the operating system the harness runs on.
74
+ *
75
+ * The OS decides which command shows the working directory (pwd on
76
+ * POSIX, cd / echo %cd% on Windows), so the summary names it.
77
+ */
78
+ export declare function describeOs(platformName?: string): string;
79
+ /**
80
+ * The command that prints the working directory on this OS.
81
+ */
82
+ export declare function describeCwdCommand(platformName?: string): string;
83
+ /**
84
+ * Remove the pids of our own processes from a pgrep result.
85
+ *
86
+ * The shell that runs pgrep carries the search pattern in its own command
87
+ * line, so pgrep always reports it - a server would be claimed where none
88
+ * runs. The pids of our own processes are therefore dropped.
89
+ */
90
+ export declare function filterOwnPids(pids: string[], ownPids: string[]): string[];
91
+ /**
92
+ * Shorten the home directory to "~".
93
+ *
94
+ * The check is on a path boundary: "/home/frappe" is the home of
95
+ * "/home/frappe/app", but not of "/home/frappeX".
96
+ */
97
+ export declare function formatWorkspacePath(workspacePath: string, home?: string): string;
47
98
  /**
48
99
  * Human readable mode label.
49
100
  *
@@ -56,10 +107,12 @@ export declare function describeWorkspaceMode(env: WorkspaceEnvResult): string;
56
107
  /**
57
108
  * Describe a dev/build server as a state.
58
109
  *
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.
110
+ * "not detected" is what a check that found nothing says - it is NOT the
111
+ * same as "not running": on a platform where no check is possible the
112
+ * state is "not checked". Neither of them says anything about the
113
+ * workspace mode.
62
114
  */
115
+ export declare function describeServerState(state: ServerState): string;
63
116
  export declare function describeServerState(running: boolean, hasScript: boolean): string;
64
117
  /**
65
118
  * One line status for the footer / status bar.
@@ -12,6 +12,88 @@
12
12
  * "not running (script available)" so it cannot be mistaken for a running
13
13
  * server or for evidence about the workspace mode.
14
14
  */
15
+ /**
16
+ * The state of a dev/build server.
17
+ */
18
+ /**
19
+ * Is this command line a package-manager launcher?
20
+ *
21
+ * A launcher ("npm run dev", "bun run start") only STARTS a server - the
22
+ * server is its child. Counting the launcher would report a running
23
+ * server for a script that does not start one at all ("sleep 300").
24
+ *
25
+ * The rule is pure, so it lives in the pure module and can be tested
26
+ * without the SQLite cache.
27
+ */
28
+ export function isLauncherCommand(cmdline) {
29
+ return /^(npm|bun|yarn|pnpm)\b.*\b(run\s+)?(dev|start)\b/.test(cmdline.trim());
30
+ }
31
+ /**
32
+ * The name of the operating system the harness runs on.
33
+ *
34
+ * The OS decides which command shows the working directory (pwd on
35
+ * POSIX, cd / echo %cd% on Windows), so the summary names it.
36
+ */
37
+ export function describeOs(platformName) {
38
+ const name = platformName ?? process.platform;
39
+ switch (name) {
40
+ case "linux":
41
+ return "Linux";
42
+ case "darwin":
43
+ return "macOS";
44
+ case "win32":
45
+ return "Windows";
46
+ default:
47
+ return name;
48
+ }
49
+ }
50
+ /**
51
+ * The command that prints the working directory on this OS.
52
+ */
53
+ export function describeCwdCommand(platformName) {
54
+ switch (platformName ?? process.platform) {
55
+ case "win32":
56
+ return "cd or echo %cd% (CMD), or (Get-Location).Path (PowerShell)";
57
+ case "linux":
58
+ case "darwin":
59
+ return "pwd";
60
+ default:
61
+ return "pwd (POSIX shells)";
62
+ }
63
+ }
64
+ /**
65
+ * Remove the pids of our own processes from a pgrep result.
66
+ *
67
+ * The shell that runs pgrep carries the search pattern in its own command
68
+ * line, so pgrep always reports it - a server would be claimed where none
69
+ * runs. The pids of our own processes are therefore dropped.
70
+ */
71
+ export function filterOwnPids(pids, ownPids) {
72
+ const own = new Set(ownPids.map((pid) => pid.trim()));
73
+ return pids.map((pid) => pid.trim()).filter((pid) => pid.length > 0 && !own.has(pid));
74
+ }
75
+ /**
76
+ * Shorten the home directory to "~".
77
+ *
78
+ * The check is on a path boundary: "/home/frappe" is the home of
79
+ * "/home/frappe/app", but not of "/home/frappeX".
80
+ */
81
+ export function formatWorkspacePath(workspacePath, home) {
82
+ if (!workspacePath)
83
+ return "";
84
+ // A trailing separator is not part of the path.
85
+ const path = workspacePath.replace(/[\\/]+$/, "") || workspacePath;
86
+ const homeDir = (home ?? process.env["HOME"] ?? process.env["USERPROFILE"] ?? "").replace(/[\\/]+$/, "");
87
+ // A root home ("/") shortens nothing - every path would look like "~".
88
+ if (homeDir && homeDir !== "/" && path.startsWith(homeDir)) {
89
+ const rest = path.slice(homeDir.length);
90
+ if (rest === "")
91
+ return "~";
92
+ if (rest.startsWith("/") || rest.startsWith("\\"))
93
+ return `~${rest}`;
94
+ }
95
+ return path;
96
+ }
15
97
  /**
16
98
  * Human readable mode label.
17
99
  *
@@ -30,28 +112,42 @@ export function describeWorkspaceMode(env) {
30
112
  }
31
113
  return "UNKNOWN (no explicit NODE_ENV)";
32
114
  }
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";
115
+ export function describeServerState(stateOrRunning, hasScript) {
116
+ // The legacy signature (a boolean plus the script flag) is kept so that
117
+ // published consumers keep working.
118
+ if (typeof stateOrRunning === "boolean") {
119
+ if (stateOrRunning)
120
+ return "running";
121
+ return hasScript ? "not running (script available)" : "no script";
122
+ }
123
+ switch (stateOrRunning) {
124
+ case "running":
125
+ return "running";
126
+ case "not-detected":
127
+ return "not detected";
128
+ case "not-checked":
129
+ return "not checked";
130
+ default:
131
+ // An unknown state is never reported as "not running".
132
+ return "not checked";
133
+ }
46
134
  }
47
135
  /**
48
136
  * One line status for the footer / status bar.
49
137
  */
50
138
  export function formatEnvStatusLine(env) {
51
139
  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}`;
140
+ // Without a stored state the honest answer is "not checked" - a legacy
141
+ // row that says "false" cannot tell a missing server from a platform
142
+ // where no check was possible.
143
+ const devState = env.devServerState ?? (env.devServerRunning ? "running" : "not-checked");
144
+ const buildState = env.buildServerState ?? (env.buildServerRunning ? "running" : "not-checked");
145
+ const path = formatWorkspacePath(env.workspacePath);
146
+ let status = `Workspace: ${path} | OS: ${describeOs()} | mode: ${mode}` +
147
+ ` | dev server: ${describeServerState(devState)}` +
148
+ ` | dev script: ${env.hasDevScript ? "available" : "unavailable"}` +
149
+ ` | build server: ${describeServerState(buildState)}` +
150
+ ` | build script: ${env.hasBuildScript ? "available" : "unavailable"}`;
55
151
  const ramStr = formatRam(env);
56
152
  if (ramStr) {
57
153
  status += ` | RAM: ${ramStr}`;
@@ -93,18 +189,19 @@ function formatRamForAgent(env) {
93
189
  export function formatEnvForAgentResult(env) {
94
190
  let message = `[Workspace Environment]\n`;
95
191
  message += `Mode: ${describeWorkspaceMode(env)}\n`;
192
+ message += `OS: ${describeOs()}\n`;
193
+ message += `Workspace path: ${formatWorkspacePath(env.workspacePath)}\n`;
194
+ message += `The workspace path was captured from the process working directory - to verify it in a shell: ${describeCwdCommand()}\n`;
96
195
  if (env.evidence.length > 0) {
97
196
  const sources = env.evidence.map((e) => `${e.source}:${e.value}`).join(", ");
98
197
  message += `Evidence: ${sources}\n`;
99
198
  }
100
199
  message += `Dev script: ${env.hasDevScript ? "yes" : "no"}\n`;
101
200
  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
- }
201
+ const devState = env.devServerState ?? (env.devServerRunning ? "running" : "not-checked");
202
+ const buildState = env.buildServerState ?? (env.buildServerRunning ? "running" : "not-checked");
203
+ message += `Dev server: ${describeServerState(devState)}\n`;
204
+ message += `Build server: ${describeServerState(buildState)}\n`;
108
205
  if (env.ramTotalGb !== null || env.ramAvailableGb !== null) {
109
206
  message += `RAM: ${formatRamForAgent(env)}\n`;
110
207
  if (env.lowRamWarning) {
@@ -9,15 +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
+ import { describeCwdCommand, describeOs, describeServerState, describeWorkspaceMode, formatEnvForAgentResult, formatWorkspacePath } from "./workspace-env-format.js";
13
+ import type { EnvEvidence, ServerState, WorkspaceEnvResult, WorkspaceMode } from "./workspace-env-format.js";
14
14
  /**
15
15
  * The mode/evidence types and every human readable rendering of the workspace
16
16
  * environment live in workspace-env-format.ts. They are re-exported here so
17
17
  * the existing import paths keep working.
18
18
  */
19
- export { describeServerState, describeWorkspaceMode, formatEnvForAgentResult, };
20
- export type { EnvEvidence, WorkspaceEnvResult, WorkspaceMode };
19
+ export { describeCwdCommand, describeOs, describeServerState, describeWorkspaceMode, formatEnvForAgentResult, formatWorkspacePath, };
20
+ export type { EnvEvidence, ServerState, WorkspaceEnvResult, WorkspaceMode };
21
21
  /**
22
22
  * Detect and store workspace environment
23
23
  */