pi-harness-runtime 1.1.49 → 1.1.71-3

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 (203) hide show
  1. package/CHANGELOG.md +30 -9
  2. package/README.md +119 -0
  3. package/harness/langchain/agents.d.ts +143 -0
  4. package/harness/langchain/agents.test.d.ts +2 -0
  5. package/harness/langchain/agents.test.ts +0 -1
  6. package/harness/langchain/agents.ts +196 -13
  7. package/harness/langchain/checkpointer.d.ts +48 -0
  8. package/harness/langchain/checkpointer.test.d.ts +8 -0
  9. package/harness/langchain/cron-cli.d.ts +12 -0
  10. package/harness/langchain/cron-cli.ts +169 -0
  11. package/harness/langchain/daemon.d.ts +161 -0
  12. package/harness/langchain/daemon.test.d.ts +11 -0
  13. package/harness/langchain/daemon.test.ts +62 -5
  14. package/harness/langchain/daemon.ts +231 -12
  15. package/harness/langchain/file-tools.d.ts +97 -0
  16. package/harness/langchain/file-tools.ts +29 -11
  17. package/harness/langchain/graph.d.ts +706 -0
  18. package/harness/langchain/graph.ts +184 -26
  19. package/harness/langchain/model-router.d.ts +63 -0
  20. package/harness/langchain/model-router.test.d.ts +2 -0
  21. package/harness/langchain/model-router.test.ts +191 -0
  22. package/harness/langchain/model-router.ts +188 -0
  23. package/harness/langchain/models.d.ts +72 -0
  24. package/harness/langchain/models.ts +111 -49
  25. package/harness/langchain/ping-pong-middleware.d.ts +28 -0
  26. package/harness/langchain/ping-pong-middleware.test.d.ts +2 -0
  27. package/harness/langchain/ping-pong-middleware.test.ts +69 -0
  28. package/harness/langchain/ping-pong-middleware.ts +204 -0
  29. package/harness/langchain/run.d.ts +15 -0
  30. package/harness/langchain/run.ts +15 -3
  31. package/harness/langchain/scoreboard.test.d.ts +13 -0
  32. package/harness/langchain/scoreboard.test.ts +28 -5
  33. package/harness/langchain/status-line.d.ts +91 -0
  34. package/harness/langchain/status-line.test.d.ts +8 -0
  35. package/harness/langchain/surge.d.ts +103 -0
  36. package/harness/langchain/surge.test.d.ts +10 -0
  37. package/harness/langchain/surge.test.ts +179 -0
  38. package/harness/langchain/surge.ts +188 -19
  39. package/harness/langchain/widget.d.ts +105 -0
  40. package/harness/langchain/widget.test.d.ts +8 -0
  41. package/harness/ssh-detach-interceptor.test.ts +263 -0
  42. package/harness/ssh-detach-interceptor.ts +438 -0
  43. package/index.ts +212 -47
  44. package/package.json +4 -2
  45. package/packages/a2a-adapter/package.json +1 -1
  46. package/packages/architecture-generator/package.json +1 -1
  47. package/packages/auth/package.json +1 -1
  48. package/packages/autonomous-refactor/package.json +1 -1
  49. package/packages/autonomous-runtime/package.json +1 -1
  50. package/packages/cache-strategy/package.json +1 -1
  51. package/packages/capability-registry/package.json +1 -1
  52. package/packages/checkpoint/package.json +1 -1
  53. package/packages/cli-plugin-sdk/package.json +1 -1
  54. package/packages/clipboard/package.json +1 -1
  55. package/packages/clipboard-plugin/package.json +1 -1
  56. package/packages/code-generation/package.json +1 -1
  57. package/packages/code-review/package.json +1 -1
  58. package/packages/codex-adapter/package.json +1 -1
  59. package/packages/config-capture/package.json +1 -1
  60. package/packages/context-compiler/package.json +1 -1
  61. package/packages/context-discovery/package.json +1 -1
  62. package/packages/context-manager/package.json +1 -1
  63. package/packages/cookie-sanitizer/package.json +1 -1
  64. package/packages/cost-optimizer/package.json +1 -1
  65. package/packages/dependency-analyzer/package.json +1 -1
  66. package/packages/django-plugin/package.json +1 -1
  67. package/packages/doc-generator/package.json +1 -1
  68. package/packages/evaluation-engine/package.json +1 -1
  69. package/packages/evaluation-runner/package.json +1 -1
  70. package/packages/event-bus/package.json +1 -1
  71. package/packages/event-store/package.json +1 -1
  72. package/packages/experience-replay/package.json +1 -1
  73. package/packages/feedback-collector/package.json +1 -1
  74. package/packages/file-copy-helper/package.json +1 -1
  75. package/packages/framework-detector/package.json +1 -1
  76. package/packages/framework-plugin-sdk/package.json +1 -1
  77. package/packages/frappe-plugin/package.json +1 -1
  78. package/packages/generic-web-plugin/package.json +1 -1
  79. package/packages/health-monitor/package.json +1 -1
  80. package/packages/intent-analyzer/dist/src/index.d.ts +2 -0
  81. package/packages/intent-analyzer/dist/src/index.d.ts.map +1 -1
  82. package/packages/intent-analyzer/dist/src/index.js +1 -0
  83. package/packages/intent-analyzer/dist/src/index.js.map +1 -1
  84. package/packages/intent-analyzer/dist/src/ping-pong.d.ts +56 -0
  85. package/packages/intent-analyzer/dist/src/ping-pong.d.ts.map +1 -0
  86. package/packages/intent-analyzer/dist/src/ping-pong.js +286 -0
  87. package/packages/intent-analyzer/dist/src/ping-pong.js.map +1 -0
  88. package/packages/intent-analyzer/package.json +1 -1
  89. package/packages/intent-analyzer/src/index.ts +13 -0
  90. package/packages/intent-analyzer/src/ping-pong.ts +387 -0
  91. package/packages/intent-analyzer/test/ping-pong.test.ts +96 -0
  92. package/packages/jev-judge/README.md +234 -0
  93. package/packages/jev-judge/dist/decision-engine.d.ts +100 -0
  94. package/packages/jev-judge/dist/decision-engine.d.ts.map +1 -0
  95. package/packages/jev-judge/dist/decision-engine.js +151 -0
  96. package/packages/jev-judge/dist/decision-engine.js.map +1 -0
  97. package/packages/jev-judge/dist/e2e-judge.d.ts +61 -0
  98. package/packages/jev-judge/dist/e2e-judge.d.ts.map +1 -0
  99. package/packages/jev-judge/dist/e2e-judge.js +268 -0
  100. package/packages/jev-judge/dist/e2e-judge.js.map +1 -0
  101. package/packages/jev-judge/dist/flaky-detector.d.ts +34 -0
  102. package/packages/jev-judge/dist/flaky-detector.d.ts.map +1 -0
  103. package/packages/jev-judge/dist/flaky-detector.js +154 -0
  104. package/packages/jev-judge/dist/flaky-detector.js.map +1 -0
  105. package/packages/jev-judge/dist/index.d.ts +77 -0
  106. package/packages/jev-judge/dist/index.d.ts.map +1 -0
  107. package/packages/jev-judge/dist/index.js +271 -0
  108. package/packages/jev-judge/dist/index.js.map +1 -0
  109. package/packages/jev-judge/dist/types.d.ts +97 -0
  110. package/packages/jev-judge/dist/types.d.ts.map +1 -0
  111. package/packages/jev-judge/dist/types.js +8 -0
  112. package/packages/jev-judge/dist/types.js.map +1 -0
  113. package/packages/jev-judge/package.json +46 -0
  114. package/packages/jev-judge/src/decision-engine.ts +231 -0
  115. package/packages/jev-judge/src/e2e-judge.ts +364 -0
  116. package/packages/jev-judge/src/flaky-detector.ts +190 -0
  117. package/packages/jev-judge/src/index.ts +345 -0
  118. package/packages/jev-judge/src/types.ts +131 -0
  119. package/packages/knowledge-graph/package.json +1 -1
  120. package/packages/knowledge-retrieval/package.json +1 -1
  121. package/packages/laravel-plugin/package.json +1 -1
  122. package/packages/learning-engine/package.json +1 -1
  123. package/packages/mcp-adapter/package.json +1 -1
  124. package/packages/memory-engine/package.json +1 -1
  125. package/packages/milestone-manager/package.json +1 -1
  126. package/packages/model-registry/package.json +1 -1
  127. package/packages/nextjs-plugin/package.json +1 -1
  128. package/packages/notification/package.json +1 -1
  129. package/packages/observability/package.json +1 -1
  130. package/packages/okf-indexer/package.json +1 -1
  131. package/packages/performance-optimizer/package.json +1 -1
  132. package/packages/privilege-broker/package.json +1 -1
  133. package/packages/project-analyzer/package.json +1 -1
  134. package/packages/project-bootstrap/package.json +1 -1
  135. package/packages/projection-engine/package.json +1 -1
  136. package/packages/prompt-compiler/package.json +1 -1
  137. package/packages/prompt-versioning/package.json +1 -1
  138. package/packages/provider-adapter-sdk/package.json +1 -1
  139. package/packages/provider-router/package.json +1 -1
  140. package/packages/provider-selector/package.json +1 -1
  141. package/packages/providers/package.json +1 -1
  142. package/packages/quota-manager/package.json +1 -1
  143. package/packages/rate-limiter/package.json +1 -1
  144. package/packages/react-vite-plugin/package.json +1 -1
  145. package/packages/release-manager/package.json +1 -1
  146. package/packages/requirement-compiler/package.json +1 -1
  147. package/packages/runtime/package.json +1 -1
  148. package/packages/scheduler/package.json +1 -1
  149. package/packages/scheduler-adapter/package.json +1 -1
  150. package/packages/session/package.json +1 -1
  151. package/packages/session-api/package.json +1 -1
  152. package/packages/session-export/package.json +1 -1
  153. package/packages/shared-context/package.json +1 -1
  154. package/packages/skill-mcp-client/package.json +1 -1
  155. package/packages/skill-registry/package.json +1 -1
  156. package/packages/sprint-planner/package.json +1 -1
  157. package/packages/subscription-engine/package.json +1 -1
  158. package/packages/task-compiler/package.json +1 -1
  159. package/packages/tencentdb-memory/package.json +1 -1
  160. package/packages/tencentdb-sync/package.json +1 -1
  161. package/packages/test-data-generator/package.json +1 -1
  162. package/packages/test-generator/package.json +1 -1
  163. package/packages/todo-bd-sync/README.md +71 -0
  164. package/packages/todo-bd-sync/package.json +1 -1
  165. package/packages/todo-bd-sync/src/extension.ts +16 -1
  166. package/packages/token-estimation/package.json +1 -1
  167. package/packages/token-optimizer/package.json +1 -1
  168. package/packages/trajectory/dist/src/approved-patterns.d.ts +51 -0
  169. package/packages/trajectory/dist/src/approved-patterns.d.ts.map +1 -0
  170. package/packages/trajectory/dist/src/approved-patterns.js +134 -0
  171. package/packages/trajectory/dist/src/approved-patterns.js.map +1 -0
  172. package/packages/trajectory/dist/src/index.d.ts +13 -0
  173. package/packages/trajectory/dist/src/index.d.ts.map +1 -0
  174. package/packages/trajectory/dist/src/index.js +12 -0
  175. package/packages/trajectory/dist/src/index.js.map +1 -0
  176. package/packages/trajectory/dist/src/store.d.ts +69 -0
  177. package/packages/trajectory/dist/src/store.d.ts.map +1 -0
  178. package/packages/trajectory/dist/src/store.js +253 -0
  179. package/packages/trajectory/dist/src/store.js.map +1 -0
  180. package/packages/trajectory/dist/src/types.d.ts +73 -0
  181. package/packages/trajectory/dist/src/types.d.ts.map +1 -0
  182. package/packages/trajectory/dist/src/types.js +9 -0
  183. package/packages/trajectory/dist/src/types.js.map +1 -0
  184. package/packages/trajectory/dist/test/store.test.d.ts +2 -0
  185. package/packages/trajectory/dist/test/store.test.d.ts.map +1 -0
  186. package/packages/trajectory/dist/test/store.test.js +228 -0
  187. package/packages/trajectory/dist/test/store.test.js.map +1 -0
  188. package/packages/trajectory/package.json +29 -0
  189. package/packages/trajectory/src/approved-patterns.ts +180 -0
  190. package/packages/trajectory/src/index.ts +26 -0
  191. package/packages/trajectory/src/store.ts +285 -0
  192. package/packages/trajectory/src/types.ts +84 -0
  193. package/packages/trajectory/test/store.test.d.ts +2 -0
  194. package/packages/trajectory/test/store.test.ts +250 -0
  195. package/packages/tui/package.json +1 -1
  196. package/packages/types/package.json +1 -1
  197. package/packages/workflow-events/package.json +1 -1
  198. package/packages/workspace-scanner/package.json +1 -1
  199. package/packages/worktree/package.json +1 -1
  200. package/packages/write-review/package.json +1 -1
  201. package/packages/write-review/src/blackboard.ts +38 -5
  202. package/packages/write-review/src/types.ts +13 -0
  203. package/skills/harness-runtime/SKILL.md +67 -4
package/index.ts CHANGED
@@ -13,7 +13,6 @@
13
13
  * Runs directly from Bun.
14
14
  */
15
15
 
16
- import { Key } from "@earendil-works/pi-tui";
17
16
  import type {
18
17
  CompactOptions,
19
18
  ExtensionAPI,
@@ -24,8 +23,6 @@ import { UsageTracker } from "./tracker.ts";
24
23
  import { MirrorStore, type MirrorRecord } from "./mirror.ts";
25
24
  import { MiniMaxQuotaScraper } from "./harness/e2e/minimax-quota-scraper.js";
26
25
  import { OpenAIQuotaScraper } from "./harness/e2e/openai-quota-scraper.js";
27
- import { GLMQuotaScraper } from "./harness/e2e/glm-quota-scraper.js";
28
- import { ChatGPTQuotaScraper } from "./harness/e2e/chatgpt-quota-scraper.js";
29
26
  import { parseMiniMaxQuotaText } from "./harness/e2e/minimax-quota-parser.js";
30
27
  import {
31
28
  CookieWatcher,
@@ -43,7 +40,6 @@ import {
43
40
  import { QuotaManager } from "./packages/quota-manager/src/quota-manager.ts";
44
41
  import { buildFooterStatusValue } from "./footer-status.ts";
45
42
  import { registerGithubLoginCommand } from "./packages/clipboard/src/github-login.js";
46
- import { registerCopySyncShortcut } from "./packages/clipboard/src/copy-sync.js";
47
43
  import {
48
44
  MAX_PROACTIVE_COMPACT_FAILURES,
49
45
  OUTPUT_LIMIT_RESUME_PROMPT,
@@ -116,37 +112,152 @@ async function initFileCopyHelper(pi: ExtensionAPI): Promise<void> {
116
112
  // file-copy-helper not available
117
113
  }
118
114
  }
119
- import { homedir } from "node:os";
120
- import { appendFileSync, existsSync, mkdirSync } from "node:fs";
121
- import { join } from "node:path";
122
115
 
123
- // --- Debug log → file instead of TUI ---------------------------------
124
- const DEBUG_LOG_DIR = join(homedir(), ".pi", "harness-logs");
125
- const DEBUG_LOG_PATH = join(DEBUG_LOG_DIR, "harness-debug.log");
116
+ // --- SSH detach interceptor: Auto-transform bare ssh ... & to detached pattern ---
117
+ // Prevents 2000+ second hangs when SSH background commands aren't detached.
118
+ // Intercepts every bash tool call before execution and rewrites risky SSH commands.
119
+ //
120
+ // Also guards foreground `ssh ... "<daemon>"` commands (pollers, servers) by
121
+ // prefixing `timeout Ns` so the local call always returns (PI_HARNESS_SSH_TIMEOUT_S).
122
+ async function initSshDetachInterceptor(pi: ExtensionAPI): Promise<void> {
123
+ try {
124
+ const interceptor = await import("./harness/ssh-detach-interceptor.js");
125
+ // Register on tool_call with default priority (100)
126
+ // NOTE: module functions are captured in closure scope — do NOT use require()
127
+ // here; this package is ESM ("type": "module") and require is undefined.
128
+ pi.on("tool_call", (event, _api) => {
129
+ if (event.toolName !== "bash") return {};
130
+ const input = event.input as { command?: string; timeout?: number };
131
+ const command = input.command ?? "";
132
+ if (!interceptor.isSshCommand(command)) return {};
133
+
134
+ // 1) Detach transform: bare `ssh ... &` or `ssh "a && b &"` chains
135
+ if (
136
+ !interceptor.isDetachedPattern(command) &&
137
+ (interceptor.hasBareAmpersand(command) ||
138
+ interceptor.hasCommandChainWithAmpersand(command))
139
+ ) {
140
+ const transformed = interceptor.transformToDetached(command);
141
+ if (transformed !== command) {
142
+ input.command = transformed;
143
+ return {};
144
+ }
145
+ }
126
146
 
127
- try {
128
- if (!existsSync(DEBUG_LOG_DIR)) mkdirSync(DEBUG_LOG_DIR, { recursive: true });
129
- } catch {
130
- /* non-fatal */
147
+ // 2) Timeout guard: foreground SSH running a daemon-ish remote command
148
+ // (e.g. `ssh -tt host "python -m amos_worker --video | head -10"`)
149
+ if (interceptor.isForegroundDaemonSsh(command)) {
150
+ const seconds = interceptor.getDefaultSshTimeoutSeconds();
151
+ input.command = interceptor.addSshTimeoutGuard(command, seconds);
152
+ // Also cap the bash tool's own timeout as belt-and-suspenders
153
+ if (input.timeout === undefined || input.timeout > seconds) {
154
+ input.timeout = seconds;
155
+ }
156
+ }
157
+ return {};
158
+ });
159
+ } catch {
160
+ // ssh-detach-interceptor not available
161
+ }
131
162
  }
132
163
 
133
- // Write harness runtime logs to file only (NOT to TUI stdout)
134
- function _debugLog(...args: unknown[]): void {
164
+ // --- loop-completions: Watch daemon loop completions → update TUI todos ---------
165
+ // When the daemon loop finishes, it writes a completion event to
166
+ // ~/.pi-harness-runtime/loop-completions/. The harness extension watches this
167
+ // dir and sends a steer message to the agent so the TUI todo count updates.
168
+ async function initLoopCompletions(pi: ExtensionAPI): Promise<void> {
169
+ const { existsSync, mkdirSync, watch } = await import("node:fs");
170
+ const { join: joinPath } = await import("node:path");
171
+ const { homedir: getHomeDir } = await import("node:os");
172
+
173
+ const COMPLETION_DIR = joinPath(
174
+ getHomeDir(),
175
+ ".pi-harness-runtime",
176
+ "loop-completions",
177
+ );
178
+
135
179
  try {
136
- const line =
137
- new Date().toISOString() +
138
- " " +
139
- args
140
- .map((a) => (typeof a === "object" ? JSON.stringify(a) : String(a)))
141
- .join(" ");
142
- appendFileSync(DEBUG_LOG_PATH, line + "\n");
180
+ if (!existsSync(COMPLETION_DIR)) {
181
+ mkdirSync(COMPLETION_DIR, { recursive: true });
182
+ }
143
183
  } catch {
144
- // non-fatal
184
+ return; // can't create dir — skip
185
+ }
186
+
187
+ // Process any pre-existing files (e.g. from a previous session)
188
+ try {
189
+ const { readdirSync } = await import("node:fs");
190
+ const files = readdirSync(COMPLETION_DIR).filter((f) => f.endsWith(".json"));
191
+ for (const file of files) {
192
+ processCompletionFile(joinPath(COMPLETION_DIR, file), pi);
193
+ }
194
+ } catch {
195
+ // ignore — best-effort
196
+ }
197
+
198
+ // Watch for new completion files
199
+ try {
200
+ const watcher = watch(
201
+ COMPLETION_DIR,
202
+ { persistent: false },
203
+ (event, filename) => {
204
+ if (event !== "rename") return;
205
+ if (!filename || !filename.endsWith(".json")) return;
206
+ const filePath = joinPath(COMPLETION_DIR, filename);
207
+ // Delay so the write finishes before we read
208
+ setTimeout(() => processCompletionFile(filePath, pi), 500);
209
+ },
210
+ );
211
+ watcher.on("error", () => {});
212
+ } catch {
213
+ // fs.watch not available — skip
145
214
  }
146
215
  }
147
216
 
217
+ interface LoopCompletion {
218
+ taskId: string;
219
+ request: string;
220
+ verdict: string;
221
+ iterations: number;
222
+ finishedAt: string;
223
+ }
224
+
225
+ function processCompletionFile(filePath: string, pi: ExtensionAPI): void {
226
+ const { existsSync, readFileSync, unlinkSync } = require("node:fs");
227
+ try {
228
+ if (!existsSync(filePath)) return;
229
+ const raw = readFileSync(filePath, "utf8");
230
+ const completion = JSON.parse(raw) as LoopCompletion;
231
+
232
+ const tag =
233
+ completion.verdict === "approved"
234
+ ? "[ok]"
235
+ : completion.verdict === "blocked"
236
+ ? "[blocked]"
237
+ : "[done]";
238
+
239
+ const message =
240
+ `Loop completed ${tag}: ${completion.taskId}\n` +
241
+ ` verdict: ${completion.verdict}\n` +
242
+ ` iterations: ${completion.iterations}\n` +
243
+ ` task: ${completion.request.slice(0, 80)}${completion.request.length > 80 ? "..." : ""}\n\n` +
244
+ `Run \`bd close ${completion.taskId} --reason "${completion.verdict}"\` if not already closed.`;
245
+
246
+ try {
247
+ pi.sendUserMessage(message, { deliverAs: "steer" });
248
+ } catch {
249
+ // ignore — best-effort
250
+ }
251
+ unlinkSync(filePath);
252
+ } catch {
253
+ // ignore — best-effort
254
+ }
255
+ }
148
256
  // --- Debug logging (file only, no console override) ---------------
149
257
  // Logs written to file only. Real console output preserved for pi's TUI.
258
+ import { homedir } from "node:os";
259
+ import { existsSync, mkdirSync } from "node:fs";
260
+ import { join } from "node:path";
150
261
 
151
262
  // --- Harness Runtime State --------------------------------------------
152
263
  const HARNESS_ROOT_DIR = join(homedir(), ".pi", "harness");
@@ -210,6 +321,7 @@ async function getCheckpointManager(): Promise<CheckpointManager> {
210
321
  const { JsonCheckpointManager } = await import(
211
322
  "./packages/checkpoint/src/checkpoint-manager.ts"
212
323
  );
324
+ // SAFETY: JsonCheckpointManager implements CheckpointManager via structural typing
213
325
  return new JsonCheckpointManager(
214
326
  HARNESS_ROOT_DIR,
215
327
  ) as unknown as CheckpointManager;
@@ -260,14 +372,39 @@ export default function (pi: ExtensionAPI) {
260
372
  // --- file-copy-helper: Inject cp rule when mimicking files -------------
261
373
  void initFileCopyHelper(pi);
262
374
 
375
+ // --- ssh-detach-interceptor: Auto-fix bare ssh & → nohup pattern ---------
376
+ void initSshDetachInterceptor(pi);
377
+
378
+ // --- loop-completions: Watch daemon completions → agent TUI todos --------
379
+ void initLoopCompletions(pi);
380
+
381
+ // --- TASK TRACKING TERMINOLOGY CLARIFICATION ------------------------
382
+ // CRITICAL: This project has TWO separate task trackers. Agents often confuse them.
383
+ const TASK_TERMINOLOGY_CLARIFICATION = `
384
+ ## TASK TRACKING TERMINOLOGY (CRITICAL)
385
+
386
+ This project uses TWO different task tracking systems. **Always clarify which one the user means:**
387
+
388
+ | What you say | Tool | Purpose |
389
+ |-------------|------|---------|
390
+ | **bd** / **beads** / **issues** | \`bd create\`, \`bd list\`, \`bd close\` | Issue tracker (GitHub-synced) |
391
+ | **todo** / **todos** | \`todo\` tool (JSON-RPC) | Local task list (session-only) |
392
+
393
+ **When in doubt, ASK:** "Did you mean **bd** (issue tracker) or **todo** (local task list)?"
394
+
395
+ ### When to use each:
396
+ - **bd (beads)**: Long-lived issues, bugs, features, GitHub issues
397
+ - **todo**: Short-lived session tasks, immediate action items
398
+ `;
399
+
263
400
  // --- Auto-Invoke rpiv-todo via System Prompt ------------------------
264
401
  // This makes the todo overlay ALWAYS activate at session start
265
402
  const AUTO_TODO_INVOKE_HINT = `
266
403
 
267
- You have access to a todo tool that shows a persistent task overlay.
404
+ You have access to a **todo tool** that shows a persistent task overlay.
268
405
  ALWAYS use it at the START of every session to capture user requirements as tasks.
269
406
  When the user gives you a multi-step task:
270
- 1. Immediately create todo items for each step using the todo tool
407
+ 1. Immediately create todo items for each step using the **todo tool** (not bd!)
271
408
  2. Keep tasks updated - mark in_progress when working, completed when done
272
409
  3. When a task is completed, continue to the next or ask the user
273
410
 
@@ -277,10 +414,12 @@ The todo overlay persists and helps track progress across your conversation.
277
414
  const COMMIT_BUILD_CHECKLIST = `
278
415
 
279
416
  BEFORE committing code or triggering builds, ALWAYS check:
280
- 1. Run 'bd ready' to see pending tasks
281
- 2. Mark completed tasks with 'bd close <id> --reason "Done"'
282
- 3. Acknowledge pending tasks before proceeding
283
- 4. Never commit/build without acknowledging pending todos
417
+ 1. Run \`bd ready\` to see pending **bd issues**
418
+ 2. Mark completed issues with \`bd close <id> --reason "Done"\`
419
+ 3. Acknowledge pending issues before proceeding
420
+ 4. Never commit/build without acknowledging pending bd issues
421
+
422
+ NOTE: Use \`todo\` tool for session task tracking, \`bd\` for issue tracker.
284
423
  `;
285
424
 
286
425
  const WRITE_REVIEW_HINT = `
@@ -289,10 +428,11 @@ WRITER-REVIEWER WORKFLOW:
289
428
  When working on a feature from {project}/wiki/* or {project}/.write-review/:
290
429
  1. Read the prompt/task file to understand requirements
291
430
  2. Write clean, complete code
292
- 3. Mark task "in_progress" in todos
293
- 4. When code is ready, trigger review with \`{done} bd create "Review: <task>" -t review -p 1 && bd close <id> --reason "Approved"\`
294
- 5. If reviewer requests changes, update code and re-review
295
- 6. Never skip review on non-trivial features
431
+ 3. Mark task "in_progress" using **todo tool**
432
+ 4. When code is ready, trigger review with \`bd create "Review: <task>" -t review -p 1\`
433
+ 5. After review, close the issue: \`bd close <id> --reason "Approved"\`
434
+ 6. If reviewer requests changes, update code and re-review
435
+ 7. Never skip review on non-trivial features
296
436
  `;
297
437
 
298
438
  const DOCKER_CLEANUP_HINT = `
@@ -304,13 +444,34 @@ AFTER running any Docker build command (docker build, docker compose build, dock
304
444
  3. Example: After \`docker compose up --build\`, run \`docker builder prune -f\`
305
445
  4. For aggressive cleanup: \`docker builder prune -a -f\` (removes ALL unused cache)
306
446
  `;
447
+ // --- SSH Guard Rules: Never run servers over SSH in the foreground --------
448
+ // This runs in EVERY repo the agent enters — not just pi-harness-runtime.
449
+ // Pattern: embed as constant (no file dependency) so it works everywhere.
450
+ const SSH_GUARD_RULES = `
451
+ ## SSH / Remote Server Commands
452
+
453
+ **CRITICAL — SSH BACKGROUNDING RULE:**
454
+ Never run a server or long-running process over SSH in the foreground. This causes the SSH session to hang indefinitely, blocking the agent for hours.
455
+
456
+ - BAD: \`ssh host "python -m uvicorn ..."\` (no &, SSH will hang)
457
+ - GOOD: \`ssh host "nohup python -m uvicorn ... > /tmp/log 2>&1 & echo DONE"\`
458
+ - GOOD: \`ssh host "cd /path && python -m uvicorn ... > log 2>&1 & echo DONE"\`
459
+
460
+ Every SSH command that starts a server, daemon, or background process MUST use the detached SSH pattern: nohup + redirect + & + echo DONE.
461
+ If you are unsure whether a command will hang, ALWAYS use nohup + backgrounding. It is always safe to detach; it is never safe to run a server in the foreground over SSH.
462
+
463
+ Server process examples that must be detached: uvicorn, fastapi dev server, gunicorn, node server, flask run, django runserver, python http.server, any listening daemon.
464
+ `;
465
+
307
466
  let firstAgentStart = true;
308
467
  pi.on("before_agent_start", async (event) => {
309
468
  if (firstAgentStart) {
469
+ event.systemPrompt += TASK_TERMINOLOGY_CLARIFICATION;
310
470
  event.systemPrompt += AUTO_TODO_INVOKE_HINT;
311
471
  event.systemPrompt += COMMIT_BUILD_CHECKLIST;
312
472
  event.systemPrompt += WRITE_REVIEW_HINT;
313
473
  event.systemPrompt += DOCKER_CLEANUP_HINT;
474
+ event.systemPrompt += SSH_GUARD_RULES;
314
475
  firstAgentStart = false;
315
476
  }
316
477
  });
@@ -335,12 +496,13 @@ AFTER running any Docker build command (docker build, docker compose build, dock
335
496
 
336
497
  const TODO_BUILD_REMINDER = `
337
498
 
338
- IMPORTANT - TODO UPDATE REMINDER:
499
+ IMPORTANT - TASK UPDATE REMINDER:
339
500
  Before running a build, ensure you update the current task status:
340
- 1. Mark the task as in_progress with bd update <id> --status in_progress
341
- 2. After build succeeds, update the task: bd close <id> --reason "Done" or bd update <id> --status pending
501
+ 1. If tracking in **todo tool**: mark task in_progress
502
+ 2. If tracking in **bd issues**: use \`bd update <id> --status in_progress\`
503
+ 3. After build succeeds: mark completed in the appropriate tracker
342
504
 
343
- Run \`bd ready\` to see current tasks.
505
+ Run \`bd ready\` to see current bd issues.
344
506
  `;
345
507
 
346
508
  // Detect build commands and append todo reminder to their output
@@ -458,8 +620,9 @@ Run \`bd ready\` to see current tasks.
458
620
  // );
459
621
  tuiMonitor.processMessage(text);
460
622
  }
461
- } catch (e) {
462
- // console.error("[DEBUG message_end] TUI processMessage error:", e);
623
+ // message_end: no-op on parse error (already logged upstream)
624
+ } catch {
625
+ // ignore — the message was already logged by the TUI layer
463
626
  }
464
627
  });
465
628
 
@@ -468,7 +631,10 @@ Run \`bd ready\` to see current tasks.
468
631
  const MINIMAX_REFRESH_TOKEN_THRESHOLD = 200_000;
469
632
  const MINIMAX_REFRESH_REQUEST_THRESHOLD = 12;
470
633
  const quotaScraper = process.env.QUOTA_COOKIE_FILE
471
- ? new MiniMaxQuotaScraper({ cookieFile: process.env.QUOTA_COOKIE_FILE, quiet: true })
634
+ ? new MiniMaxQuotaScraper({
635
+ cookieFile: process.env.QUOTA_COOKIE_FILE,
636
+ quiet: true,
637
+ })
472
638
  : new MiniMaxQuotaScraper({ quiet: true });
473
639
 
474
640
  // --- Smart quota fetch for OpenAI status -------------------------
@@ -497,7 +663,6 @@ Run \`bd ready\` to see current tasks.
497
663
  }
498
664
  };
499
665
 
500
-
501
666
  // --- TUI quota signal plumbing (OpenAI / GLM / Anthropic / OpenRouter) --
502
667
  // The TUIUsageMonitor parses provider quota-exhaustion messages from pi's
503
668
  // TUI / message stream and emits signals we write to per-provider mirror
@@ -589,8 +754,7 @@ Run \`bd ready\` to see current tasks.
589
754
  let pendingOutputLimitResumeAfterCompact = false;
590
755
  let pendingOutputLimitResumeAfterSettled = false;
591
756
  let providerOverloadResumeAttempts = 0;
592
- let providerOverloadResumeTimer: ReturnType<typeof setTimeout> | null =
593
- null;
757
+ let providerOverloadResumeTimer: ReturnType<typeof setTimeout> | null = null;
594
758
 
595
759
  // --- Context-usage escalating warning tiers -----------------------------
596
760
  // Amber at 75%, red at 85%. No notify above 90% (proactive compact handles it).
@@ -950,8 +1114,7 @@ Run \`bd ready\` to see current tasks.
950
1114
  description: "Force refresh quota from provider console",
951
1115
  handler: async (_args: string, ctx: ExtensionCommandContext) => {
952
1116
  const autoFetchAvailable =
953
- hasCookieSource() ||
954
- (await hasBrowserProfileAutoFetchSource());
1117
+ hasCookieSource() || (await hasBrowserProfileAutoFetchSource());
955
1118
  if (!autoFetchAvailable) {
956
1119
  ctx.ui.notify(
957
1120
  "MiniMax cookies not found. Drop any cookie file (Netscape or EditThisCookie JSON) into ~/.pi-harness-runtime/cookies/ — the runtime normalizes it for you. Or run `bun packages/auth/src/run-minimax-auth.ts auth`.",
@@ -1431,7 +1594,9 @@ Run \`bd ready\` to see current tasks.
1431
1594
  }
1432
1595
  }
1433
1596
 
1434
- function scheduleProviderOverloadResume(hasPendingMessages: () => boolean): void {
1597
+ function scheduleProviderOverloadResume(
1598
+ hasPendingMessages: () => boolean,
1599
+ ): void {
1435
1600
  clearProviderOverloadResume();
1436
1601
  const delayMs = getProviderOverloadResumeDelayMs();
1437
1602
  const delayMinutes = Math.max(1, Math.round(delayMs / 60_000));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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": {
@@ -17,7 +17,9 @@
17
17
  "skills:sync": "bun scripts/skills-sync.ts",
18
18
  "skills:sync:check": "bun scripts/skills-sync.ts --check-only",
19
19
  "build": "for pkg in packages/*/; do [ -f \"${pkg}tsconfig.json\" ] && [ \"$pkg\" != \"packages/provider-router/\" ] && node_modules/.bin/tsc -p \"${pkg}tsconfig.json\" --skipLibCheck > /dev/null 2>&1 || true; done",
20
- "prepublishOnly": "bun run build"
20
+ "prepublishOnly": "bun run build",
21
+ "test:harness": "bun test harness/langchain/",
22
+ "test:all": "node --test && bun test harness/langchain/"
21
23
  },
22
24
  "bin": {
23
25
  "harness-auth": "packages/auth/src/run-minimax-auth.ts"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/a2a-adapter",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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-harness/architecture-generator",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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": "@moocoding/config-capture",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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/context-compiler",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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": "@pi-harness/evaluation-engine",
3
- "version": "1.1.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
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.49",
3
+ "version": "1.1.71-3",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {