taskplane 0.22.15 → 0.22.17

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.
@@ -770,10 +770,36 @@ piArgs.push(...args.passthrough);
770
770
 
771
771
  // ── Spawn pi process ─────────────────────────────────────────────────
772
772
 
773
+ // ── System prompt: file-based passthrough to avoid command line limits ────
774
+ // Windows CreateProcess has a ~32K command line limit. Orchestrated worker
775
+ // system prompts routinely exceed this (PROMPT.md + context docs + steps).
776
+ // When the system prompt is large, write it to a temp file and use shell
777
+ // expansion `$(cat file)` to pass it. This works in MSYS2/Git Bash tmux
778
+ // (where the lane sessions run) without hitting the Win32 limit.
779
+ //
780
+ // For small system prompts (< 8K), pass inline for simplicity.
781
+ const SYSTEM_PROMPT_FILE_THRESHOLD = 8192;
782
+ let systemPromptTempFile = null;
783
+
784
+ if (systemPromptContent && systemPromptContent.length >= SYSTEM_PROMPT_FILE_THRESHOLD) {
785
+ // Remove --system-prompt from piArgs (was added above) and use file instead
786
+ const sysIdx = piArgs.indexOf("--system-prompt");
787
+ if (sysIdx >= 0) piArgs.splice(sysIdx, 2);
788
+ // Write to temp file and use --append-system-prompt with @file syntax.
789
+ // Pi's --append-system-prompt accepts @filepath to read from a file.
790
+ // We use --system-prompt "" (empty base) + --append-system-prompt @file
791
+ // to effectively set the system prompt from a file.
792
+ systemPromptTempFile = join(tmpdir(), `pi-rpc-sysprompt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
793
+ writeFileSync(systemPromptTempFile, systemPromptContent, "utf-8");
794
+ piArgs.push("--system-prompt", "");
795
+ piArgs.push("--append-system-prompt", `@${systemPromptTempFile}`);
796
+ process.stderr.write(`[rpc-wrapper] system prompt written to file (${systemPromptContent.length} chars): ${systemPromptTempFile}\n`);
797
+ }
798
+
773
799
  const proc = spawn("pi", piArgs, {
774
800
  stdio: ["pipe", "pipe", "pipe"],
775
801
  env: { ...process.env },
776
- shell: true, // Required for Windows: resolves pi.cmd shim. Matches task-runner.ts pattern.
802
+ shell: true,
777
803
  });
778
804
 
779
805
  // ── TP-097: Write PID file for orphan cleanup ──────────────────
@@ -796,6 +822,9 @@ try {
796
822
  // Clean up PID file on process exit (best-effort)
797
823
  function cleanupPidFile() {
798
824
  try { unlinkSync(pidFilePath); } catch { /* ignore */ }
825
+ if (systemPromptTempFile) {
826
+ try { unlinkSync(systemPromptTempFile); } catch { /* ignore */ }
827
+ }
799
828
  }
800
829
  process.on("exit", cleanupPidFile);
801
830
 
@@ -935,9 +964,17 @@ attachJsonlReader(proc.stdout, (line) => {
935
964
  });
936
965
 
937
966
  // Forward stderr from pi to our stderr
967
+ // Capture pi stderr for diagnostics — last 2KB preserved in exit summary.
968
+ // This is critical for diagnosing startup crashes (pi exits code 1 with 0 tokens).
969
+ let piStderrBuffer = "";
970
+ const PI_STDERR_MAX = 2048;
938
971
  proc.stderr?.setEncoding("utf-8");
939
972
  proc.stderr?.on("data", (chunk) => {
940
973
  process.stderr.write(chunk);
974
+ piStderrBuffer += chunk;
975
+ if (piStderrBuffer.length > PI_STDERR_MAX * 2) {
976
+ piStderrBuffer = piStderrBuffer.slice(-PI_STDERR_MAX);
977
+ }
941
978
  });
942
979
 
943
980
  // ── Single-Write Exit Summary Finalization ───────────────────────────
@@ -967,7 +1004,8 @@ proc.on("close", (code, signal) => {
967
1004
 
968
1005
  if (!state.agentEnded && code !== 0) {
969
1006
  // Process crashed without agent_end — capture what we have
970
- const crashError = state.error || `pi process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`;
1007
+ const stderrTail = piStderrBuffer.trim().slice(-PI_STDERR_MAX);
1008
+ const crashError = state.error || `pi process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}${stderrTail ? `\npi stderr: ${stderrTail}` : ""}`;
971
1009
  writeExitSummary(state, code, signal, crashError, startTime);
972
1010
  } else {
973
1011
  writeExitSummary(state, code, signal, null, startTime);
@@ -173,8 +173,9 @@ export function generateTelemetryPaths(
173
173
  const effectiveBatchId = batchId || String(Date.now());
174
174
  const effectiveRepoId = repoId || "default";
175
175
 
176
- // Extract role from sessionName lane sessions are "worker" role
177
- const role = "worker";
176
+ // Lane sessions are the task-runner orchestration layer, NOT the worker agent.
177
+ // Use "lane" role to avoid filename collisions with worker sidecar files.
178
+ const role = "lane";
178
179
  const laneMatch = sessionName.match(/lane-(\d+)/);
179
180
  const laneSuffix = laneMatch ? `-lane-${laneMatch[1]}` : "";
180
181
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.22.15",
3
+ "version": "0.22.17",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",