pi-subagents 0.8.3 → 0.8.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.8.4] - 2026-02-13
6
+
7
+ ### Fixed
8
+ - JSONL artifact files no longer written by default — they duplicated pi's own session files and were the sole cause of `subagent-artifacts` directories growing to 10+ GB. Changed `includeJsonl` default from `true` to `false`. `_output.md` and `_meta.json` still capture the useful data.
9
+ - Artifact cleanup now covers session-based directories, not just the temp dir. Previously `cleanupOldArtifacts` only ran on `os.tmpdir()/pi-subagent-artifacts` at startup, while sync runs (the common path) wrote to `<session-dir>/subagent-artifacts/` which was never cleaned. Now scans all `~/.pi/agent/sessions/*/subagent-artifacts/` dirs on startup and cleans the current session's artifacts dir on session lifecycle events.
10
+ - JSONL writer now enforces a 50 MB size cap (`maxBytes` on `JsonlWriterDeps`) as defense-in-depth for users who opt into JSONL. Silently stops writing at the cap without pausing the source stream, so the progress tracker keeps working.
11
+
5
12
  ## [0.8.3] - 2026-02-11
6
13
 
7
14
  ### Added
package/artifacts.ts CHANGED
@@ -69,3 +69,24 @@ export function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {
69
69
 
70
70
  fs.writeFileSync(markerPath, String(now));
71
71
  }
72
+
73
+ export function cleanupAllArtifactDirs(maxAgeDays: number): void {
74
+ cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);
75
+
76
+ const sessionsBase = path.join(os.homedir(), ".pi", "agent", "sessions");
77
+ if (!fs.existsSync(sessionsBase)) return;
78
+
79
+ let dirs: string[];
80
+ try {
81
+ dirs = fs.readdirSync(sessionsBase);
82
+ } catch {
83
+ return;
84
+ }
85
+
86
+ for (const dir of dirs) {
87
+ const artifactsDir = path.join(sessionsBase, dir, "subagent-artifacts");
88
+ try {
89
+ cleanupOldArtifacts(artifactsDir, maxAgeDays);
90
+ } catch {}
91
+ }
92
+ }
package/index.ts CHANGED
@@ -22,7 +22,7 @@ import { type AgentConfig, type AgentScope, discoverAgents, discoverAgentsAll }
22
22
  import { resolveExecutionAgentScope } from "./agent-scope.js";
23
23
  import { cleanupOldChainDirs, getStepAgents, isParallelStep, resolveStepBehavior, type ChainStep, type SequentialStep } from "./settings.js";
24
24
  import { ChainClarifyComponent, type ChainClarifyResult, type ModelInfo } from "./chain-clarify.js";
25
- import { cleanupOldArtifacts, getArtifactsDir } from "./artifacts.js";
25
+ import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "./artifacts.js";
26
26
  import {
27
27
  type AgentProgress,
28
28
  type ArtifactConfig,
@@ -78,7 +78,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
78
78
  const asyncByDefault = config.asyncByDefault === true;
79
79
 
80
80
  const tempArtifactsDir = getArtifactsDir(null);
81
- cleanupOldArtifacts(tempArtifactsDir, DEFAULT_ARTIFACT_CONFIG.cleanupDays);
81
+ cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
82
82
  let baseCwd = process.cwd();
83
83
  let currentSessionId: string | null = null;
84
84
  const asyncJobs = new Map<string, AsyncJobState>();
@@ -1143,9 +1143,19 @@ MANAGEMENT (use action field — omit agent/task/chain/tasks):
1143
1143
  }
1144
1144
  });
1145
1145
 
1146
+ const cleanupSessionArtifacts = (ctx: ExtensionContext) => {
1147
+ try {
1148
+ const sessionFile = ctx.sessionManager.getSessionFile();
1149
+ if (sessionFile) {
1150
+ cleanupOldArtifacts(getArtifactsDir(sessionFile), DEFAULT_ARTIFACT_CONFIG.cleanupDays);
1151
+ }
1152
+ } catch {}
1153
+ };
1154
+
1146
1155
  pi.on("session_start", (_event, ctx) => {
1147
1156
  baseCwd = ctx.cwd;
1148
1157
  currentSessionId = ctx.sessionManager.getSessionFile() ?? `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1158
+ cleanupSessionArtifacts(ctx);
1149
1159
  for (const timer of cleanupTimers.values()) clearTimeout(timer);
1150
1160
  cleanupTimers.clear();
1151
1161
  asyncJobs.clear();
@@ -1158,6 +1168,7 @@ MANAGEMENT (use action field — omit agent/task/chain/tasks):
1158
1168
  pi.on("session_switch", (_event, ctx) => {
1159
1169
  baseCwd = ctx.cwd;
1160
1170
  currentSessionId = ctx.sessionManager.getSessionFile() ?? `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1171
+ cleanupSessionArtifacts(ctx);
1161
1172
  for (const timer of cleanupTimers.values()) clearTimeout(timer);
1162
1173
  cleanupTimers.clear();
1163
1174
  asyncJobs.clear();
@@ -1170,6 +1181,7 @@ MANAGEMENT (use action field — omit agent/task/chain/tasks):
1170
1181
  pi.on("session_branch", (_event, ctx) => {
1171
1182
  baseCwd = ctx.cwd;
1172
1183
  currentSessionId = ctx.sessionManager.getSessionFile() ?? `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1184
+ cleanupSessionArtifacts(ctx);
1173
1185
  for (const timer of cleanupTimers.values()) clearTimeout(timer);
1174
1186
  cleanupTimers.clear();
1175
1187
  asyncJobs.clear();
package/jsonl-writer.ts CHANGED
@@ -11,8 +11,11 @@ export interface JsonlWriteStream {
11
11
  end(callback?: () => void): void;
12
12
  }
13
13
 
14
+ const DEFAULT_MAX_JSONL_BYTES = 50 * 1024 * 1024;
15
+
14
16
  export interface JsonlWriterDeps {
15
17
  createWriteStream?: (filePath: string) => JsonlWriteStream;
18
+ maxBytes?: number;
16
19
  }
17
20
 
18
21
  export interface JsonlWriter {
@@ -45,12 +48,18 @@ export function createJsonlWriter(
45
48
 
46
49
  let backpressured = false;
47
50
  let closed = false;
51
+ let bytesWritten = 0;
52
+ const maxBytes = deps.maxBytes ?? DEFAULT_MAX_JSONL_BYTES;
48
53
 
49
54
  return {
50
55
  writeLine(line: string) {
51
56
  if (!stream || closed || !line.trim()) return;
57
+ const chunk = `${line}\n`;
58
+ const chunkBytes = Buffer.byteLength(chunk, "utf-8");
59
+ if (bytesWritten + chunkBytes > maxBytes) return;
52
60
  try {
53
- const ok = stream.write(`${line}\n`);
61
+ const ok = stream.write(chunk);
62
+ bytesWritten += chunkBytes;
54
63
  if (!ok && !backpressured) {
55
64
  backpressured = true;
56
65
  source.pause();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
package/types.ts CHANGED
@@ -232,7 +232,7 @@ export const DEFAULT_ARTIFACT_CONFIG: ArtifactConfig = {
232
232
  enabled: true,
233
233
  includeInput: true,
234
234
  includeOutput: true,
235
- includeJsonl: true,
235
+ includeJsonl: false,
236
236
  includeMetadata: true,
237
237
  cleanupDays: 7,
238
238
  };