privateer-agent 0.12.26 → 0.12.28

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.
@@ -64,6 +64,38 @@ function sweepLegacyShims() {
64
64
  }
65
65
  }
66
66
 
67
+ // Pi's shell tools spill oversized command output to `$TMPDIR/pi-{bash,powershell,
68
+ // output}-<hex>.log` and hand the model the path — a good design, with no other half:
69
+ // NOTHING in Pi ever deletes them. Two `grep -rn` calls that wandered into node_modules
70
+ // left 630MB of minified JS in one developer's tmpdir, inside a 640MB/22-file pile that
71
+ // had been accumulating for weeks. macOS clears /var/folders only on its own schedule
72
+ // and Linux distros vary, so on a long-lived machine this is simply a leak.
73
+ //
74
+ // A day is the useful window: the path is only ever quoted into a live session, so a log
75
+ // outlives its usefulness the moment that session ends. Silent and best-effort — the acp
76
+ // branch's stdout is a JSON-RPC stream, and a tmpdir we can't read is not a launch
77
+ // problem. Name-matched before stat so the common case is one readdir.
78
+ const BASH_LOG_MAX_AGE_MS = 24 * 60 * 60 * 1000;
79
+ const BASH_LOG_RE = /^pi-(?:bash|powershell|output)-[0-9a-f]{16}\.log$/;
80
+
81
+ function sweepBashLogs() {
82
+ try {
83
+ const dir = os.tmpdir();
84
+ const cutoff = Date.now() - BASH_LOG_MAX_AGE_MS;
85
+ for (const name of fs.readdirSync(dir)) {
86
+ if (!BASH_LOG_RE.test(name)) continue;
87
+ const file = path.join(dir, name);
88
+ try {
89
+ if (fs.statSync(file).mtimeMs < cutoff) fs.rmSync(file, { force: true });
90
+ } catch {
91
+ /* raced with another session, or not ours to delete — either way, skip it. */
92
+ }
93
+ }
94
+ } catch {
95
+ /* unreadable tmpdir; the logs stay. */
96
+ }
97
+ }
98
+
67
99
  // --- bundle detection ------------------------------------------------------
68
100
  // A self-contained bundle ships its own pinned Node at "$REPO/node[.exe]" plus a
69
101
  // BUNDLE_INFO.json marker (built by scripts/build-bundle.mjs). When present we use
@@ -279,6 +311,7 @@ if (sub === "update") {
279
311
  // `daemon` is a hidden back-compat alias for the pre-rename command name.
280
312
  else if (sub === "harbor" || sub === "daemon") {
281
313
  sweepLegacyShims(); // a harbor-only machine upgrades too — see the function's note
314
+ sweepBashLogs();
282
315
  const nodeArgs = fs.existsSync(ENV_FILE) ? [`--env-file=${ENV_FILE}`] : [];
283
316
  // A resident background process, stopped by launchd/systemd/scripts with a plain
284
317
  // `kill` — which reaches only this launcher. See runToCompletion.
@@ -304,6 +337,7 @@ else if (sub === "verify") {
304
337
  // stray stdout line breaks the JSON-RPC stream and the host disconnects.
305
338
  else if (sub === "acp") {
306
339
  sweepLegacyShims(); // silent: only ever removes files
340
+ sweepBashLogs(); // silent too
307
341
  const nodeArgs = fs.existsSync(ENV_FILE) ? [`--env-file=${ENV_FILE}`] : [];
308
342
  // Long-lived and driven over stdio by an editor, which stops it by terminating
309
343
  // the process rather than by a keystroke. Same leak, same fix.
@@ -329,6 +363,7 @@ else {
329
363
  // to drop one, and clear out any shim an older release left behind.
330
364
  fs.mkdirSync(EXT_DIR, { recursive: true });
331
365
  sweepLegacyShims();
366
+ sweepBashLogs();
332
367
 
333
368
  // Resolve every moat entry point to an absolute path, to be passed to Pi as `-e`.
334
369
  // Dependencies resolve by walking the node_modules chain, NOT as REPO/node_modules: npm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateer-agent",
3
- "version": "0.12.26",
3
+ "version": "0.12.28",
4
4
  "description": "Privacy-first terminal coding agent — bring your own model across 20 providers (Anthropic, OpenAI, OpenRouter, Google, local Ollama…). Safe-by-default permissions, MCP, sub-agents, workflows, and verifiable TEE inference. Built on the Pi toolkit.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -69,9 +69,9 @@
69
69
  "node": ">=22.19.0"
70
70
  },
71
71
  "dependencies": {
72
- "@earendil-works/pi-ai": "0.84.1",
73
- "@earendil-works/pi-coding-agent": "0.84.1",
74
- "@earendil-works/pi-tui": "0.84.1",
72
+ "@earendil-works/pi-ai": "0.84.4",
73
+ "@earendil-works/pi-coding-agent": "0.84.4",
74
+ "@earendil-works/pi-tui": "0.84.4",
75
75
  "@juicesharp/rpiv-ask-user-question": "2.2.0",
76
76
  "@juicesharp/rpiv-web-tools": "1.20.0",
77
77
  "@noble/ciphers": "2.2.0",
@@ -80,6 +80,7 @@
80
80
  "@phala/dcap-qvl": "0.5.2",
81
81
  "@zed-industries/agent-client-protocol": "0.4.5",
82
82
  "patch-package": "8.0.1",
83
+ "pi-background-tasks": "2.4.2",
83
84
  "pi-mcp-adapter": "2.11.0",
84
85
  "pi-privacy": "0.13.0",
85
86
  "pi-subagents": "0.34.0",
@@ -1,8 +1,8 @@
1
1
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/config.js b/node_modules/@earendil-works/pi-coding-agent/dist/config.js
2
- index 4600b23..075ecae 100644
2
+ index f04269f..536e030 100644
3
3
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/config.js
4
4
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/config.js
5
- @@ -393,6 +393,64 @@ export const APP_NAME = piConfigName || "pi";
5
+ @@ -401,6 +401,64 @@ export const APP_NAME = piConfigName || "pi";
6
6
  export const APP_TITLE = piConfigName ? APP_NAME : "π";
7
7
  export const CONFIG_DIR_NAME = pkg.piConfig?.configDir || ".pi";
8
8
  export const VERSION = pkg.version || "0.0.0";
@@ -68,7 +68,7 @@ index 4600b23..075ecae 100644
68
68
  export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
69
69
  export const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;
70
70
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
71
- index ce8a9a2..679e584 100644
71
+ index fd82b71..5e0821c 100644
72
72
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
73
73
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
74
74
  @@ -38,6 +38,111 @@ import { createLocalBashOperations } from "./tools/bash.js";
@@ -207,7 +207,7 @@ index ce8a9a2..679e584 100644
207
207
  throw new Error(`Authentication failed for "${model.provider}". ` +
208
208
  `Credentials may have expired or network is unavailable. ` +
209
209
  `Run '/login ${model.provider}' to re-authenticate.`);
210
- @@ -360,6 +475,16 @@ export class AgentSession {
210
+ @@ -380,6 +495,16 @@ export class AgentSession {
211
211
  }
212
212
  }
213
213
  }
@@ -224,7 +224,7 @@ index ce8a9a2..679e584 100644
224
224
  // Emit to extensions first
225
225
  await this._emitExtensionEvent(event);
226
226
  // Notify all listeners
227
- @@ -772,6 +897,33 @@ export class AgentSession {
227
+ @@ -801,6 +926,33 @@ export class AgentSession {
228
228
  finalError: msg.errorMessage,
229
229
  });
230
230
  this._retryAttempt = 0;
@@ -258,7 +258,7 @@ index ce8a9a2..679e584 100644
258
258
  }
259
259
  if (await this._checkCompaction(msg)) {
260
260
  return true;
261
- @@ -852,6 +1004,14 @@ export class AgentSession {
261
+ @@ -882,6 +1034,14 @@ export class AgentSession {
262
262
  if (!hasConfiguredAuth) {
263
263
  const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
264
264
  if (isOAuth) {
@@ -273,7 +273,7 @@ index ce8a9a2..679e584 100644
273
273
  throw new Error(`Authentication failed for "${this.model.provider}". ` +
274
274
  `Credentials may have expired or network is unavailable. ` +
275
275
  `Run '/login ${this.model.provider}' to re-authenticate.`);
276
- @@ -861,7 +1021,13 @@ export class AgentSession {
276
+ @@ -891,7 +1051,13 @@ export class AgentSession {
277
277
  // Check if we need to compact before sending (catches aborted responses).
278
278
  // The user's new prompt is sent below, so do not call agent.continue() here.
279
279
  const lastAssistant = this._findLastAssistantMessage();
@@ -288,7 +288,7 @@ index ce8a9a2..679e584 100644
288
288
  await this._checkCompaction(lastAssistant, false);
289
289
  }
290
290
  // Build messages array (custom message if any, then user message)
291
- @@ -2084,6 +2250,27 @@ export class AgentSession {
291
+ @@ -2242,6 +2408,27 @@ export class AgentSession {
292
292
  // Context overflow is handled by compaction, not retry.
293
293
  if (isContextOverflow(message, this.model?.contextWindow ?? 0))
294
294
  return false;
@@ -316,7 +316,7 @@ index ce8a9a2..679e584 100644
316
316
  return isRetryableAssistantError(message);
317
317
  }
318
318
  /**
319
- @@ -2129,7 +2316,11 @@ export class AgentSession {
319
+ @@ -2287,7 +2474,11 @@ export class AgentSession {
320
320
  this._retryAttempt--;
321
321
  return false;
322
322
  }
@@ -375,7 +375,7 @@ index 197bccc..bc9ac3f 100644
375
375
  //# sourceMappingURL=auth-guidance.js.map
376
376
 
377
377
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
378
- index 324d30a..e613e92 100644
378
+ index bb022d4..382ff2b 100644
379
379
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
380
380
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
381
381
  @@ -18,7 +18,7 @@ import { createJiti } from "jiti/static";
@@ -387,7 +387,7 @@ index 324d30a..e613e92 100644
387
387
  // NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
388
388
  // avoiding a circular dependency. Extensions can import from @earendil-works/pi-coding-agent.
389
389
  import * as _bundledPiCodingAgent from "../../index.js";
390
- @@ -554,9 +554,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
390
+ @@ -621,9 +621,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
391
391
  }
392
392
  }
393
393
  };
@@ -415,19 +415,19 @@ index 324d30a..e613e92 100644
415
415
  const globalExtDir = path.join(resolvedAgentDir, "extensions");
416
416
  addPaths(discoverExtensionsInDir(globalExtDir));
417
417
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
418
- index e806af3..9b00ebd 100644
418
+ index 696e98c..7585930 100644
419
419
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
420
420
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
421
- @@ -25,7 +25,7 @@ import { globSync } from "glob";
421
+ @@ -24,7 +24,7 @@ import { basename, dirname, join, relative, resolve, sep } from "node:path";
422
422
  import ignore from "ignore";
423
423
  import { minimatch } from "minimatch";
424
- import { maxSatisfying, rcompare, satisfies, valid, validRange } from "semver";
424
+ import { gt, maxSatisfying, rcompare, satisfies, valid, validRange } from "semver";
425
425
  -import { CONFIG_DIR_NAME } from "../config.js";
426
426
  +import { projectConfigDirs, projectConfigWriteDir, resolveProjectConfigPath } from "../config.js";
427
427
  import { spawnProcess, spawnProcessSync } from "../utils/child-process.js";
428
428
  import { parseGitUrl } from "../utils/git.js";
429
429
  import { canonicalizePath, isLocalPath, markPathIgnoredByCloudSync, resolvePath } from "../utils/paths.js";
430
- @@ -698,23 +698,29 @@ export class DefaultPackageManager {
430
+ @@ -711,23 +711,29 @@ export class DefaultPackageManager {
431
431
  const packageSources = this.dedupePackages(allPackages);
432
432
  await this.resolvePackageSources(packageSources, accumulator, onMissing);
433
433
  const globalBaseDir = this.agentDir;
@@ -464,7 +464,7 @@ index e806af3..9b00ebd 100644
464
464
  return this.toResolvedPaths(accumulator);
465
465
  }
466
466
  async resolveExtensionSources(sources, options) {
467
- @@ -1668,13 +1674,30 @@ export class DefaultPackageManager {
467
+ @@ -1681,13 +1687,30 @@ export class DefaultPackageManager {
468
468
  writeFileSync(ignorePath, "*\n!.gitignore\n", "utf-8");
469
469
  }
470
470
  }
@@ -496,7 +496,7 @@ index e806af3..9b00ebd 100644
496
496
  }
497
497
  return join(this.agentDir, "npm");
498
498
  }
499
- @@ -1713,7 +1736,9 @@ export class DefaultPackageManager {
499
+ @@ -1726,7 +1749,9 @@ export class DefaultPackageManager {
500
500
  }
501
501
  if (scope === "project") {
502
502
  this.assertProjectTrustedForScope(scope);
@@ -507,7 +507,7 @@ index e806af3..9b00ebd 100644
507
507
  }
508
508
  return join(this.agentDir, "npm", "node_modules", source.name);
509
509
  }
510
- @@ -1749,7 +1774,7 @@ export class DefaultPackageManager {
510
+ @@ -1762,7 +1787,7 @@ export class DefaultPackageManager {
511
511
  }
512
512
  if (scope === "project") {
513
513
  this.assertProjectTrustedForScope(scope);
@@ -516,7 +516,7 @@ index e806af3..9b00ebd 100644
516
516
  }
517
517
  return join(this.agentDir, "git");
518
518
  }
519
- @@ -1772,7 +1797,7 @@ export class DefaultPackageManager {
519
+ @@ -1785,7 +1810,7 @@ export class DefaultPackageManager {
520
520
  getBaseDirForScope(scope) {
521
521
  if (scope === "project") {
522
522
  this.assertProjectTrustedForScope(scope);
@@ -525,7 +525,7 @@ index e806af3..9b00ebd 100644
525
525
  }
526
526
  if (scope === "user") {
527
527
  return this.agentDir;
528
- @@ -1783,7 +1808,30 @@ export class DefaultPackageManager {
528
+ @@ -1796,7 +1821,30 @@ export class DefaultPackageManager {
529
529
  return resolvePath(input, this.cwd, { homeDir: getHomeDir(), trim: true });
530
530
  }
531
531
  resolvePathFromBase(input, baseDir) {
@@ -557,7 +557,7 @@ index e806af3..9b00ebd 100644
557
557
  }
558
558
  collectPackageResources(packageRoot, accumulator, filter, metadata) {
559
559
  if (filter) {
560
- @@ -1928,19 +1976,24 @@ export class DefaultPackageManager {
560
+ @@ -1936,19 +1984,24 @@ export class DefaultPackageManager {
561
561
  this.addResource(target, f, metadata, enabledPaths.has(f));
562
562
  }
563
563
  }
@@ -586,7 +586,7 @@ index e806af3..9b00ebd 100644
586
586
  const userOverrides = {
587
587
  extensions: (globalSettings.extensions ?? []),
588
588
  skills: (globalSettings.skills ?? []),
589
- @@ -1959,12 +2012,12 @@ export class DefaultPackageManager {
589
+ @@ -1967,12 +2020,12 @@ export class DefaultPackageManager {
590
590
  prompts: join(globalBaseDir, "prompts"),
591
591
  themes: join(globalBaseDir, "themes"),
592
592
  };
@@ -605,7 +605,7 @@ index e806af3..9b00ebd 100644
605
605
  const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
606
606
  const projectTrusted = this.settingsManager.isProjectTrusted();
607
607
  const projectAgentsSkillDirs = projectTrusted
608
- @@ -1978,10 +2031,13 @@ export class DefaultPackageManager {
608
+ @@ -1986,10 +2039,13 @@ export class DefaultPackageManager {
609
609
  }
610
610
  };
611
611
  if (projectTrusted) {
@@ -623,7 +623,7 @@ index e806af3..9b00ebd 100644
623
623
  }
624
624
  // Project skills from .agents/ (each with its own baseDir)
625
625
  for (const agentsSkillsDir of projectAgentsSkillDirs) {
626
- @@ -1993,8 +2049,11 @@ export class DefaultPackageManager {
626
+ @@ -2001,8 +2057,11 @@ export class DefaultPackageManager {
627
627
  addResources("skills", collectAutoSkillEntries(agentsSkillsDir, "agents"), agentsMetadata, projectOverrides.skills, agentsBaseDir);
628
628
  }
629
629
  if (projectTrusted) {
@@ -638,17 +638,17 @@ index e806af3..9b00ebd 100644
638
638
  // User extensions from ~/.pi/agent/
639
639
  addResources("extensions", collectAutoExtensionEntries(userDirs.extensions), userMetadata, userOverrides.extensions, globalBaseDir);
640
640
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
641
- index c4f2509..a4402df 100644
641
+ index 2f85768..328b27b 100644
642
642
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
643
643
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
644
644
  @@ -1,8 +1,8 @@
645
- -import { CONFIG_DIR_NAME } from "../config.js";
646
- +import { formatProjectConfigDirNames } from "../config.js";
645
+ -import { APP_NAME, CONFIG_DIR_NAME } from "../config.js";
646
+ +import { APP_NAME, formatProjectConfigDirNames } from "../config.js";
647
647
  import { emitProjectTrustEvent } from "./extensions/runner.js";
648
648
  import { getProjectTrustOptions, hasTrustRequiringProjectResources, } from "./trust-manager.js";
649
649
  function formatProjectTrustPrompt(cwd) {
650
- - return `Trust project folder?\n${cwd}\n\nThis allows pi to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
651
- + return `Trust project folder?\n${cwd}\n\nThis allows pi to load ${formatProjectConfigDirNames()} settings and resources, install missing project packages, and execute project extensions.`;
650
+ - return `Trust project folder?\n${cwd}\n\nThis allows ${APP_NAME} to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
651
+ + return `Trust project folder?\n${cwd}\n\nThis allows ${APP_NAME} to load ${formatProjectConfigDirNames()} settings and resources, install missing project packages, and execute project extensions.`;
652
652
  }
653
653
  async function selectProjectTrustOption(cwd, ctx) {
654
654
  const options = getProjectTrustOptions(cwd, { includeSessionOnly: true });
@@ -696,7 +696,7 @@ index 3700c08..79cb85e 100644
696
696
  // 3. Load explicit prompt paths
697
697
  for (const rawPath of promptPaths) {
698
698
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
699
- index 97af6cd..74d5d52 100644
699
+ index c5ed4d7..d9f50ae 100644
700
700
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
701
701
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
702
702
  @@ -1,7 +1,7 @@
@@ -707,8 +707,8 @@ index 97af6cd..74d5d52 100644
707
707
  +import { findProjectConfigPath, projectConfigDirCandidates, projectConfigDirs } from "../config.js";
708
708
  import { loadThemeFromPath } from "../modes/interactive/theme/theme.js";
709
709
  import { canonicalizePath, isLocalPath, resolvePath } from "../utils/paths.js";
710
- import { createEventBus } from "./event-bus.js";
711
- @@ -624,12 +624,14 @@ export class DefaultResourceLoader {
710
+ import { stripBom } from "../utils/text.js";
711
+ @@ -625,12 +625,14 @@ export class DefaultResourceLoader {
712
712
  join(this.agentDir, "themes"),
713
713
  join(this.agentDir, "extensions"),
714
714
  ];
@@ -729,7 +729,7 @@ index 97af6cd..74d5d52 100644
729
729
  for (const root of agentRoots) {
730
730
  if (this.isUnderPath(normalizedPath, root)) {
731
731
  return { path: filePath, source: "local", scope: "user", origin: "top-level", baseDir: root };
732
- @@ -668,7 +670,11 @@ export class DefaultResourceLoader {
732
+ @@ -669,7 +671,11 @@ export class DefaultResourceLoader {
733
733
  const themes = [];
734
734
  const diagnostics = [];
735
735
  if (includeDefaults) {
@@ -742,7 +742,7 @@ index 97af6cd..74d5d52 100644
742
742
  for (const dir of defaultDirs) {
743
743
  this.loadThemesFromDir(dir, themes, diagnostics);
744
744
  }
745
- @@ -806,8 +812,9 @@ export class DefaultResourceLoader {
745
+ @@ -807,8 +813,9 @@ export class DefaultResourceLoader {
746
746
  return { themes: Array.from(seen.values()), diagnostics };
747
747
  }
748
748
  discoverSystemPromptFile() {
@@ -754,7 +754,7 @@ index 97af6cd..74d5d52 100644
754
754
  return projectPath;
755
755
  }
756
756
  const globalPath = join(this.agentDir, "SYSTEM.md");
757
- @@ -817,8 +824,9 @@ export class DefaultResourceLoader {
757
+ @@ -818,8 +825,9 @@ export class DefaultResourceLoader {
758
758
  return undefined;
759
759
  }
760
760
  discoverAppendSystemPromptFile() {
@@ -767,7 +767,7 @@ index 97af6cd..74d5d52 100644
767
767
  }
768
768
  const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
769
769
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
770
- index cb06c10..41fb90c 100644
770
+ index f0f936e..ad164d2 100644
771
771
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
772
772
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
773
773
  @@ -2,7 +2,7 @@ import { randomUUID } from "crypto";
@@ -777,11 +777,11 @@ index cb06c10..41fb90c 100644
777
777
  -import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
778
778
  +import { getAgentDir, projectConfigDirCandidates, projectConfigWriteDir } from "../config.js";
779
779
  import { normalizePath, resolvePath } from "../utils/paths.js";
780
+ import { stripBom } from "../utils/text.js";
780
781
  import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.js";
781
- function isMergeableObject(value) {
782
- @@ -37,14 +37,112 @@ function parseTimeoutSetting(value, settingName) {
783
- }
784
- return undefined;
782
+ @@ -45,14 +45,112 @@ function toSettingsError(scope, error, path) {
783
+ error: error instanceof Error ? error : new Error(String(error)),
784
+ };
785
785
  }
786
786
  +/**
787
787
  + * Privateer patch: drop the keys of `settings` that are byte-identical to `base`.
@@ -893,7 +893,7 @@ index cb06c10..41fb90c 100644
893
893
  }
894
894
  acquireLockSyncWithRetry(path) {
895
895
  const maxAttempts = 10;
896
- @@ -71,7 +169,13 @@ export class FileSettingsStorage {
896
+ @@ -79,7 +177,13 @@ export class FileSettingsStorage {
897
897
  throw lastError ?? new Error("Failed to acquire settings lock");
898
898
  }
899
899
  withLock(scope, fn) {
@@ -908,7 +908,16 @@ index cb06c10..41fb90c 100644
908
908
  const dir = dirname(path);
909
909
  let release;
910
910
  try {
911
- @@ -568,7 +672,15 @@ export class SettingsManager {
911
+ @@ -157,7 +261,7 @@ export class SettingsManager {
912
+ const storage = new FileSettingsStorage(resolvedCwd, resolvedAgentDir);
913
+ return SettingsManager.fromStorageWithPaths(storage, options, {
914
+ global: join(resolvedAgentDir, "settings.json"),
915
+ - project: join(resolvedCwd, CONFIG_DIR_NAME, "settings.json"),
916
+ + project: join(projectConfigWriteDir(resolvedCwd), "settings.json"),
917
+ });
918
+ }
919
+ /** Create a SettingsManager from an arbitrary storage backend */
920
+ @@ -610,7 +714,15 @@ export class SettingsManager {
912
921
  getProviderRetrySettings() {
913
922
  return {
914
923
  timeoutMs: this.settings.retry?.provider?.timeoutMs,
@@ -926,7 +935,7 @@ index cb06c10..41fb90c 100644
926
935
  };
927
936
  }
928
937
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
929
- index 4e7e784..7383a2b 100644
938
+ index 56cf7a3..2a32170 100644
930
939
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
931
940
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
932
941
  @@ -1,7 +1,7 @@
@@ -938,7 +947,7 @@ index 4e7e784..7383a2b 100644
938
947
  import { parseFrontmatter } from "../utils/frontmatter.js";
939
948
  import { canonicalizePath, resolvePath } from "../utils/paths.js";
940
949
  import { createSyntheticSourceInfo } from "./source-info.js";
941
- @@ -328,10 +328,15 @@ export function loadSkills(options) {
950
+ @@ -346,10 +346,15 @@ export function loadSkills(options) {
942
951
  }
943
952
  if (includeDefaults) {
944
953
  addSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, "skills"), "user", true));
@@ -956,7 +965,7 @@ index 4e7e784..7383a2b 100644
956
965
  const isUnderPath = (target, root) => {
957
966
  const normalizedRoot = resolve(root);
958
967
  if (target === normalizedRoot) {
959
- @@ -344,7 +349,7 @@ export function loadSkills(options) {
968
+ @@ -362,7 +367,7 @@ export function loadSkills(options) {
960
969
  if (!includeDefaults) {
961
970
  if (isUnderPath(resolvedPath, userSkillsDir))
962
971
  return "user";
@@ -965,8 +974,121 @@ index 4e7e784..7383a2b 100644
965
974
  return "project";
966
975
  }
967
976
  return "path";
977
+ diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/output-accumulator.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/output-accumulator.js
978
+ index 7241668..7b79305 100644
979
+ --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/output-accumulator.js
980
+ +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/output-accumulator.js
981
+ @@ -10,6 +10,30 @@ function defaultTempFilePath(prefix) {
982
+ function byteLength(text) {
983
+ return Buffer.byteLength(text, "utf-8");
984
+ }
985
+ +// --- Privateer patch: per-line cap on the DISPLAY tail --------------------------
986
+ +//
987
+ +// Stock Pi bounds tool output two ways — 2000 lines and 50KB — but never bounds a
988
+ +// single LINE. One minified line is enough to spend the whole budget: a bare
989
+ +// `grep -rn <term> .` that wanders into node_modules matches inside
990
+ +// typescript.js or an Expo web bundle, and those "lines" are megabytes wide. The
991
+ +// observed shape is a 331MB capture whose entire visible output was 390 lines,
992
+ +// of which the model saw one — the tail of a single line of minified JS. The
993
+ +// context window pays 50KB for nothing, and the truncation notice reads as noise
994
+ +// because nothing about it says "your grep hit a bundle".
995
+ +//
996
+ +// So cap what goes into the display tail at one line's worth of characters and
997
+ +// mark the elision. Accounting (totalDecodedBytes, totalLines, currentLineBytes)
998
+ +// still measures the REAL stream, so "Showing lines X-Y of Z" stays honest, and
999
+ +// the temp file still receives every raw byte — `Full output:` means what it
1000
+ +// says. Only the bytes we hand the model change: 25+ real lines instead of one
1001
+ +// blob.
1002
+ +//
1003
+ +// 2000 rather than grep's 500 (GREP_MAX_LINE_LENGTH): a 2000-char line is already
1004
+ +// ~25 terminal rows, so ordinary output — stack traces, long JSON, compiler
1005
+ +// diagnostics — passes through untouched, while the pathological case is capped
1006
+ +// three orders of magnitude below where it hurts.
1007
+ +const DEFAULT_MAX_LINE_CHARS = 2000;
1008
+ +const LINE_ELISION = "... [line truncated]";
1009
+ /**
1010
+ * Incrementally tracks streaming output with bounded memory.
1011
+ *
1012
+ @@ -32,6 +56,11 @@ export class OutputAccumulator {
1013
+ completedLines = 0;
1014
+ totalLines = 0;
1015
+ currentLineBytes = 0;
1016
+ + // Privateer patch: per-line display cap, carried across chunk boundaries
1017
+ + // because a long line rarely arrives in one read().
1018
+ + maxLineChars;
1019
+ + lineCharsEmitted = 0;
1020
+ + lineElided = false;
1021
+ hasOpenLine = false;
1022
+ finished = false;
1023
+ tempFilePath;
1024
+ @@ -40,6 +69,8 @@ export class OutputAccumulator {
1025
+ this.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
1026
+ this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
1027
+ this.maxRollingBytes = Math.max(this.maxBytes * 2, 1);
1028
+ + // Privateer patch: 0 or less disables the cap.
1029
+ + this.maxLineChars = options.maxLineChars ?? DEFAULT_MAX_LINE_CHARS;
1030
+ this.tempFilePrefix = options.tempFilePrefix ?? "pi-output";
1031
+ }
1032
+ append(data) {
1033
+ @@ -122,8 +153,11 @@ export class OutputAccumulator {
1034
+ }
1035
+ const bytes = byteLength(text);
1036
+ this.totalDecodedBytes += bytes;
1037
+ - this.tailText += text;
1038
+ - this.tailBytes += bytes;
1039
+ + // Privateer patch: the tail is what the model and the TUI see, so it is the
1040
+ + // only thing capped. Every counter below still sees the full `text`.
1041
+ + const display = this.capLongLines(text);
1042
+ + this.tailText += display;
1043
+ + this.tailBytes += byteLength(display);
1044
+ if (this.tailBytes > this.maxRollingBytes * 2) {
1045
+ this.trimTail();
1046
+ }
1047
+ @@ -145,6 +179,42 @@ export class OutputAccumulator {
1048
+ }
1049
+ this.totalLines = this.completedLines + (this.hasOpenLine ? 1 : 0);
1050
+ }
1051
+ + /**
1052
+ + * Privateer patch: emit at most `maxLineChars` characters per line, followed by
1053
+ + * one elision marker, dropping the rest of that line until its newline.
1054
+ + *
1055
+ + * Streaming, so the per-line counters live on the instance: a 5MB line arrives
1056
+ + * as hundreds of chunks and every one of them must know the line is already
1057
+ + * spent.
1058
+ + */
1059
+ + capLongLines(text) {
1060
+ + if (this.maxLineChars <= 0) {
1061
+ + return text;
1062
+ + }
1063
+ + let out = "";
1064
+ + let i = 0;
1065
+ + while (i < text.length) {
1066
+ + const nl = text.indexOf("\n", i);
1067
+ + const segment = text.slice(i, nl === -1 ? text.length : nl);
1068
+ + const room = Math.max(this.maxLineChars - this.lineCharsEmitted, 0);
1069
+ + if (room > 0) {
1070
+ + out += segment.slice(0, room);
1071
+ + this.lineCharsEmitted += Math.min(segment.length, room);
1072
+ + }
1073
+ + if (segment.length > room && !this.lineElided) {
1074
+ + out += LINE_ELISION;
1075
+ + this.lineElided = true;
1076
+ + }
1077
+ + if (nl === -1) {
1078
+ + break;
1079
+ + }
1080
+ + out += "\n";
1081
+ + this.lineCharsEmitted = 0;
1082
+ + this.lineElided = false;
1083
+ + i = nl + 1;
1084
+ + }
1085
+ + return out;
1086
+ + }
1087
+ trimTail() {
1088
+ const buffer = Buffer.from(this.tailText, "utf-8");
1089
+ if (buffer.length <= this.maxRollingBytes) {
968
1090
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
969
- index 17af938..6d97caa 100644
1091
+ index 0877ee7..faa2559 100644
970
1092
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
971
1093
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
972
1094
  @@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -976,9 +1098,9 @@ index 17af938..6d97caa 100644
976
1098
  -import { CONFIG_DIR_NAME } from "../config.js";
977
1099
  +import { projectConfigDirCandidates } from "../config.js";
978
1100
  import { canonicalizePath, resolvePath } from "../utils/paths.js";
1101
+ import { stripBom } from "../utils/text.js";
979
1102
  const TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [
980
- "settings.json",
981
- @@ -150,9 +150,13 @@ export function hasTrustRequiringProjectResources(cwd) {
1103
+ @@ -151,9 +151,13 @@ export function hasTrustRequiringProjectResources(cwd) {
982
1104
  const homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));
983
1105
  const userAgentsSkillsDir = join(homeDir, ".agents", "skills");
984
1106
  let currentDir = canonicalizePath(resolvePath(cwd));
@@ -996,7 +1118,7 @@ index 17af938..6d97caa 100644
996
1118
  while (true) {
997
1119
  const agentsSkillsDir = join(currentDir, ".agents", "skills");
998
1120
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts b/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
999
- index 7d3e24c..578ada5 100644
1121
+ index 3e9241d..bfbfcbf 100644
1000
1122
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
1001
1123
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
1002
1124
  @@ -1,7 +1,7 @@
@@ -1007,9 +1129,9 @@ index 7d3e24c..578ada5 100644
1007
1129
  +export { AuthStorage, readStoredCredential } from "./core/auth-storage.ts";
1008
1130
  export { type BranchPreparation, type BranchSummaryResult, type CollectEntriesResult, type CompactionResult, type CutPointResult, calculateContextTokens, collectEntriesForBranchSummary, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, type FileOperations, findCutPoint, findTurnStartIndex, type GenerateBranchSummaryOptions, generateBranchSummary, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, shouldCompact, } from "./core/compaction/index.ts";
1009
1131
  export { createEventBus, type EventBus, type EventBusController } from "./core/event-bus.ts";
1010
- export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextUsage, CustomToolCallEvent, EditToolCallEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, GrepToolCallEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, MarkdownTransformContext, MarkdownTransformer, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, RegisteredCommand, RegisteredTool, ResolvedCommand, SessionBeforeCompactEvent, SessionBeforeForkEvent, SessionBeforeSwitchEvent, SessionBeforeTreeEvent, SessionCompactEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SlashCommandInfo, SlashCommandSource, SourceInfo, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, TurnEndEvent, TurnStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, } from "./core/extensions/index.ts";
1132
+ export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextUsage, CustomToolCallEvent, EditToolCallEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, GrepToolCallEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, MarkdownTransformContext, MarkdownTransformer, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, PowerShellToolCallEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, RegisteredCommand, RegisteredTool, ResolvedCommand, SessionBeforeCompactEvent, SessionBeforeForkEvent, SessionBeforeSwitchEvent, SessionBeforeTreeEvent, SessionCompactEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SlashCommandInfo, SlashCommandSource, SourceInfo, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, TurnEndEvent, TurnStartEvent, UIPromptEndEvent, UIPromptKind, UIPromptStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, } from "./core/extensions/index.ts";
1011
1133
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/index.js b/node_modules/@earendil-works/pi-coding-agent/dist/index.js
1012
- index 76d728e..2b2c892 100644
1134
+ index 01017cd..c2b8722 100644
1013
1135
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/index.js
1014
1136
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/index.js
1015
1137
  @@ -3,7 +3,7 @@ export { parseArgs } from "./cli/args.js";
@@ -1022,10 +1144,10 @@ index 76d728e..2b2c892 100644
1022
1144
  export { calculateContextTokens, collectEntriesForBranchSummary, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, findCutPoint, findTurnStartIndex, generateBranchSummary, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, shouldCompact, } from "./core/compaction/index.js";
1023
1145
  export { createEventBus } from "./core/event-bus.js";
1024
1146
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/main.js b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
1025
- index 5ff9c80..5b91d5f 100644
1147
+ index 940e0f4..c0f3d11 100644
1026
1148
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/main.js
1027
1149
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
1028
- @@ -786,7 +786,58 @@ export async function main(args, options) {
1150
+ @@ -795,7 +795,58 @@ export async function main(args, options) {
1029
1151
  if (exitCode !== 0) {
1030
1152
  process.exitCode = exitCode;
1031
1153
  }
@@ -1086,7 +1208,7 @@ index 5ff9c80..5b91d5f 100644
1086
1208
  //# sourceMappingURL=main.js.map
1087
1209
 
1088
1210
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
1089
- index e3a5ccf..a561a8a 100644
1211
+ index 072268a..472ee6d 100644
1090
1212
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
1091
1213
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
1092
1214
  @@ -4,7 +4,7 @@
@@ -1096,9 +1218,9 @@ index e3a5ccf..a561a8a 100644
1096
1218
  -import { CONFIG_DIR_NAME, getAgentDir, getBinDir } from "./config.js";
1097
1219
  +import { getAgentDir, getBinDir, projectConfigDirs } from "./config.js";
1098
1220
  import { migrateKeybindingsConfig } from "./core/keybindings.js";
1221
+ import { stripBom } from "./utils/text.js";
1099
1222
  const MIGRATION_GUIDE_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
1100
- const EXTENSIONS_DOC_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/docs/extensions.md";
1101
- @@ -230,14 +230,17 @@ function checkDeprecatedExtensionDirs(baseDir, label) {
1223
+ @@ -231,14 +231,17 @@ function checkDeprecatedExtensionDirs(baseDir, label) {
1102
1224
  */
1103
1225
  function migrateExtensionSystem(cwd) {
1104
1226
  const agentDir = getAgentDir();
@@ -1410,16 +1532,18 @@ index 1d9f046..c0326e3 100644
1410
1532
  return lines;
1411
1533
  }
1412
1534
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js
1413
- index 3f93cc6..50a2a99 100644
1535
+ index c33fc39..060a38f 100644
1414
1536
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js
1415
1537
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js
1416
- @@ -1,8 +1,35 @@
1538
+ @@ -1,10 +1,37 @@
1417
1539
  -import { Box, Container, getCapabilities, Image, Spacer, Text } from "@earendil-works/pi-tui";
1418
1540
  +import { Box, Container, getCapabilities, Image, Spacer, visibleWidth, Text } from "@earendil-works/pi-tui";
1419
1541
  import { createAllToolDefinitions } from "../../../core/tools/index.js";
1420
1542
  import { getTextOutput as getRenderedTextOutput } from "../../../core/tools/render-utils.js";
1421
1543
  import { convertToPng } from "../../../utils/image-convert.js";
1422
1544
  import { theme } from "../theme/theme.js";
1545
+ import { keyHint } from "./keybinding-hints.js";
1546
+ const FALLBACK_PREVIEW_LINES = 10;
1423
1547
  +/**
1424
1548
  + * Privateer: mark a failed tool call inline on its title line.
1425
1549
  + *
@@ -1450,7 +1574,7 @@ index 3f93cc6..50a2a99 100644
1450
1574
  export class ToolExecutionComponent extends Container {
1451
1575
  contentBox;
1452
1576
  contentText;
1453
- @@ -43,8 +70,13 @@ export class ToolExecutionComponent extends Container {
1577
+ @@ -45,8 +72,13 @@ export class ToolExecutionComponent extends Container {
1454
1578
  // Always create all shell variants. contentBox is used for default renderer-based composition.
1455
1579
  // selfRenderContainer is used when the tool renders its own framing.
1456
1580
  // contentText is reserved for generic fallback rendering when no tool definition exists.
@@ -1466,7 +1590,7 @@ index 3f93cc6..50a2a99 100644
1466
1590
  this.selfRenderContainer = new Container();
1467
1591
  if (this.hasRendererDefinition()) {
1468
1592
  this.addChild(this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox);
1469
- @@ -106,6 +138,13 @@ export class ToolExecutionComponent extends Container {
1593
+ @@ -108,6 +140,13 @@ export class ToolExecutionComponent extends Container {
1470
1594
  createCallFallback() {
1471
1595
  return new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0);
1472
1596
  }
@@ -1480,7 +1604,7 @@ index 3f93cc6..50a2a99 100644
1480
1604
  createResultFallback() {
1481
1605
  const output = this.getTextOutput();
1482
1606
  if (!output) {
1483
- @@ -218,19 +257,22 @@ export class ToolExecutionComponent extends Container {
1607
+ @@ -227,19 +266,22 @@ export class ToolExecutionComponent extends Container {
1484
1608
  renderContainer.clear();
1485
1609
  const callRenderer = this.getCallRenderer();
1486
1610
  if (!callRenderer) {
@@ -1506,7 +1630,7 @@ index 3f93cc6..50a2a99 100644
1506
1630
  hasContent = true;
1507
1631
  }
1508
1632
  }
1509
- @@ -302,7 +344,8 @@ export class ToolExecutionComponent extends Container {
1633
+ @@ -311,7 +353,8 @@ export class ToolExecutionComponent extends Container {
1510
1634
  return getRenderedTextOutput(this.result, this.showImages);
1511
1635
  }
1512
1636
  formatToolExecution() {
@@ -1517,19 +1641,19 @@ index 3f93cc6..50a2a99 100644
1517
1641
  if (content) {
1518
1642
  text += `\n\n${content}`;
1519
1643
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
1520
- index 42e655d..606c7cc 100644
1644
+ index a27754f..61a1fff 100644
1521
1645
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
1522
1646
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
1523
1647
  @@ -10,7 +10,7 @@ import * as TuiLayouts from "@earendil-works/pi-tui";
1524
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
1648
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
1525
1649
  import chalk from "chalk";
1526
- import { spawn, spawnSync } from "child_process";
1527
- -import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
1528
- +import { APP_NAME, APP_TITLE, formatProjectConfigDirNames, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
1650
+ import { spawn } from "child_process";
1651
+ -import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, VERSION, } from "../../config.js";
1652
+ +import { APP_NAME, APP_TITLE, formatProjectConfigDirNames, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, VERSION, } from "../../config.js";
1529
1653
  import { parseSkillBlock } from "../../core/agent-session.js";
1530
1654
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
1531
1655
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
1532
- @@ -119,7 +119,12 @@ export function formatResumeCommand(sessionManager) {
1656
+ @@ -126,7 +126,12 @@ export function formatResumeCommand(sessionManager) {
1533
1657
  const sessionFile = sessionManager.getSessionFile();
1534
1658
  if (!sessionFile || !fs.existsSync(sessionFile))
1535
1659
  return undefined;
@@ -1543,7 +1667,7 @@ index 42e655d..606c7cc 100644
1543
1667
  if (!sessionManager.usesDefaultSessionDir()) {
1544
1668
  args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
1545
1669
  }
1546
- @@ -207,6 +212,54 @@ export function createInteractiveTuiReference(getTui) {
1670
+ @@ -232,6 +237,54 @@ export function createInteractiveTuiReference(getTui) {
1547
1671
  getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
1548
1672
  });
1549
1673
  }
@@ -1598,7 +1722,7 @@ index 42e655d..606c7cc 100644
1598
1722
  export class InteractiveMode {
1599
1723
  runtimeHost;
1600
1724
  renderer;
1601
- @@ -405,9 +458,15 @@ export class InteractiveMode {
1725
+ @@ -439,9 +492,15 @@ export class InteractiveMode {
1602
1726
  }
1603
1727
  getBuiltInCommandConflictDiagnostics(extensionRunner) {
1604
1728
  const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
@@ -1615,7 +1739,7 @@ index 42e655d..606c7cc 100644
1615
1739
  .map((command) => ({
1616
1740
  type: "warning",
1617
1741
  message: command.invocationName === command.name
1618
- @@ -754,20 +813,18 @@ export class InteractiveMode {
1742
+ @@ -815,20 +874,18 @@ export class InteractiveMode {
1619
1743
  this.showNewVersionNotification(newRelease);
1620
1744
  }
1621
1745
  });
@@ -1648,7 +1772,7 @@ index 42e655d..606c7cc 100644
1648
1772
  // Check tmux keyboard setup asynchronously
1649
1773
  this.checkTmuxKeyboardSetup().then((warning) => {
1650
1774
  if (warning) {
1651
- @@ -2310,7 +2367,17 @@ export class InteractiveMode {
1775
+ @@ -2393,7 +2450,17 @@ export class InteractiveMode {
1652
1776
  if (text === "/model" || text.startsWith("/model ")) {
1653
1777
  const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
1654
1778
  this.editor.setText("");
@@ -1666,8 +1790,8 @@ index 42e655d..606c7cc 100644
1666
1790
  + }
1667
1791
  return;
1668
1792
  }
1669
- if (text === "/export" || text.startsWith("/export ")) {
1670
- @@ -2376,12 +2443,42 @@ export class InteractiveMode {
1793
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
1794
+ @@ -2465,12 +2532,42 @@ export class InteractiveMode {
1671
1795
  if (text === "/login" || text.startsWith("/login ")) {
1672
1796
  const providerRef = text.startsWith("/login ") ? text.slice(7).trim() : undefined;
1673
1797
  this.editor.setText("");
@@ -1712,7 +1836,7 @@ index 42e655d..606c7cc 100644
1712
1836
  return;
1713
1837
  }
1714
1838
  if (text === "/new") {
1715
- @@ -3024,7 +3121,7 @@ export class InteractiveMode {
1839
+ @@ -3164,7 +3261,7 @@ export class InteractiveMode {
1716
1840
  if (this.chatContainer.children.length > 0) {
1717
1841
  this.chatContainer.addChild(new Spacer(1));
1718
1842
  }
@@ -1721,7 +1845,7 @@ index 42e655d..606c7cc 100644
1721
1845
  }
1722
1846
  async getUserInput() {
1723
1847
  const queuedInput = this.pendingUserInputs.shift();
1724
- @@ -3363,7 +3460,17 @@ export class InteractiveMode {
1848
+ @@ -3504,7 +3601,17 @@ export class InteractiveMode {
1725
1849
  }
1726
1850
  showError(errorMessage) {
1727
1851
  this.chatContainer.addChild(new Spacer(1));
@@ -1741,10 +1865,10 @@ index 42e655d..606c7cc 100644
1741
1865
  }
1742
1866
  showWarning(warningMessage) {
1743
1867
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json
1744
- index 9db9cbd..b370180 100644
1868
+ index 01d1e02..c591e1d 100644
1745
1869
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json
1746
1870
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json
1747
- @@ -39,9 +39,9 @@
1871
+ @@ -41,9 +41,9 @@
1748
1872
  "customMessageBg": "customMsgBg",
1749
1873
  "customMessageText": "text",
1750
1874
  "customMessageLabel": "#9575cd",
@@ -1758,10 +1882,10 @@ index 9db9cbd..b370180 100644
1758
1882
  "toolOutput": "gray",
1759
1883
 
1760
1884
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json
1761
- index 74ef3d1..709a21d 100644
1885
+ index 0fde42b..cc3b7c5 100644
1762
1886
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json
1763
1887
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json
1764
- @@ -38,9 +38,9 @@
1888
+ @@ -40,9 +40,9 @@
1765
1889
  "customMessageBg": "customMsgBg",
1766
1890
  "customMessageText": "text",
1767
1891
  "customMessageLabel": "#7e57c2",
@@ -1775,11 +1899,11 @@ index 74ef3d1..709a21d 100644
1775
1899
  "toolOutput": "mediumGray",
1776
1900
 
1777
1901
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js b/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
1778
- index 4230b18..f47e80f 100644
1902
+ index 3f101e6..51c7f73 100644
1779
1903
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
1780
1904
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
1781
- @@ -3,7 +3,7 @@ import { Markdown } from "@earendil-works/pi-tui";
1782
- import chalk from "chalk";
1905
+ @@ -5,7 +5,7 @@ import chalk from "chalk";
1906
+ import lockfile from "proper-lockfile";
1783
1907
  import { selectConfig } from "./cli/config-selector.js";
1784
1908
  import { createProjectTrustContext } from "./cli/project-trust.js";
1785
1909
  -import { APP_NAME, CONFIG_DIR_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
@@ -1787,7 +1911,7 @@ index 4230b18..f47e80f 100644
1787
1911
  import { ModelRuntime } from "./core/model-runtime.js";
1788
1912
  import { DefaultPackageManager } from "./core/package-manager.js";
1789
1913
  import { resolveProjectTrusted } from "./core/project-trust.js";
1790
- @@ -60,7 +60,7 @@ Without -l, starts in global settings (~/${CONFIG_DIR_NAME}/agent/settings.json)
1914
+ @@ -221,7 +221,7 @@ Without -l, starts in global settings (~/${CONFIG_DIR_NAME}/agent/settings.json)
1791
1915
  Press Tab in the TUI to switch between global and project-local modes.
1792
1916
 
1793
1917
  Options:
@@ -1796,7 +1920,7 @@ index 4230b18..f47e80f 100644
1796
1920
  -a, --approve Trust project-local files for this command with -l
1797
1921
  -na, --no-approve Ignore project-local files for this command with -l
1798
1922
  `);
1799
- @@ -74,7 +74,7 @@ function printPackageCommandHelp(command) {
1923
+ @@ -235,7 +235,7 @@ function printPackageCommandHelp(command) {
1800
1924
  Install a package and add it to settings.
1801
1925
 
1802
1926
  Options:
@@ -1805,7 +1929,7 @@ index 4230b18..f47e80f 100644
1805
1929
  -a, --approve Trust project-local files for this command
1806
1930
  -na, --no-approve Ignore project-local files for this command
1807
1931
 
1808
- @@ -95,7 +95,7 @@ Remove a package and its source from settings.
1932
+ @@ -256,7 +256,7 @@ Remove a package and its source from settings.
1809
1933
  Alias: ${APP_NAME} uninstall <source> [-l]
1810
1934
 
1811
1935
  Options:
@@ -1,28 +1,31 @@
1
1
  {
2
2
  "$comment": "The one list of extensions Privateer ships. Read by bin/privateer-launch.mjs (shim install, dependency-free, pre-patch) and src/config/moatManifest.ts (RESERVED + profile composition). JSON so both a plain .mjs and the TS side can read it without a build step. See moatManifest.ts for the field contract.",
3
- "shims": [
4
- { "name": "privateer-brand", "entry": "extensions/privateer-brand.ts", "note": "banner, ⚓ badge, /signin /signout" },
5
- { "name": "privateer-context", "entry": "extensions/privateer-context.ts", "note": "PRIVATEER.md context + /init" },
6
- { "name": "privateer-gate", "entry": "extensions/privateer-gate.ts", "note": "the permission gate (moat)" },
7
- { "name": "privateer-account", "entry": "extensions/privateer-account.ts", "note": "account inference provider" },
8
- { "name": "privateer-models", "entry": "extensions/privateer-models.ts", "note": "/models picker w/ privacy shields" },
9
- { "name": "privateer-posture", "entry": "extensions/privateer-posture.ts", "note": "live attestation shield" },
10
- { "name": "privateer-tools", "entry": "extensions/privateer-tools.ts", "note": "Privateer tool pack" },
11
- { "name": "privateer-privacy", "entry": "extensions/privateer-privacy.ts", "note": "pi-privacy + account tier resolver" },
12
- { "name": "privateer-connect", "entry": "extensions/privateer-connect.ts", "note": "/connect — MCP connector manager" },
13
- { "name": "privateer-media", "entry": "extensions/privateer-media.ts", "note": "image/video/speech/music + ffmpeg compose" },
14
- { "name": "privateer-desktop", "entry": "extensions/privateer-desktop.ts", "note": "/desktop — open the Privateer desktop app" },
15
- { "name": "privateer-hints", "entry": "extensions/privateer-hints.ts", "note": "rotating tips in the working line + /hints" },
16
- { "name": "privateer-update", "entry": "extensions/privateer-update.ts", "note": "tool pack updates in place — banner flag + /update" },
17
- { "name": "privateer-speak", "entry": "extensions/privateer-speak.ts", "note": "spoken responses (/speak) + voice input (/talk) — pi-speak + confidential account TTS/STT" },
18
- { "name": "privateer-web", "entry": "extensions/privateer-web.ts", "note": "web_search/web_fetch — account search when signed in, else the user's own provider (rpiv-web-tools)" },
19
- { "name": "rpiv-ask-user-question", "dep": ["@juicesharp/rpiv-ask-user-question", "index.ts"], "note": "ask_user_question" },
20
- { "name": "pi-mcp-adapter", "dep": ["pi-mcp-adapter", "index.ts"], "note": "MCP servers as first-class tools" },
21
- { "name": "pi-subagents", "dep": ["pi-subagents", "src", "extension", "index.ts"], "note": "bounded parallel sub-agents" }
22
- ],
23
- "retired": ["pi-privacy", "pi-web-access", "pi-hypa", "rpiv-web-tools"],
24
3
  "reservedAliases": [
25
4
  "@juicesharp/rpiv-web-tools",
26
- "@juicesharp/rpiv-ask-user-question"
5
+ "@juicesharp/rpiv-ask-user-question",
6
+ "background-tasks"
7
+ ],
8
+ "retired": ["pi-privacy", "pi-web-access", "pi-hypa", "rpiv-web-tools"],
9
+ "shims": [
10
+ { "entry": "extensions/privateer-brand.ts", "name": "privateer-brand", "note": "banner, ⚓ badge, /signin /signout" },
11
+ { "entry": "extensions/privateer-context.ts", "name": "privateer-context", "note": "PRIVATEER.md context + /init" },
12
+ { "entry": "extensions/privateer-gate.ts", "name": "privateer-gate", "note": "the permission gate (moat)" },
13
+ { "entry": "extensions/privateer-account.ts", "name": "privateer-account", "note": "account inference provider" },
14
+ { "entry": "extensions/privateer-models.ts", "name": "privateer-models", "note": "/models picker w/ privacy shields" },
15
+ { "entry": "extensions/privateer-posture.ts", "name": "privateer-posture", "note": "live attestation shield" },
16
+ { "entry": "extensions/privateer-tools.ts", "name": "privateer-tools", "note": "Privateer tool pack" },
17
+ { "entry": "extensions/privateer-privacy.ts", "name": "privateer-privacy", "note": "pi-privacy + account tier resolver" },
18
+ { "entry": "extensions/privateer-connect.ts", "name": "privateer-connect", "note": "/connect — MCP connector manager" },
19
+ { "entry": "extensions/privateer-media.ts", "name": "privateer-media", "note": "image/video/speech/music + ffmpeg compose" },
20
+ { "entry": "extensions/privateer-desktop.ts", "name": "privateer-desktop", "note": "/desktop — open the Privateer desktop app" },
21
+ { "entry": "extensions/privateer-hints.ts", "name": "privateer-hints", "note": "rotating tips in the working line + /hints" },
22
+ { "entry": "extensions/privateer-update.ts", "name": "privateer-update", "note": "tool pack updates in place — banner flag + /update" },
23
+ { "entry": "extensions/privateer-speak.ts", "name": "privateer-speak", "note": "spoken responses (/speak) + voice input (/talk) — pi-speak + confidential account TTS/STT" },
24
+ { "entry": "extensions/privateer-web.ts", "name": "privateer-web", "note": "web_search/web_fetch — account search when signed in, else the user's own provider (rpiv-web-tools)" },
25
+ { "dep": ["@juicesharp/rpiv-ask-user-question", "index.ts"], "name": "rpiv-ask-user-question", "note": "ask_user_question" },
26
+ { "dep": ["pi-mcp-adapter", "index.ts"], "name": "pi-mcp-adapter", "note": "MCP servers as first-class tools" },
27
+ { "dep": ["pi-subagents", "src", "extension", "index.ts"], "name": "pi-subagents", "note": "bounded parallel sub-agents" },
28
+ { "dep": ["pi-background-tasks", "extensions", "anthropic-attribution.ts"], "name": "anthropic-attribution", "note": "Anthropic OAuth attribution and prompt sanitization" },
29
+ { "dep": ["pi-background-tasks", "extensions", "background-tasks.ts"], "name": "pi-background-tasks", "note": "durable background tasks, delegated agents, and fusion" }
27
30
  ]
28
31
  }