pi-subagents 0.24.3 → 0.24.4

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.
@@ -8,6 +8,7 @@ export interface RunnerSubagentStep {
8
8
  tools?: string[];
9
9
  extensions?: string[];
10
10
  mcpDirectTools?: string[];
11
+ completionGuard?: boolean;
11
12
  systemPrompt?: string | null;
12
13
  systemPromptMode?: "append" | "replace";
13
14
  inheritProjectContext: boolean;
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { resolveMcpDirectToolNames } from "./mcp-direct-tool-allowlist.ts";
5
6
 
6
7
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
7
8
  const TASK_ARG_LIMIT = 8000;
@@ -27,6 +28,7 @@ interface BuildPiArgsInput {
27
28
  extensions?: string[];
28
29
  systemPrompt?: string | null;
29
30
  mcpDirectTools?: string[];
31
+ cwd?: string;
30
32
  promptFileStem?: string;
31
33
  intercomSessionName?: string;
32
34
  orchestratorIntercomTarget?: string;
@@ -80,6 +82,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
80
82
  }
81
83
  }
82
84
  if (builtinTools.length > 0) {
85
+ if (input.mcpDirectTools?.length) {
86
+ builtinTools.push(...resolveMcpDirectToolNames(input.mcpDirectTools, input.cwd));
87
+ }
83
88
  args.push("--tools", builtinTools.join(","));
84
89
  }
85
90
  }
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
- import * as os from "node:os";
3
2
  import * as path from "node:path";
3
+ import { getAgentDir } from "../../shared/utils.ts";
4
4
 
5
5
  export interface RunEntry {
6
6
  agent: string;
@@ -11,10 +11,13 @@ export interface RunEntry {
11
11
  exit?: number;
12
12
  }
13
13
 
14
- const HISTORY_PATH = path.join(os.homedir(), ".pi", "agent", "run-history.jsonl");
15
14
  const ROTATE_READ_THRESHOLD = 1200;
16
15
  const ROTATE_KEEP = 1000;
17
16
 
17
+ function getHistoryPath(): string {
18
+ return path.join(getAgentDir(), "run-history.jsonl");
19
+ }
20
+
18
21
  export function recordRun(agent: string, task: string, exitCode: number, durationMs: number): void {
19
22
  try {
20
23
  const entry: RunEntry = {
@@ -25,18 +28,20 @@ export function recordRun(agent: string, task: string, exitCode: number, duratio
25
28
  duration: durationMs,
26
29
  ...(exitCode !== 0 ? { exit: exitCode } : {}),
27
30
  };
28
- fs.mkdirSync(path.dirname(HISTORY_PATH), { recursive: true });
29
- fs.appendFileSync(HISTORY_PATH, `${JSON.stringify(entry)}\n`);
31
+ const historyPath = getHistoryPath();
32
+ fs.mkdirSync(path.dirname(historyPath), { recursive: true });
33
+ fs.appendFileSync(historyPath, `${JSON.stringify(entry)}\n`);
30
34
  } catch {
31
35
  // Best-effort — never crash the execution flow for history recording
32
36
  }
33
37
  }
34
38
 
35
39
  export function loadRunsForAgent(agent: string): RunEntry[] {
36
- if (!fs.existsSync(HISTORY_PATH)) return [];
40
+ const historyPath = getHistoryPath();
41
+ if (!fs.existsSync(historyPath)) return [];
37
42
  let raw: string;
38
43
  try {
39
- raw = fs.readFileSync(HISTORY_PATH, "utf-8");
44
+ raw = fs.readFileSync(historyPath, "utf-8");
40
45
  } catch {
41
46
  return [];
42
47
  }
@@ -45,7 +50,7 @@ export function loadRunsForAgent(agent: string): RunEntry[] {
45
50
 
46
51
  if (lines.length > ROTATE_READ_THRESHOLD) {
47
52
  lines = lines.slice(-ROTATE_KEEP);
48
- try { fs.writeFileSync(HISTORY_PATH, `${lines.join("\n")}\n`, "utf-8"); } catch {}
53
+ try { fs.writeFileSync(historyPath, `${lines.join("\n")}\n`, "utf-8"); } catch {}
49
54
  }
50
55
 
51
56
  return lines
@@ -8,12 +8,22 @@ export interface SingleOutputSnapshot {
8
8
  size?: number;
9
9
  }
10
10
 
11
+ export function normalizeSingleOutputOverride(
12
+ output: string | boolean | undefined,
13
+ defaultOutput: string | undefined,
14
+ ): string | false | undefined {
15
+ if (output === false || output === "false") return false;
16
+ if (output === true || output === "true") return defaultOutput;
17
+ if (typeof output === "string" && output.length > 0) return output;
18
+ return undefined;
19
+ }
20
+
11
21
  export function resolveSingleOutputPath(
12
- output: string | false | undefined,
22
+ output: string | boolean | undefined,
13
23
  runtimeCwd: string,
14
24
  requestedCwd?: string,
15
25
  ): string | undefined {
16
- if (typeof output !== "string" || !output) return undefined;
26
+ if (typeof output !== "string" || !output || output === "false" || output === "true") return undefined;
17
27
  if (path.isAbsolute(output)) return output;
18
28
  const baseCwd = requestedCwd
19
29
  ? (path.isAbsolute(requestedCwd) ? requestedCwd : path.resolve(runtimeCwd, requestedCwd))
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
- import * as os from "node:os";
3
2
  import * as path from "node:path";
4
3
  import { TEMP_ARTIFACTS_DIR, type ArtifactPaths } from "./types.ts";
4
+ import { getAgentDir } from "./utils.ts";
5
5
  const CLEANUP_MARKER_FILE = ".last-cleanup";
6
6
 
7
7
  export function getArtifactsDir(sessionFile: string | null): string {
@@ -74,7 +74,7 @@ export function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {
74
74
  export function cleanupAllArtifactDirs(maxAgeDays: number): void {
75
75
  cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);
76
76
 
77
- const sessionsBase = path.join(os.homedir(), ".pi", "agent", "sessions");
77
+ const sessionsBase = path.join(getAgentDir(), "sessions");
78
78
  if (!fs.existsSync(sessionsBase)) return;
79
79
 
80
80
  let dirs: string[];
@@ -13,6 +13,13 @@ import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, Singl
13
13
  // File System Utilities
14
14
  // ============================================================================
15
15
 
16
+ export function getAgentDir(): string {
17
+ const configured = process.env.PI_CODING_AGENT_DIR;
18
+ if (configured === "~") return os.homedir();
19
+ if (configured?.startsWith("~/")) return path.join(os.homedir(), configured.slice(2));
20
+ return configured || path.join(os.homedir(), ".pi", "agent");
21
+ }
22
+
16
23
  const statusCache = new Map<string, { mtime: number; status: AsyncStatus }>();
17
24
 
18
25
  function getErrorMessage(error: unknown): string {
@@ -182,8 +189,11 @@ export function getFinalOutput(messages: Message[]): string {
182
189
  for (let i = messages.length - 1; i >= 0; i--) {
183
190
  const msg = messages[i];
184
191
  if (msg.role === "assistant") {
192
+ const hasAssistantError = ("errorMessage" in msg && typeof msg.errorMessage === "string" && msg.errorMessage.length > 0)
193
+ || ("stopReason" in msg && msg.stopReason === "error");
194
+ if (hasAssistantError) continue;
185
195
  for (const part of msg.content) {
186
- if (part.type === "text") return part.text;
196
+ if (part.type === "text" && part.text.trim().length > 0) return part.text;
187
197
  }
188
198
  }
189
199
  }