privateer-agent 0.12.8 → 0.12.10

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.
@@ -68,12 +68,12 @@ 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 e223ce1..cc853e7 100644
71
+ index ce8a9a2..1ae7b25 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
- @@ -163,6 +163,14 @@ export class AgentSession {
74
+ @@ -186,6 +186,14 @@ export class AgentSession {
75
75
  }
76
- const isOAuth = this._modelRegistry.isUsingOAuth(model);
76
+ const isOAuth = this._modelRuntime.isUsingOAuth(model.provider);
77
77
  if (isOAuth) {
78
78
  + // Privateer patch: the account channel has no API key that could expire —
79
79
  + // the terminal is simply not signed in yet (a fresh install now boots on
@@ -86,7 +86,7 @@ index e223ce1..cc853e7 100644
86
86
  throw new Error(`Authentication failed for "${model.provider}". ` +
87
87
  `Credentials may have expired or network is unavailable. ` +
88
88
  `Run '/login ${model.provider}' to re-authenticate.`);
89
- @@ -711,6 +719,15 @@ export class AgentSession {
89
+ @@ -772,6 +780,15 @@ export class AgentSession {
90
90
  finalError: msg.errorMessage,
91
91
  });
92
92
  this._retryAttempt = 0;
@@ -102,7 +102,22 @@ index e223ce1..cc853e7 100644
102
102
  }
103
103
  if (await this._checkCompaction(msg)) {
104
104
  return true;
105
- @@ -1996,6 +2013,18 @@ export class AgentSession {
105
+ @@ -852,6 +869,14 @@ export class AgentSession {
106
+ if (!hasConfiguredAuth) {
107
+ const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
108
+ if (isOAuth) {
109
+ + // Privateer patch: the account channel has no API key that could expire —
110
+ + // the terminal is simply not signed in yet (a fresh install now boots on
111
+ + // `privateer/*`, which is the model it will run once logged in). Stock Pi's
112
+ + // wording describes a state that user was never in, and its "/login
113
+ + // privateer" bypasses the branded sign-in. auth-guidance owns the words.
114
+ + if (this.model.provider === "privateer") {
115
+ + throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
116
+ + }
117
+ throw new Error(`Authentication failed for "${this.model.provider}". ` +
118
+ `Credentials may have expired or network is unavailable. ` +
119
+ `Run '/login ${this.model.provider}' to re-authenticate.`);
120
+ @@ -2084,6 +2109,18 @@ export class AgentSession {
106
121
  // Context overflow is handled by compaction, not retry.
107
122
  if (isContextOverflow(message, this.model?.contextWindow ?? 0))
108
123
  return false;
@@ -167,10 +182,10 @@ index 197bccc..bc9ac3f 100644
167
182
  //# sourceMappingURL=auth-guidance.js.map
168
183
 
169
184
  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
170
- index 4b79482..a81b452 100644
185
+ index 324d30a..e613e92 100644
171
186
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
172
187
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
173
- @@ -17,7 +17,7 @@ import { createJiti } from "jiti/static";
188
+ @@ -18,7 +18,7 @@ import { createJiti } from "jiti/static";
174
189
  import * as _bundledTypebox from "typebox";
175
190
  import * as _bundledTypeboxCompile from "typebox/compile";
176
191
  import * as _bundledTypeboxValue from "typebox/value";
@@ -179,7 +194,7 @@ index 4b79482..a81b452 100644
179
194
  // NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
180
195
  // avoiding a circular dependency. Extensions can import from @earendil-works/pi-coding-agent.
181
196
  import * as _bundledPiCodingAgent from "../../index.js";
182
- @@ -508,9 +508,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
197
+ @@ -554,9 +554,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
183
198
  }
184
199
  }
185
200
  };
@@ -207,7 +222,7 @@ index 4b79482..a81b452 100644
207
222
  const globalExtDir = path.join(resolvedAgentDir, "extensions");
208
223
  addPaths(discoverExtensionsInDir(globalExtDir));
209
224
  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
210
- index a125c9a..5ca0ccc 100644
225
+ index e806af3..9b00ebd 100644
211
226
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
212
227
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
213
228
  @@ -25,7 +25,7 @@ import { globSync } from "glob";
@@ -219,7 +234,7 @@ index a125c9a..5ca0ccc 100644
219
234
  import { spawnProcess, spawnProcessSync } from "../utils/child-process.js";
220
235
  import { parseGitUrl } from "../utils/git.js";
221
236
  import { canonicalizePath, isLocalPath, markPathIgnoredByCloudSync, resolvePath } from "../utils/paths.js";
222
- @@ -693,23 +693,29 @@ export class DefaultPackageManager {
237
+ @@ -698,23 +698,29 @@ export class DefaultPackageManager {
223
238
  const packageSources = this.dedupePackages(allPackages);
224
239
  await this.resolvePackageSources(packageSources, accumulator, onMissing);
225
240
  const globalBaseDir = this.agentDir;
@@ -256,7 +271,7 @@ index a125c9a..5ca0ccc 100644
256
271
  return this.toResolvedPaths(accumulator);
257
272
  }
258
273
  async resolveExtensionSources(sources, options) {
259
- @@ -1586,13 +1592,30 @@ export class DefaultPackageManager {
274
+ @@ -1668,13 +1674,30 @@ export class DefaultPackageManager {
260
275
  writeFileSync(ignorePath, "*\n!.gitignore\n", "utf-8");
261
276
  }
262
277
  }
@@ -288,7 +303,7 @@ index a125c9a..5ca0ccc 100644
288
303
  }
289
304
  return join(this.agentDir, "npm");
290
305
  }
291
- @@ -1631,7 +1654,9 @@ export class DefaultPackageManager {
306
+ @@ -1713,7 +1736,9 @@ export class DefaultPackageManager {
292
307
  }
293
308
  if (scope === "project") {
294
309
  this.assertProjectTrustedForScope(scope);
@@ -299,7 +314,7 @@ index a125c9a..5ca0ccc 100644
299
314
  }
300
315
  return join(this.agentDir, "npm", "node_modules", source.name);
301
316
  }
302
- @@ -1667,7 +1692,7 @@ export class DefaultPackageManager {
317
+ @@ -1749,7 +1774,7 @@ export class DefaultPackageManager {
303
318
  }
304
319
  if (scope === "project") {
305
320
  this.assertProjectTrustedForScope(scope);
@@ -308,7 +323,7 @@ index a125c9a..5ca0ccc 100644
308
323
  }
309
324
  return join(this.agentDir, "git");
310
325
  }
311
- @@ -1690,7 +1715,7 @@ export class DefaultPackageManager {
326
+ @@ -1772,7 +1797,7 @@ export class DefaultPackageManager {
312
327
  getBaseDirForScope(scope) {
313
328
  if (scope === "project") {
314
329
  this.assertProjectTrustedForScope(scope);
@@ -317,7 +332,7 @@ index a125c9a..5ca0ccc 100644
317
332
  }
318
333
  if (scope === "user") {
319
334
  return this.agentDir;
320
- @@ -1701,7 +1726,30 @@ export class DefaultPackageManager {
335
+ @@ -1783,7 +1808,30 @@ export class DefaultPackageManager {
321
336
  return resolvePath(input, this.cwd, { homeDir: getHomeDir(), trim: true });
322
337
  }
323
338
  resolvePathFromBase(input, baseDir) {
@@ -349,7 +364,7 @@ index a125c9a..5ca0ccc 100644
349
364
  }
350
365
  collectPackageResources(packageRoot, accumulator, filter, metadata) {
351
366
  if (filter) {
352
- @@ -1847,19 +1895,24 @@ export class DefaultPackageManager {
367
+ @@ -1928,19 +1976,24 @@ export class DefaultPackageManager {
353
368
  this.addResource(target, f, metadata, enabledPaths.has(f));
354
369
  }
355
370
  }
@@ -378,7 +393,7 @@ index a125c9a..5ca0ccc 100644
378
393
  const userOverrides = {
379
394
  extensions: (globalSettings.extensions ?? []),
380
395
  skills: (globalSettings.skills ?? []),
381
- @@ -1878,12 +1931,12 @@ export class DefaultPackageManager {
396
+ @@ -1959,12 +2012,12 @@ export class DefaultPackageManager {
382
397
  prompts: join(globalBaseDir, "prompts"),
383
398
  themes: join(globalBaseDir, "themes"),
384
399
  };
@@ -397,7 +412,7 @@ index a125c9a..5ca0ccc 100644
397
412
  const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
398
413
  const projectTrusted = this.settingsManager.isProjectTrusted();
399
414
  const projectAgentsSkillDirs = projectTrusted
400
- @@ -1897,10 +1950,13 @@ export class DefaultPackageManager {
415
+ @@ -1978,10 +2031,13 @@ export class DefaultPackageManager {
401
416
  }
402
417
  };
403
418
  if (projectTrusted) {
@@ -415,7 +430,7 @@ index a125c9a..5ca0ccc 100644
415
430
  }
416
431
  // Project skills from .agents/ (each with its own baseDir)
417
432
  for (const agentsSkillsDir of projectAgentsSkillDirs) {
418
- @@ -1912,8 +1968,11 @@ export class DefaultPackageManager {
433
+ @@ -1993,8 +2049,11 @@ export class DefaultPackageManager {
419
434
  addResources("skills", collectAutoSkillEntries(agentsSkillsDir, "agents"), agentsMetadata, projectOverrides.skills, agentsBaseDir);
420
435
  }
421
436
  if (projectTrusted) {
@@ -445,7 +460,7 @@ index c4f2509..a4402df 100644
445
460
  async function selectProjectTrustOption(cwd, ctx) {
446
461
  const options = getProjectTrustOptions(cwd, { includeSessionOnly: true });
447
462
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
448
- index ae4a7d4..a251c37 100644
463
+ index 3700c08..79cb85e 100644
449
464
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
450
465
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
451
466
  @@ -1,6 +1,6 @@
@@ -488,19 +503,19 @@ index ae4a7d4..a251c37 100644
488
503
  // 3. Load explicit prompt paths
489
504
  for (const rawPath of promptPaths) {
490
505
  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
491
- index 74e36fe..7dfef45 100644
506
+ index 97af6cd..74d5d52 100644
492
507
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
493
508
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
494
509
  @@ -1,7 +1,7 @@
495
510
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
496
- import { join, resolve, sep } from "node:path";
511
+ import { basename, dirname, join, resolve, sep } from "node:path";
497
512
  import chalk from "chalk";
498
513
  -import { CONFIG_DIR_NAME } from "../config.js";
499
514
  +import { findProjectConfigPath, projectConfigDirCandidates, projectConfigDirs } from "../config.js";
500
515
  import { loadThemeFromPath } from "../modes/interactive/theme/theme.js";
501
516
  import { canonicalizePath, isLocalPath, resolvePath } from "../utils/paths.js";
502
517
  import { createEventBus } from "./event-bus.js";
503
- @@ -570,12 +570,14 @@ export class DefaultResourceLoader {
518
+ @@ -624,12 +624,14 @@ export class DefaultResourceLoader {
504
519
  join(this.agentDir, "themes"),
505
520
  join(this.agentDir, "extensions"),
506
521
  ];
@@ -521,7 +536,7 @@ index 74e36fe..7dfef45 100644
521
536
  for (const root of agentRoots) {
522
537
  if (this.isUnderPath(normalizedPath, root)) {
523
538
  return { path: filePath, source: "local", scope: "user", origin: "top-level", baseDir: root };
524
- @@ -614,7 +616,11 @@ export class DefaultResourceLoader {
539
+ @@ -668,7 +670,11 @@ export class DefaultResourceLoader {
525
540
  const themes = [];
526
541
  const diagnostics = [];
527
542
  if (includeDefaults) {
@@ -534,7 +549,7 @@ index 74e36fe..7dfef45 100644
534
549
  for (const dir of defaultDirs) {
535
550
  this.loadThemesFromDir(dir, themes, diagnostics);
536
551
  }
537
- @@ -749,8 +755,9 @@ export class DefaultResourceLoader {
552
+ @@ -806,8 +812,9 @@ export class DefaultResourceLoader {
538
553
  return { themes: Array.from(seen.values()), diagnostics };
539
554
  }
540
555
  discoverSystemPromptFile() {
@@ -546,7 +561,7 @@ index 74e36fe..7dfef45 100644
546
561
  return projectPath;
547
562
  }
548
563
  const globalPath = join(this.agentDir, "SYSTEM.md");
549
- @@ -760,8 +767,9 @@ export class DefaultResourceLoader {
564
+ @@ -817,8 +824,9 @@ export class DefaultResourceLoader {
550
565
  return undefined;
551
566
  }
552
567
  discoverAppendSystemPromptFile() {
@@ -559,7 +574,7 @@ index 74e36fe..7dfef45 100644
559
574
  }
560
575
  const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
561
576
  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
562
- index 5910aac..f379c0f 100644
577
+ index cb06c10..930a0de 100644
563
578
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
564
579
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
565
580
  @@ -2,7 +2,7 @@ import { randomUUID } from "crypto";
@@ -570,8 +585,8 @@ index 5910aac..f379c0f 100644
570
585
  +import { getAgentDir, projectConfigDirCandidates, projectConfigWriteDir } from "../config.js";
571
586
  import { normalizePath, resolvePath } from "../utils/paths.js";
572
587
  import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.js";
573
- /** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
574
- @@ -40,14 +40,112 @@ function parseTimeoutSetting(value, settingName) {
588
+ function isMergeableObject(value) {
589
+ @@ -37,14 +37,112 @@ function parseTimeoutSetting(value, settingName) {
575
590
  }
576
591
  return undefined;
577
592
  }
@@ -685,7 +700,7 @@ index 5910aac..f379c0f 100644
685
700
  }
686
701
  acquireLockSyncWithRetry(path) {
687
702
  const maxAttempts = 10;
688
- @@ -74,7 +172,13 @@ export class FileSettingsStorage {
703
+ @@ -71,7 +169,13 @@ export class FileSettingsStorage {
689
704
  throw lastError ?? new Error("Failed to acquire settings lock");
690
705
  }
691
706
  withLock(scope, fn) {
@@ -770,11 +785,37 @@ index 17af938..6d97caa 100644
770
785
  }
771
786
  while (true) {
772
787
  const agentsSkillsDir = join(currentDir, ".agents", "skills");
788
+ 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
789
+ index 7d3e24c..578ada5 100644
790
+ --- a/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
791
+ +++ b/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
792
+ @@ -1,7 +1,7 @@
793
+ export { type Args, parseArgs } from "./cli/args.ts";
794
+ export { CONFIG_DIR_NAME, getAgentDir, getDocsPath, getExamplesPath, getPackageDir, getReadmePath, VERSION, } from "./config.ts";
795
+ export { AgentSession, type AgentSessionConfig, type AgentSessionEvent, type AgentSessionEventListener, type ModelCycleResult, type ParsedSkillBlock, type PromptOptions, parseSkillBlock, type SessionStats, } from "./core/agent-session.ts";
796
+ -export { readStoredCredential } from "./core/auth-storage.ts";
797
+ +export { AuthStorage, readStoredCredential } from "./core/auth-storage.ts";
798
+ 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";
799
+ export { createEventBus, type EventBus, type EventBusController } from "./core/event-bus.ts";
800
+ 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";
801
+ diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/index.js b/node_modules/@earendil-works/pi-coding-agent/dist/index.js
802
+ index 76d728e..2b2c892 100644
803
+ --- a/node_modules/@earendil-works/pi-coding-agent/dist/index.js
804
+ +++ b/node_modules/@earendil-works/pi-coding-agent/dist/index.js
805
+ @@ -3,7 +3,7 @@ export { parseArgs } from "./cli/args.js";
806
+ // Config paths
807
+ export { CONFIG_DIR_NAME, getAgentDir, getDocsPath, getExamplesPath, getPackageDir, getReadmePath, VERSION, } from "./config.js";
808
+ export { AgentSession, parseSkillBlock, } from "./core/agent-session.js";
809
+ -export { readStoredCredential } from "./core/auth-storage.js";
810
+ +export { AuthStorage, readStoredCredential } from "./core/auth-storage.js";
811
+ // Compaction
812
+ export { calculateContextTokens, collectEntriesForBranchSummary, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, findCutPoint, findTurnStartIndex, generateBranchSummary, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, shouldCompact, } from "./core/compaction/index.js";
813
+ export { createEventBus } from "./core/event-bus.js";
773
814
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/main.js b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
774
- index fc62585..8b08615 100644
815
+ index 5ff9c80..5b91d5f 100644
775
816
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/main.js
776
817
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
777
- @@ -693,7 +693,58 @@ export async function main(args, options) {
818
+ @@ -786,7 +786,58 @@ export async function main(args, options) {
778
819
  if (exitCode !== 0) {
779
820
  process.exitCode = exitCode;
780
821
  }
@@ -869,7 +910,7 @@ index e3a5ccf..a561a8a 100644
869
910
  return warnings;
870
911
  }
871
912
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
872
- index 3355a47..8c6bba9 100644
913
+ index 6f7ed6a..b0ed0ae 100644
873
914
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
874
915
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
875
916
  @@ -4,7 +4,7 @@
@@ -877,11 +918,11 @@ index 3355a47..8c6bba9 100644
877
918
  import { basename, dirname, join, relative } from "node:path";
878
919
  import { Container, getKeybindings, Input, matchesKey, Spacer, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
879
920
  -import { CONFIG_DIR_NAME } from "../../../config.js";
880
- +import { formatProjectConfigDirNames, projectConfigDirs, projectConfigWriteDir } from "../../../config.js";
921
+ +import { CONFIG_DIR_NAME, PROJECT_CONFIG_DIR_NAMES, formatProjectConfigDirNames, projectConfigDirs, projectConfigWriteDir } from "../../../config.js";
922
+ import { canonicalizePath, isLocalPath, resolvePath } from "../../../utils/paths.js";
881
923
  import { theme } from "../theme/theme.js";
882
924
  import { DynamicBorder } from "./dynamic-border.js";
883
- import { rawKeyHint } from "./keybinding-hints.js";
884
- @@ -41,7 +41,7 @@ function getGroupLabel(metadata, agentDir) {
925
+ @@ -43,7 +43,7 @@ function getGroupLabel(metadata, agentDir) {
885
926
  ? `User (${formatBaseDir(metadata.baseDir)})`
886
927
  : `Project (${formatBaseDir(metadata.baseDir)})`;
887
928
  }
@@ -890,8 +931,17 @@ index 3355a47..8c6bba9 100644
890
931
  }
891
932
  return metadata.scope === "user" ? "User settings" : "Project settings";
892
933
  }
893
- @@ -458,7 +458,12 @@ class ResourceList {
894
- }
934
+ @@ -141,7 +141,7 @@ class ConfigSelectorHeader {
935
+ const hint = switchHint + actionHint + sep + rawKeyHint("esc", "close");
936
+ const spacing = Math.max(1, width - visibleWidth(title) - visibleWidth(hint));
937
+ const scopeHint = this.writeScope === "project"
938
+ - ? theme.fg("muted", `${CONFIG_DIR_NAME}/settings.json · inherited global resources are dimmed`)
939
+ + ? theme.fg("muted", `${PROJECT_CONFIG_DIR_NAMES[0]}/settings.json · inherited global resources are dimmed`)
940
+ : theme.fg("muted", `~/${CONFIG_DIR_NAME}/agent/settings.json`);
941
+ return [
942
+ truncateToWidth(`${title}${" ".repeat(spacing)}${hint}`, width, ""),
943
+ @@ -695,7 +695,12 @@ class ResourceList {
944
+ return item.metadata.scope === "project" ? "project" : "user";
895
945
  }
896
946
  getTopLevelBaseDir(scope) {
897
947
  - return scope === "project" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;
@@ -905,7 +955,7 @@ index 3355a47..8c6bba9 100644
905
955
  getResourcePattern(item) {
906
956
  const scope = item.metadata.scope;
907
957
  diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
908
- index 68d0308..f7208f1 100644
958
+ index 1d9f046..2a1fba8 100644
909
959
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
910
960
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
911
961
  @@ -1,5 +1,5 @@
@@ -913,9 +963,9 @@ index 68d0308..f7208f1 100644
913
963
  -import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
914
964
  +import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
915
965
  import { areExperimentalFeaturesEnabled } from "../../../core/experimental.js";
966
+ import { addUsageToTotals, createUsageTotals } from "../../../core/usage-totals.js";
916
967
  import { theme } from "../theme/theme.js";
917
- /**
918
- @@ -13,6 +13,43 @@ function sanitizeStatusText(text) {
968
+ @@ -14,6 +14,43 @@ function sanitizeStatusText(text) {
919
969
  .replace(/ +/g, " ")
920
970
  .trim();
921
971
  }
@@ -959,7 +1009,7 @@ index 68d0308..f7208f1 100644
959
1009
  /**
960
1010
  * Format token counts for compact footer display.
961
1011
  */
962
- @@ -211,9 +248,9 @@ export class FooterComponent {
1012
+ @@ -212,9 +249,9 @@ export class FooterComponent {
963
1013
  const sortedStatuses = Array.from(extensionStatuses.entries())
964
1014
  .sort(([a], [b]) => a.localeCompare(b))
965
1015
  .map(([, text]) => sanitizeStatusText(text));
@@ -973,19 +1023,19 @@ index 68d0308..f7208f1 100644
973
1023
  return lines;
974
1024
  }
975
1025
  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
976
- index 5d65200..397a7b7 100644
1026
+ index 42e655d..d00bec9 100644
977
1027
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
978
1028
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
979
- @@ -10,7 +10,7 @@ import { getProviders, } from "@earendil-works/pi-ai/compat";
980
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
1029
+ @@ -10,7 +10,7 @@ import * as TuiLayouts from "@earendil-works/pi-tui";
1030
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
981
1031
  import chalk from "chalk";
982
1032
  import { spawn, spawnSync } from "child_process";
983
1033
  -import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
984
1034
  +import { APP_NAME, APP_TITLE, formatProjectConfigDirNames, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
985
1035
  import { parseSkillBlock } from "../../core/agent-session.js";
986
1036
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
987
- import { FooterDataProvider } from "../../core/footer-data-provider.js";
988
- @@ -110,7 +110,12 @@ export function formatResumeCommand(sessionManager) {
1037
+ import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
1038
+ @@ -119,7 +119,12 @@ export function formatResumeCommand(sessionManager) {
989
1039
  const sessionFile = sessionManager.getSessionFile();
990
1040
  if (!sessionFile || !fs.existsSync(sessionFile))
991
1041
  return undefined;
@@ -999,7 +1049,7 @@ index 5d65200..397a7b7 100644
999
1049
  if (!sessionManager.usesDefaultSessionDir()) {
1000
1050
  args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
1001
1051
  }
1002
- @@ -301,9 +306,15 @@ export class InteractiveMode {
1052
+ @@ -405,9 +410,15 @@ export class InteractiveMode {
1003
1053
  }
1004
1054
  getBuiltInCommandConflictDiagnostics(extensionRunner) {
1005
1055
  const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
@@ -1016,15 +1066,23 @@ index 5d65200..397a7b7 100644
1016
1066
  .map((command) => ({
1017
1067
  type: "warning",
1018
1068
  message: command.invocationName === command.name
1019
- @@ -549,12 +560,14 @@ export class InteractiveMode {
1069
+ @@ -754,20 +765,18 @@ export class InteractiveMode {
1020
1070
  this.showNewVersionNotification(newRelease);
1021
1071
  }
1022
1072
  });
1023
1073
  - // Start package update check asynchronously
1024
- - this.checkForPackageUpdates().then((updates) => {
1074
+ - this.checkForPackageUpdates()
1075
+ - .then((updates) => {
1025
1076
  - if (updates.length > 0) {
1026
1077
  - this.showPackageUpdateNotification(updates);
1027
1078
  - }
1079
+ - })
1080
+ - .finally(() => {
1081
+ - // On Windows, npm can overwrite the shared console title while checking
1082
+ - // extension package versions. Restore Pi's title after the startup check.
1083
+ - if (process.platform === "win32" && this.isInitialized) {
1084
+ - this.updateTerminalTitle();
1085
+ - }
1028
1086
  - });
1029
1087
  + // Privateer owns the package ("tool pack") update surface — see
1030
1088
  + // extensions/privateer-update.ts. Upstream ran its own check here and drew a
@@ -1034,10 +1092,14 @@ index 5d65200..397a7b7 100644
1034
1092
  + // line in the startup banner plus /update, which applies packs in place. The
1035
1093
  + // CHECK is removed too, not just the box, so this costs no registry round
1036
1094
  + // trip — the extension does exactly one, on the same startup.
1095
+ + //
1096
+ + // 0.84.1 added a .finally() here restoring the Windows console title after
1097
+ + // npm overwrote it. That repair existed only because of this check, so it
1098
+ + // goes with it — nothing is left to scribble on the title.
1037
1099
  // Check tmux keyboard setup asynchronously
1038
1100
  this.checkTmuxKeyboardSetup().then((warning) => {
1039
1101
  if (warning) {
1040
- @@ -2042,7 +2055,17 @@ export class InteractiveMode {
1102
+ @@ -2310,7 +2319,17 @@ export class InteractiveMode {
1041
1103
  if (text === "/model" || text.startsWith("/model ")) {
1042
1104
  const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
1043
1105
  this.editor.setText("");
@@ -1056,14 +1118,11 @@ index 5d65200..397a7b7 100644
1056
1118
  return;
1057
1119
  }
1058
1120
  if (text === "/export" || text.startsWith("/export ")) {
1059
- @@ -2105,14 +2128,44 @@ export class InteractiveMode {
1060
- this.editor.setText("");
1061
- return;
1062
- }
1063
- - if (text === "/login") {
1064
- - this.showOAuthSelector("login");
1065
- + if (text === "/login" || text.startsWith("/login ")) {
1121
+ @@ -2376,12 +2395,42 @@ export class InteractiveMode {
1122
+ if (text === "/login" || text.startsWith("/login ")) {
1123
+ const providerRef = text.startsWith("/login ") ? text.slice(7).trim() : undefined;
1066
1124
  this.editor.setText("");
1125
+ - await this.handleLoginCommand(providerRef);
1067
1126
  + // Privateer redirect: a bare /login IS the account sign-in. Pi's built-in
1068
1127
  + // opens a two-step menu ("Use a subscription" → a list of 20+ providers)
1069
1128
  + // that buries the one option a Privateer user wants, and — because it
@@ -1071,14 +1130,14 @@ index 5d65200..397a7b7 100644
1071
1130
  + // successful login through it left the terminal on its launch model and
1072
1131
  + // the next prompt died on "No API key found". The extension's own flow
1073
1132
  + // signs in, arms the account channel, and selects the model.
1074
- + // `/login <anything>` (documented as `/login keys`) still opens Pi's own
1075
- + // selector, so BYO provider keys stay reachable; and if the extension
1076
- + // isn't loaded we fall back to Pi's selector for both.
1077
- + if (text === "/login" && this.isExtensionCommand("/privateer")) {
1133
+ + // `/login <anything>` (documented as `/login keys`) still reaches Pi's
1134
+ + // own flow, so BYO provider keys stay reachable; and if the extension
1135
+ + // isn't loaded we fall back to Pi for both.
1136
+ + if (!providerRef && this.isExtensionCommand("/privateer")) {
1078
1137
  + await this.session.prompt("/privateer login");
1079
1138
  + }
1080
1139
  + else {
1081
- + this.showOAuthSelector("login");
1140
+ + await this.handleLoginCommand(providerRef);
1082
1141
  + }
1083
1142
  return;
1084
1143
  }
@@ -1104,7 +1163,7 @@ index 5d65200..397a7b7 100644
1104
1163
  return;
1105
1164
  }
1106
1165
  if (text === "/new") {
1107
- @@ -2650,7 +2703,7 @@ export class InteractiveMode {
1166
+ @@ -3024,7 +3073,7 @@ export class InteractiveMode {
1108
1167
  if (this.chatContainer.children.length > 0) {
1109
1168
  this.chatContainer.addChild(new Spacer(1));
1110
1169
  }
@@ -1114,19 +1173,28 @@ index 5d65200..397a7b7 100644
1114
1173
  async getUserInput() {
1115
1174
  const queuedInput = this.pendingUserInputs.shift();
1116
1175
  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
1117
- index f81b4b4..9712a37 100644
1176
+ index 4230b18..f47e80f 100644
1118
1177
  --- a/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
1119
1178
  +++ b/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
1120
- @@ -2,7 +2,7 @@ import { Markdown } from "@earendil-works/pi-tui";
1179
+ @@ -3,7 +3,7 @@ import { Markdown } from "@earendil-works/pi-tui";
1121
1180
  import chalk from "chalk";
1122
1181
  import { selectConfig } from "./cli/config-selector.js";
1123
1182
  import { createProjectTrustContext } from "./cli/project-trust.js";
1124
1183
  -import { APP_NAME, CONFIG_DIR_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
1125
- +import { APP_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, PROJECT_CONFIG_DIR_NAMES, VERSION, } from "./config.js";
1184
+ +import { APP_NAME, CONFIG_DIR_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, PROJECT_CONFIG_DIR_NAMES, VERSION, } from "./config.js";
1185
+ import { ModelRuntime } from "./core/model-runtime.js";
1126
1186
  import { DefaultPackageManager } from "./core/package-manager.js";
1127
1187
  import { resolveProjectTrusted } from "./core/project-trust.js";
1128
- import { DefaultResourceLoader } from "./core/resource-loader.js";
1129
- @@ -57,7 +57,7 @@ function printPackageCommandHelp(command) {
1188
+ @@ -60,7 +60,7 @@ Without -l, starts in global settings (~/${CONFIG_DIR_NAME}/agent/settings.json)
1189
+ Press Tab in the TUI to switch between global and project-local modes.
1190
+
1191
+ Options:
1192
+ - -l, --local Edit project overrides (${CONFIG_DIR_NAME}/settings.json)
1193
+ + -l, --local Edit project overrides (${PROJECT_CONFIG_DIR_NAMES[0]}/settings.json)
1194
+ -a, --approve Trust project-local files for this command with -l
1195
+ -na, --no-approve Ignore project-local files for this command with -l
1196
+ `);
1197
+ @@ -74,7 +74,7 @@ function printPackageCommandHelp(command) {
1130
1198
  Install a package and add it to settings.
1131
1199
 
1132
1200
  Options:
@@ -1135,7 +1203,7 @@ index f81b4b4..9712a37 100644
1135
1203
  -a, --approve Trust project-local files for this command
1136
1204
  -na, --no-approve Ignore project-local files for this command
1137
1205
 
1138
- @@ -78,7 +78,7 @@ Remove a package and its source from settings.
1206
+ @@ -95,7 +95,7 @@ Remove a package and its source from settings.
1139
1207
  Alias: ${APP_NAME} uninstall <source> [-l]
1140
1208
 
1141
1209
  Options:
package/src/acp/run.ts CHANGED
@@ -87,8 +87,9 @@ export async function runAcp(): Promise<void> {
87
87
  const { createEngineEventAdapter } = await import("../bridge/engineAdapter.ts");
88
88
  type GateController = import("../ext/permissionGate.ts").GateController;
89
89
  const { moatResourceOptions } = await import("../config/moat.ts");
90
- const { privateerChannel, rememberAccountCredential, dropPersistedAccountCredential } =
90
+ const { privateerChannel, rememberAccountCredential, persistAccountCredential, dropPersistedAccountCredential } =
91
91
  await import("../providers/account.ts");
92
+ const { modelRegistryOf } = await import("../providers/piAuthStore.ts");
92
93
  const { hasCredentials, acquireAccountCredential, revokeAccountSession } = await import("../auth/privateer.ts");
93
94
  const { webEnabled, mediaEnabled } = await import("../config/hosted.ts");
94
95
  const { resolveDefaultModel } = await import("../providers/defaultModel.ts");
@@ -160,7 +161,7 @@ export async function runAcp(): Promise<void> {
160
161
  },
161
162
  });
162
163
 
163
- const registry = services.modelRegistry as any;
164
+ const registry = modelRegistryOf(services) as any;
164
165
  const resolve = (spec: string) => {
165
166
  const { provider, modelId } = parseSpec(spec);
166
167
  return registry.find(provider, modelId) ?? null;
@@ -236,7 +237,7 @@ export async function runAcp(): Promise<void> {
236
237
  }
237
238
  try {
238
239
  const creds = await acquireAccountCredential();
239
- (services.authStorage as any).set("privateer", { type: "oauth", ...creds });
240
+ await persistAccountCredential(creds);
240
241
  rememberAccountCredential(creds); // claim it, so teardown drops OUR entry only
241
242
  armedAccount = true;
242
243
  log("account channel armed");
@@ -356,7 +357,7 @@ export async function runAcp(): Promise<void> {
356
357
  /* best effort — the server-side TTL is the fallback */
357
358
  }
358
359
  try {
359
- dropPersistedAccountCredential({ modelRegistry: { authStorage: services.authStorage } } as any);
360
+ await dropPersistedAccountCredential();
360
361
  } catch {
361
362
  /* nothing persisted */
362
363
  }
@@ -48,6 +48,8 @@ import "../boot.ts"; // env + attestation dispatcher, before any Pi import
48
48
  import { AsyncLocalStorage } from "node:async_hooks";
49
49
  // Names only — the factory itself is imported lazily in main() like every other
50
50
  // module here. Safe statically: this evaluates after boot.ts and pulls in no Pi.
51
+ // Pi-free static graph by design — see the header of piAuthStore.ts.
52
+ import { modelRegistryOf } from "../providers/piAuthStore.ts";
51
53
  import { WEB_TOOL_NAMES } from "../tools/web.ts";
52
54
  import { MEDIA_TOOL_NAMES } from "../tools/media.ts";
53
55
 
@@ -219,7 +221,7 @@ async function main() {
219
221
  let m = modelCache.get(spec);
220
222
  if (m === undefined) {
221
223
  const { provider, modelId } = parseSpec(spec);
222
- m = (services.modelRegistry as any).find(provider, modelId) ?? null;
224
+ m = (modelRegistryOf(services) as any).find(provider, modelId) ?? null;
223
225
  modelCache.set(spec, m);
224
226
  }
225
227
  return m;