privateer-agent 0.12.42 → 0.12.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -10
- package/patches/{@earendil-works+pi-coding-agent+0.84.4.patch → @earendil-works+pi-coding-agent+0.86.0.patch} +109 -99
- package/src/cli/chat.ts +12 -3
- package/src/engine/contextBudget.ts +1 -1
- package/src/providers/defaultModel.ts +37 -0
- package/src/tools/media.ts +102 -12
- /package/patches/{@earendil-works+pi-ai+0.84.4.patch → @earendil-works+pi-ai+0.86.0.patch} +0 -0
- /package/patches/{pi-background-tasks+2.4.2.patch → pi-background-tasks+2.5.0.patch} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "privateer-agent",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.45",
|
|
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",
|
|
@@ -70,21 +70,21 @@
|
|
|
70
70
|
"node": ">=22.19.0"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@earendil-works/pi-ai": "0.
|
|
74
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
75
|
-
"@earendil-works/pi-tui": "0.
|
|
76
|
-
"@juicesharp/rpiv-ask-user-question": "2.
|
|
77
|
-
"@juicesharp/rpiv-web-tools": "
|
|
73
|
+
"@earendil-works/pi-ai": "0.86.0",
|
|
74
|
+
"@earendil-works/pi-coding-agent": "0.86.0",
|
|
75
|
+
"@earendil-works/pi-tui": "0.86.0",
|
|
76
|
+
"@juicesharp/rpiv-ask-user-question": "2.10.1",
|
|
77
|
+
"@juicesharp/rpiv-web-tools": "2.10.1",
|
|
78
78
|
"@noble/ciphers": "2.2.0",
|
|
79
79
|
"@noble/curves": "1.9.7",
|
|
80
80
|
"@noble/hashes": "1.8.0",
|
|
81
81
|
"@phala/dcap-qvl": "0.5.2",
|
|
82
82
|
"@zed-industries/agent-client-protocol": "0.4.5",
|
|
83
83
|
"patch-package": "8.0.1",
|
|
84
|
-
"pi-background-tasks": "2.
|
|
85
|
-
"pi-mcp-adapter": "2.
|
|
86
|
-
"pi-privacy": "0.
|
|
87
|
-
"pi-subagents": "0.
|
|
84
|
+
"pi-background-tasks": "2.5.0",
|
|
85
|
+
"pi-mcp-adapter": "2.34.0",
|
|
86
|
+
"pi-privacy": "0.14.1",
|
|
87
|
+
"pi-subagents": "0.70.0",
|
|
88
88
|
"picomatch": "4.0.5",
|
|
89
89
|
"privateer-speak": "0.2.2",
|
|
90
90
|
"privateer-workflow": "0.1.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
|
|
2
|
+
index 872dd55..edd61db 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
|
-
@@ -
|
|
5
|
+
@@ -399,9 +399,72 @@ catch (e) {
|
|
6
6
|
const piConfigName = pkg.piConfig?.name;
|
|
7
7
|
export const PACKAGE_NAME = pkg.name || "@earendil-works/pi-coding-agent";
|
|
8
8
|
export const APP_NAME = piConfigName || "pi";
|
|
@@ -77,10 +77,10 @@ index f04269f..7160497 100644
|
|
|
77
77
|
export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
|
|
78
78
|
export const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;
|
|
79
79
|
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
|
|
80
|
-
index
|
|
80
|
+
index 638646d..ba1da98 100644
|
|
81
81
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
|
|
82
82
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
|
|
83
|
-
@@ -
|
|
83
|
+
@@ -39,6 +39,129 @@ import { createLocalBashOperations } from "./tools/bash.js";
|
|
84
84
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
85
85
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
86
86
|
import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
|
|
@@ -192,7 +192,14 @@ index fd82b71..7c10ba9 100644
|
|
|
192
192
|
+// A server-stated delay wins outright — it is the only number here that is a fact.
|
|
193
193
|
+// The jitter is the point of the rest: without it every parallel request that tripped
|
|
194
194
|
+// the same limit wakes at the identical millisecond and trips it again.
|
|
195
|
-
|
|
195
|
+
+//
|
|
196
|
+
+// Privateer patch: named pvRetryDelayMs, not retryDelayMs — pi-ai now ships its OWN
|
|
197
|
+
+// `retryDelayMs(policy, attempt)` (plain exponential backoff, no retry-after, no
|
|
198
|
+
+// jitter), imported by this file below. A same-named top-level function here would be
|
|
199
|
+
+// a SyntaxError (duplicate declaration) that takes the whole CLI down at launch,
|
|
200
|
+
+// exactly the failure mode the MIN_ANSWER_TOKENS comment in the pi-ai patch warns
|
|
201
|
+
+// about. Ours still fully supersedes pi-ai's at the one call site that mattered.
|
|
202
|
+
+export function pvRetryDelayMs(errorText, attempt, baseDelayMs) {
|
|
196
203
|
+ const stated = retryAfterMs(errorText);
|
|
197
204
|
+ if (stated != null)
|
|
198
205
|
+ return stated;
|
|
@@ -203,7 +210,7 @@ index fd82b71..7c10ba9 100644
|
|
|
203
210
|
/**
|
|
204
211
|
* Parse a skill block from message text.
|
|
205
212
|
* Returns null if the text doesn't contain a skill block.
|
|
206
|
-
@@ -
|
|
213
|
+
@@ -101,6 +224,8 @@ export class AgentSession {
|
|
207
214
|
// Retry state
|
|
208
215
|
_retryAbortController = undefined;
|
|
209
216
|
_retryAttempt = 0;
|
|
@@ -212,7 +219,7 @@ index fd82b71..7c10ba9 100644
|
|
|
212
219
|
// Bash execution state
|
|
213
220
|
_bashAbortControllers = new Set();
|
|
214
221
|
_pendingBashMessages = [];
|
|
215
|
-
@@ -
|
|
222
|
+
@@ -192,6 +317,14 @@ export class AgentSession {
|
|
216
223
|
}
|
|
217
224
|
const isOAuth = this._modelRuntime.isUsingOAuth(model.provider);
|
|
218
225
|
if (isOAuth) {
|
|
@@ -227,7 +234,7 @@ index fd82b71..7c10ba9 100644
|
|
|
227
234
|
throw new Error(`Authentication failed for "${model.provider}". ` +
|
|
228
235
|
`Credentials may have expired or network is unavailable. ` +
|
|
229
236
|
`Run '/login ${model.provider}' to re-authenticate.`);
|
|
230
|
-
@@ -
|
|
237
|
+
@@ -401,6 +534,16 @@ export class AgentSession {
|
|
231
238
|
}
|
|
232
239
|
}
|
|
233
240
|
}
|
|
@@ -244,7 +251,7 @@ index fd82b71..7c10ba9 100644
|
|
|
244
251
|
// Emit to extensions first
|
|
245
252
|
await this._emitExtensionEvent(event);
|
|
246
253
|
// Notify all listeners
|
|
247
|
-
@@ -
|
|
254
|
+
@@ -904,6 +1047,33 @@ export class AgentSession {
|
|
248
255
|
finalError: msg.errorMessage,
|
|
249
256
|
});
|
|
250
257
|
this._retryAttempt = 0;
|
|
@@ -277,8 +284,8 @@ index fd82b71..7c10ba9 100644
|
|
|
277
284
|
+ return false;
|
|
278
285
|
}
|
|
279
286
|
if (await this._checkCompaction(msg)) {
|
|
280
|
-
return
|
|
281
|
-
@@ -
|
|
287
|
+
return !this._agentRunAbortRequested;
|
|
288
|
+
@@ -991,6 +1161,14 @@ export class AgentSession {
|
|
282
289
|
if (!hasConfiguredAuth) {
|
|
283
290
|
const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
|
|
284
291
|
if (isOAuth) {
|
|
@@ -293,7 +300,7 @@ index fd82b71..7c10ba9 100644
|
|
|
293
300
|
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
294
301
|
`Credentials may have expired or network is unavailable. ` +
|
|
295
302
|
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
296
|
-
@@ -
|
|
303
|
+
@@ -1000,7 +1178,13 @@ export class AgentSession {
|
|
297
304
|
// Check if we need to compact before sending (catches aborted responses).
|
|
298
305
|
// The user's new prompt is sent below, so do not call agent.continue() here.
|
|
299
306
|
const lastAssistant = this._findLastAssistantMessage();
|
|
@@ -308,7 +315,7 @@ index fd82b71..7c10ba9 100644
|
|
|
308
315
|
await this._checkCompaction(lastAssistant, false);
|
|
309
316
|
}
|
|
310
317
|
// Build messages array (custom message if any, then user message)
|
|
311
|
-
@@ -
|
|
318
|
+
@@ -2351,6 +2535,34 @@ export class AgentSession {
|
|
312
319
|
// Context overflow is handled by compaction, not retry.
|
|
313
320
|
if (isContextOverflow(message, this.model?.contextWindow ?? 0))
|
|
314
321
|
return false;
|
|
@@ -343,16 +350,15 @@ index fd82b71..7c10ba9 100644
|
|
|
343
350
|
return isRetryableAssistantError(message);
|
|
344
351
|
}
|
|
345
352
|
/**
|
|
346
|
-
@@ -
|
|
353
|
+
@@ -2408,7 +2620,10 @@ export class AgentSession {
|
|
347
354
|
this._retryAttempt--;
|
|
348
355
|
return false;
|
|
349
356
|
}
|
|
350
|
-
- const delayMs = settings
|
|
357
|
+
- const delayMs = retryDelayMs(settings, this._retryAttempt);
|
|
351
358
|
+ // Privateer patch: honour a server-stated `retry-after` and jitter the fallback.
|
|
352
|
-
+ // Stock Pi's
|
|
353
|
-
+ //
|
|
354
|
-
+
|
|
355
|
-
+ const delayMs = retryDelayMs(message.errorMessage, this._retryAttempt, settings.baseDelayMs);
|
|
359
|
+
+ // Stock Pi's own retryDelayMs (imported above, from pi-ai) is a fixed 2s/4s/8s
|
|
360
|
+
+ // ladder with no jitter and no retry-after — see pvRetryDelayMs above.
|
|
361
|
+
+ const delayMs = pvRetryDelayMs(message.errorMessage, this._retryAttempt, settings.baseDelayMs);
|
|
356
362
|
this._emit({
|
|
357
363
|
type: "auto_retry_start",
|
|
358
364
|
attempt: this._retryAttempt,
|
|
@@ -402,35 +408,37 @@ index 197bccc..bc9ac3f 100644
|
|
|
402
408
|
//# sourceMappingURL=auth-guidance.js.map
|
|
403
409
|
|
|
404
410
|
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
|
|
405
|
-
index
|
|
411
|
+
index be748bf..378f4c7 100644
|
|
406
412
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
|
|
407
413
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
|
|
408
|
-
@@ -
|
|
409
|
-
import
|
|
410
|
-
import * as
|
|
411
|
-
import
|
|
412
|
-
-import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.js";
|
|
413
|
-
+import { getAgentDir, isBunBinary, projectConfigDirCandidates } from "../../config.js";
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
import
|
|
417
|
-
@@ -
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
+
|
|
423
|
-
+
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
414
|
+
@@ -6,7 +6,7 @@ import * as fs from "node:fs";
|
|
415
|
+
import { createRequire } from "node:module";
|
|
416
|
+
import * as path from "node:path";
|
|
417
|
+
import { fileURLToPath } from "node:url";
|
|
418
|
+
-import { CONFIG_DIR_NAME, getAgentDir, isBunBinary, isBundledNode } from "../../config.js";
|
|
419
|
+
+import { getAgentDir, isBunBinary, isBundledNode, projectConfigDirCandidates } from "../../config.js";
|
|
420
|
+
import { resolvePath } from "../../utils/paths.js";
|
|
421
|
+
import { createEventBus } from "../event-bus.js";
|
|
422
|
+
import { execCommand } from "../exec.js";
|
|
423
|
+
@@ -401,12 +401,15 @@ async function loadExtensionModule(extensionPath, cacheToken) {
|
|
424
|
+
const createJitiImpl = await getCreateJiti();
|
|
425
|
+
// Compiled binaries and the bundled Node distribution use embedded modules.
|
|
426
|
+
// Source TypeScript reuses host modules and root tsconfig paths. Unbundled
|
|
427
|
+
- // Node builds use dist aliases and do not need the bundled virtual modules.
|
|
428
|
+
+ // Node builds use dist aliases.
|
|
429
|
+
+ // Privateer: reuse the host's Pi/Typebox namespaces in Node builds too.
|
|
430
|
+
+ // Otherwise each extension can reload that graph through Jiti. Aliases
|
|
431
|
+
+ // remain a fallback for non-virtual imports; extension reload is unchanged.
|
|
432
|
+
const resolutionOptions = usesEmbeddedModules
|
|
433
|
+
? { virtualModules: await getVirtualModules(), tryNative: false }
|
|
434
|
+
: isTypeScriptSourceRuntime
|
|
435
|
+
? { virtualModules: await getVirtualModules(), tsconfigPaths: true }
|
|
436
|
+
- : { alias: getAliases() };
|
|
437
|
+
+ : { virtualModules: await getVirtualModules(), alias: getAliases() };
|
|
438
|
+
const jiti = createJitiImpl(import.meta.url, {
|
|
439
|
+
moduleCache: false,
|
|
440
|
+
...resolutionOptions,
|
|
441
|
+
@@ -607,9 +610,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
|
|
434
442
|
}
|
|
435
443
|
}
|
|
436
444
|
};
|
|
@@ -810,10 +818,10 @@ index c5ed4d7..d9f50ae 100644
|
|
|
810
818
|
}
|
|
811
819
|
const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
|
|
812
820
|
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
|
|
813
|
-
index
|
|
821
|
+
index ed9d8d2..34d344c 100644
|
|
814
822
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
|
|
815
823
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
|
|
816
|
-
@@ -
|
|
824
|
+
@@ -3,7 +3,7 @@ import { randomUUID } from "crypto";
|
|
817
825
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
818
826
|
import { dirname, join } from "path";
|
|
819
827
|
import lockfile from "proper-lockfile";
|
|
@@ -822,7 +830,7 @@ index f0f936e..ad164d2 100644
|
|
|
822
830
|
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
823
831
|
import { stripBom } from "../utils/text.js";
|
|
824
832
|
import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.js";
|
|
825
|
-
@@ -
|
|
833
|
+
@@ -52,14 +52,112 @@ function toSettingsError(scope, error, path) {
|
|
826
834
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
827
835
|
};
|
|
828
836
|
}
|
|
@@ -936,7 +944,7 @@ index f0f936e..ad164d2 100644
|
|
|
936
944
|
}
|
|
937
945
|
acquireLockSyncWithRetry(path) {
|
|
938
946
|
const maxAttempts = 10;
|
|
939
|
-
@@ -
|
|
947
|
+
@@ -86,7 +184,13 @@ export class FileSettingsStorage {
|
|
940
948
|
throw lastError ?? new Error("Failed to acquire settings lock");
|
|
941
949
|
}
|
|
942
950
|
withLock(scope, fn) {
|
|
@@ -951,7 +959,7 @@ index f0f936e..ad164d2 100644
|
|
|
951
959
|
const dir = dirname(path);
|
|
952
960
|
let release;
|
|
953
961
|
try {
|
|
954
|
-
@@ -
|
|
962
|
+
@@ -164,7 +268,7 @@ export class SettingsManager {
|
|
955
963
|
const storage = new FileSettingsStorage(resolvedCwd, resolvedAgentDir);
|
|
956
964
|
return SettingsManager.fromStorageWithPaths(storage, options, {
|
|
957
965
|
global: join(resolvedAgentDir, "settings.json"),
|
|
@@ -960,7 +968,7 @@ index f0f936e..ad164d2 100644
|
|
|
960
968
|
});
|
|
961
969
|
}
|
|
962
970
|
/** Create a SettingsManager from an arbitrary storage backend */
|
|
963
|
-
@@ -
|
|
971
|
+
@@ -646,7 +750,15 @@ export class SettingsManager {
|
|
964
972
|
getProviderRetrySettings() {
|
|
965
973
|
return {
|
|
966
974
|
timeoutMs: this.settings.retry?.provider?.timeoutMs,
|
|
@@ -978,7 +986,7 @@ index f0f936e..ad164d2 100644
|
|
|
978
986
|
};
|
|
979
987
|
}
|
|
980
988
|
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
|
|
981
|
-
index
|
|
989
|
+
index d101003..540b972 100644
|
|
982
990
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
|
|
983
991
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
|
|
984
992
|
@@ -1,7 +1,7 @@
|
|
@@ -990,7 +998,7 @@ index 56cf7a3..2a32170 100644
|
|
|
990
998
|
import { parseFrontmatter } from "../utils/frontmatter.js";
|
|
991
999
|
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
992
1000
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
993
|
-
@@ -
|
|
1001
|
+
@@ -348,10 +348,15 @@ export function loadSkills(options) {
|
|
994
1002
|
}
|
|
995
1003
|
if (includeDefaults) {
|
|
996
1004
|
addSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, "skills"), "user", true));
|
|
@@ -1008,7 +1016,7 @@ index 56cf7a3..2a32170 100644
|
|
|
1008
1016
|
const isUnderPath = (target, root) => {
|
|
1009
1017
|
const normalizedRoot = resolve(root);
|
|
1010
1018
|
if (target === normalizedRoot) {
|
|
1011
|
-
@@ -
|
|
1019
|
+
@@ -364,7 +369,7 @@ export function loadSkills(options) {
|
|
1012
1020
|
if (!includeDefaults) {
|
|
1013
1021
|
if (isUnderPath(resolvedPath, userSkillsDir))
|
|
1014
1022
|
return "user";
|
|
@@ -1161,7 +1169,7 @@ index 0877ee7..faa2559 100644
|
|
|
1161
1169
|
while (true) {
|
|
1162
1170
|
const agentsSkillsDir = join(currentDir, ".agents", "skills");
|
|
1163
1171
|
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
|
|
1164
|
-
index
|
|
1172
|
+
index 205144d..14080a2 100644
|
|
1165
1173
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
|
|
1166
1174
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts
|
|
1167
1175
|
@@ -1,7 +1,7 @@
|
|
@@ -1170,9 +1178,9 @@ index 3e9241d..bfbfcbf 100644
|
|
|
1170
1178
|
export { AgentSession, type AgentSessionConfig, type AgentSessionEvent, type AgentSessionEventListener, type ModelCycleResult, type ParsedSkillBlock, type PromptOptions, parseSkillBlock, type SessionStats, } from "./core/agent-session.ts";
|
|
1171
1179
|
-export { readStoredCredential } from "./core/auth-storage.ts";
|
|
1172
1180
|
+export { AuthStorage, readStoredCredential } from "./core/auth-storage.ts";
|
|
1181
|
+
export type { CacheWarmingDecision, CacheWarmingStatus } from "./core/cache-warmer.ts";
|
|
1173
1182
|
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";
|
|
1174
1183
|
export { createEventBus, type EventBus, type EventBusController } from "./core/event-bus.ts";
|
|
1175
|
-
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";
|
|
1176
1184
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/index.js b/node_modules/@earendil-works/pi-coding-agent/dist/index.js
|
|
1177
1185
|
index 01017cd..c2b8722 100644
|
|
1178
1186
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/index.js
|
|
@@ -1187,10 +1195,10 @@ index 01017cd..c2b8722 100644
|
|
|
1187
1195
|
export { calculateContextTokens, collectEntriesForBranchSummary, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, findCutPoint, findTurnStartIndex, generateBranchSummary, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, shouldCompact, } from "./core/compaction/index.js";
|
|
1188
1196
|
export { createEventBus } from "./core/event-bus.js";
|
|
1189
1197
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/main.js b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
1190
|
-
index
|
|
1198
|
+
index be10307..636fbf4 100644
|
|
1191
1199
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
1192
1200
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
1193
|
-
@@ -
|
|
1201
|
+
@@ -802,7 +802,58 @@ export async function main(args, options) {
|
|
1194
1202
|
if (exitCode !== 0) {
|
|
1195
1203
|
process.exitCode = exitCode;
|
|
1196
1204
|
}
|
|
@@ -1251,7 +1259,7 @@ index 940e0f4..c0f3d11 100644
|
|
|
1251
1259
|
//# sourceMappingURL=main.js.map
|
|
1252
1260
|
|
|
1253
1261
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
1254
|
-
index
|
|
1262
|
+
index c30d6b8..ad1654a 100644
|
|
1255
1263
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
1256
1264
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
1257
1265
|
@@ -4,7 +4,7 @@
|
|
@@ -1262,7 +1270,7 @@ index 072268a..472ee6d 100644
|
|
|
1262
1270
|
+import { getAgentDir, getBinDir, projectConfigDirs } from "./config.js";
|
|
1263
1271
|
import { migrateKeybindingsConfig } from "./core/keybindings.js";
|
|
1264
1272
|
import { stripBom } from "./utils/text.js";
|
|
1265
|
-
const MIGRATION_GUIDE_URL = "https://github.com/earendil-works/pi
|
|
1273
|
+
const MIGRATION_GUIDE_URL = "https://github.com/earendil-works/pi/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
|
|
1266
1274
|
@@ -231,14 +231,17 @@ function checkDeprecatedExtensionDirs(baseDir, label) {
|
|
1267
1275
|
*/
|
|
1268
1276
|
function migrateExtensionSystem(cwd) {
|
|
@@ -1330,7 +1338,7 @@ index 6f7ed6a..b0ed0ae 100644
|
|
|
1330
1338
|
getResourcePattern(item) {
|
|
1331
1339
|
const scope = item.metadata.scope;
|
|
1332
1340
|
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
|
|
1333
|
-
index
|
|
1341
|
+
index 0e2811e..c0326e3 100644
|
|
1334
1342
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
|
|
1335
1343
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js
|
|
1336
1344
|
@@ -1,7 +1,6 @@
|
|
@@ -1400,7 +1408,7 @@ index 1d9f046..c0326e3 100644
|
|
|
1400
1408
|
*/
|
|
1401
1409
|
export class FooterComponent {
|
|
1402
1410
|
autoCompactEnabled = true;
|
|
1403
|
-
@@ -74,
|
|
1411
|
+
@@ -74,32 +113,9 @@ export class FooterComponent {
|
|
1404
1412
|
}
|
|
1405
1413
|
render(width) {
|
|
1406
1414
|
const state = this.session.state;
|
|
@@ -1408,7 +1416,10 @@ index 1d9f046..c0326e3 100644
|
|
|
1408
1416
|
- const usageTotals = createUsageTotals();
|
|
1409
1417
|
- let latestCacheHitRate;
|
|
1410
1418
|
- for (const entry of this.session.sessionManager.getEntries()) {
|
|
1411
|
-
- if (entry.type === "
|
|
1419
|
+
- if (entry.type === "usage") {
|
|
1420
|
+
- addUsageToTotals(usageTotals, entry.usage);
|
|
1421
|
+
- }
|
|
1422
|
+
- else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
1412
1423
|
- addUsageToTotals(usageTotals, entry.message.usage);
|
|
1413
1424
|
- const latestPromptTokens = entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
|
|
1414
1425
|
- latestCacheHitRate =
|
|
@@ -1433,7 +1444,7 @@ index 1d9f046..c0326e3 100644
|
|
|
1433
1444
|
// Replace home directory with ~
|
|
1434
1445
|
let pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);
|
|
1435
1446
|
// Add git branch if available
|
|
1436
|
-
@@ -
|
|
1447
|
+
@@ -112,56 +128,24 @@ export class FooterComponent {
|
|
1437
1448
|
if (sessionName) {
|
|
1438
1449
|
pwd = `${pwd} • ${sessionName}`;
|
|
1439
1450
|
}
|
|
@@ -1505,7 +1516,7 @@ index 1d9f046..c0326e3 100644
|
|
|
1505
1516
|
const minPadding = 2;
|
|
1506
1517
|
// Add thinking level indicator if model supports reasoning
|
|
1507
1518
|
let rightSideWithoutProvider = modelName;
|
|
1508
|
-
@@ -
|
|
1519
|
+
@@ -174,50 +158,48 @@ export class FooterComponent {
|
|
1509
1520
|
let rightSide = rightSideWithoutProvider;
|
|
1510
1521
|
if (this.footerData.getAvailableProviderCount() > 1 && state.model) {
|
|
1511
1522
|
rightSide = `(${state.model.provider}) ${rightSideWithoutProvider}`;
|
|
@@ -1575,13 +1586,12 @@ index 1d9f046..c0326e3 100644
|
|
|
1575
1586
|
return lines;
|
|
1576
1587
|
}
|
|
1577
1588
|
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
|
|
1578
|
-
index
|
|
1589
|
+
index 6d0356c..ca0aa98 100644
|
|
1579
1590
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js
|
|
1580
1591
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js
|
|
1581
|
-
@@ -1,
|
|
1582
|
-
-import { Box, Container, getCapabilities, Image, Spacer, Text } from "@earendil-works/pi-tui";
|
|
1583
|
-
+import { Box, Container, getCapabilities, Image, Spacer, visibleWidth, Text } from "@earendil-works/pi-tui";
|
|
1584
|
-
import { createAllToolDefinitions } from "../../../core/tools/index.js";
|
|
1592
|
+
@@ -1,9 +1,36 @@
|
|
1593
|
+
-import { Box, Container, getCapabilities, Image, MouseRegion, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
1594
|
+
+import { Box, Container, getCapabilities, Image, MouseRegion, Spacer, visibleWidth, Text, } from "@earendil-works/pi-tui";
|
|
1585
1595
|
import { getTextOutput as getRenderedTextOutput } from "../../../core/tools/render-utils.js";
|
|
1586
1596
|
import { convertToPng } from "../../../utils/image-convert.js";
|
|
1587
1597
|
import { theme } from "../theme/theme.js";
|
|
@@ -1617,7 +1627,7 @@ index c33fc39..060a38f 100644
|
|
|
1617
1627
|
export class ToolExecutionComponent extends Container {
|
|
1618
1628
|
contentBox;
|
|
1619
1629
|
contentText;
|
|
1620
|
-
@@ -
|
|
1630
|
+
@@ -44,8 +71,13 @@ export class ToolExecutionComponent extends Container {
|
|
1621
1631
|
// Always create all shell variants. contentBox is used for default renderer-based composition.
|
|
1622
1632
|
// selfRenderContainer is used when the tool renders its own framing.
|
|
1623
1633
|
// contentText is reserved for generic fallback rendering when no tool definition exists.
|
|
@@ -1630,10 +1640,10 @@ index c33fc39..060a38f 100644
|
|
|
1630
1640
|
+ // matches what the self-render path above emits (a single lines.push("")).
|
|
1631
1641
|
+ this.contentBox = new Box(1, 0, (text) => theme.bg("toolPendingBg", text));
|
|
1632
1642
|
+ this.contentText = new Text("", 1, 0, (text) => theme.bg("toolPendingBg", text));
|
|
1643
|
+
this.contentTextRegion = this.createResultRegion(this.contentText);
|
|
1633
1644
|
this.selfRenderContainer = new Container();
|
|
1634
1645
|
if (this.hasRendererDefinition()) {
|
|
1635
|
-
|
|
1636
|
-
@@ -108,6 +140,13 @@ export class ToolExecutionComponent extends Container {
|
|
1646
|
+
@@ -90,6 +122,13 @@ export class ToolExecutionComponent extends Container {
|
|
1637
1647
|
createCallFallback() {
|
|
1638
1648
|
return new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0);
|
|
1639
1649
|
}
|
|
@@ -1647,12 +1657,12 @@ index c33fc39..060a38f 100644
|
|
|
1647
1657
|
createResultFallback() {
|
|
1648
1658
|
const output = this.getTextOutput();
|
|
1649
1659
|
if (!output) {
|
|
1650
|
-
@@ -
|
|
1660
|
+
@@ -237,19 +276,22 @@ export class ToolExecutionComponent extends Container {
|
|
1651
1661
|
renderContainer.clear();
|
|
1652
1662
|
const callRenderer = this.getCallRenderer();
|
|
1653
1663
|
if (!callRenderer) {
|
|
1654
|
-
- renderContainer.addChild(this.createCallFallback());
|
|
1655
|
-
+ renderContainer.addChild(this.decorateCall(this.createCallFallback()));
|
|
1664
|
+
- renderContainer.addChild(this.createResultRegion(this.createCallFallback()));
|
|
1665
|
+
+ renderContainer.addChild(this.createResultRegion(this.decorateCall(this.createCallFallback())));
|
|
1656
1666
|
hasContent = true;
|
|
1657
1667
|
}
|
|
1658
1668
|
else {
|
|
@@ -1662,18 +1672,18 @@ index c33fc39..060a38f 100644
|
|
|
1662
1672
|
+ // next render and the tool's renderer calls its own methods on it,
|
|
1663
1673
|
+ // so handing it a wrapper would break every stateful renderer.
|
|
1664
1674
|
this.callRendererComponent = component;
|
|
1665
|
-
- renderContainer.addChild(component);
|
|
1666
|
-
+ renderContainer.addChild(this.decorateCall(component));
|
|
1675
|
+
- renderContainer.addChild(this.createResultRegion(component));
|
|
1676
|
+
+ renderContainer.addChild(this.createResultRegion(this.decorateCall(component)));
|
|
1667
1677
|
hasContent = true;
|
|
1668
1678
|
}
|
|
1669
1679
|
catch {
|
|
1670
1680
|
this.callRendererComponent = undefined;
|
|
1671
|
-
- renderContainer.addChild(this.createCallFallback());
|
|
1672
|
-
+ renderContainer.addChild(this.decorateCall(this.createCallFallback()));
|
|
1681
|
+
- renderContainer.addChild(this.createResultRegion(this.createCallFallback()));
|
|
1682
|
+
+ renderContainer.addChild(this.createResultRegion(this.decorateCall(this.createCallFallback())));
|
|
1673
1683
|
hasContent = true;
|
|
1674
1684
|
}
|
|
1675
1685
|
}
|
|
1676
|
-
@@ -
|
|
1686
|
+
@@ -322,7 +364,8 @@ export class ToolExecutionComponent extends Container {
|
|
1677
1687
|
return getRenderedTextOutput(this.result, this.showImages);
|
|
1678
1688
|
}
|
|
1679
1689
|
formatToolExecution() {
|
|
@@ -1684,11 +1694,11 @@ index c33fc39..060a38f 100644
|
|
|
1684
1694
|
if (content) {
|
|
1685
1695
|
text += `\n\n${content}`;
|
|
1686
1696
|
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
|
|
1687
|
-
index
|
|
1697
|
+
index d82182c..41426bc 100644
|
|
1688
1698
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
|
|
1689
1699
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
|
|
1690
1700
|
@@ -10,7 +10,7 @@ import * as TuiLayouts from "@earendil-works/pi-tui";
|
|
1691
|
-
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey,
|
|
1701
|
+
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
|
|
1692
1702
|
import chalk from "chalk";
|
|
1693
1703
|
import { spawn } from "child_process";
|
|
1694
1704
|
-import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, VERSION, } from "../../config.js";
|
|
@@ -1696,7 +1706,7 @@ index a27754f..3403ca8 100644
|
|
|
1696
1706
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
1697
1707
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
1698
1708
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
1699
|
-
@@ -
|
|
1709
|
+
@@ -141,7 +141,12 @@ export function formatResumeCommand(sessionManager) {
|
|
1700
1710
|
const sessionFile = sessionManager.getSessionFile();
|
|
1701
1711
|
if (!sessionFile || !fs.existsSync(sessionFile))
|
|
1702
1712
|
return undefined;
|
|
@@ -1710,9 +1720,9 @@ index a27754f..3403ca8 100644
|
|
|
1710
1720
|
if (!sessionManager.usesDefaultSessionDir()) {
|
|
1711
1721
|
args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
|
|
1712
1722
|
}
|
|
1713
|
-
@@ -
|
|
1714
|
-
|
|
1715
|
-
}
|
|
1723
|
+
@@ -192,6 +197,64 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
1724
|
+
const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
|
|
1725
|
+
return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
|
|
1716
1726
|
}
|
|
1717
1727
|
+// Privateer patch: describe an error we only have the TEXT of. Mirrors
|
|
1718
1728
|
+// describeErrorText in src/engine/errors.ts — see the incident note there.
|
|
@@ -1775,7 +1785,7 @@ index a27754f..3403ca8 100644
|
|
|
1775
1785
|
export class InteractiveMode {
|
|
1776
1786
|
runtimeHost;
|
|
1777
1787
|
renderer;
|
|
1778
|
-
@@ -
|
|
1788
|
+
@@ -404,9 +467,15 @@ export class InteractiveMode {
|
|
1779
1789
|
}
|
|
1780
1790
|
getBuiltInCommandConflictDiagnostics(extensionRunner) {
|
|
1781
1791
|
const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
|
|
@@ -1792,7 +1802,7 @@ index a27754f..3403ca8 100644
|
|
|
1792
1802
|
.map((command) => ({
|
|
1793
1803
|
type: "warning",
|
|
1794
1804
|
message: command.invocationName === command.name
|
|
1795
|
-
@@ -
|
|
1805
|
+
@@ -775,20 +844,18 @@ export class InteractiveMode {
|
|
1796
1806
|
this.showNewVersionNotification(newRelease);
|
|
1797
1807
|
}
|
|
1798
1808
|
});
|
|
@@ -1825,7 +1835,7 @@ index a27754f..3403ca8 100644
|
|
|
1825
1835
|
// Check tmux keyboard setup asynchronously
|
|
1826
1836
|
this.checkTmuxKeyboardSetup().then((warning) => {
|
|
1827
1837
|
if (warning) {
|
|
1828
|
-
@@ -
|
|
1838
|
+
@@ -2419,7 +2486,17 @@ export class InteractiveMode {
|
|
1829
1839
|
if (text === "/model" || text.startsWith("/model ")) {
|
|
1830
1840
|
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
1831
1841
|
this.editor.setText("");
|
|
@@ -1844,7 +1854,7 @@ index a27754f..3403ca8 100644
|
|
|
1844
1854
|
return;
|
|
1845
1855
|
}
|
|
1846
1856
|
if (text === "/thinking" || text.startsWith("/thinking ")) {
|
|
1847
|
-
@@ -
|
|
1857
|
+
@@ -2497,12 +2574,42 @@ export class InteractiveMode {
|
|
1848
1858
|
if (text === "/login" || text.startsWith("/login ")) {
|
|
1849
1859
|
const providerRef = text.startsWith("/login ") ? text.slice(7).trim() : undefined;
|
|
1850
1860
|
this.editor.setText("");
|
|
@@ -1889,7 +1899,7 @@ index a27754f..3403ca8 100644
|
|
|
1889
1899
|
return;
|
|
1890
1900
|
}
|
|
1891
1901
|
if (text === "/new") {
|
|
1892
|
-
@@ -
|
|
1902
|
+
@@ -2827,9 +2934,19 @@ export class InteractiveMode {
|
|
1893
1903
|
if (entries[0]?.type !== "compaction") {
|
|
1894
1904
|
throw new Error("Completed compaction is missing from the session context");
|
|
1895
1905
|
}
|
|
@@ -1912,7 +1922,7 @@ index a27754f..3403ca8 100644
|
|
|
1912
1922
|
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
1913
1923
|
if (event.result.usage) {
|
|
1914
1924
|
this.addCompactionCostNotice({
|
|
1915
|
-
@@ -
|
|
1925
|
+
@@ -3253,7 +3370,7 @@ export class InteractiveMode {
|
|
1916
1926
|
if (this.chatContainer.children.length > 0) {
|
|
1917
1927
|
this.chatContainer.addChild(new Spacer(1));
|
|
1918
1928
|
}
|
|
@@ -1921,7 +1931,7 @@ index a27754f..3403ca8 100644
|
|
|
1921
1931
|
}
|
|
1922
1932
|
async getUserInput() {
|
|
1923
1933
|
const queuedInput = this.pendingUserInputs.shift();
|
|
1924
|
-
@@ -
|
|
1934
|
+
@@ -3597,7 +3714,17 @@ export class InteractiveMode {
|
|
1925
1935
|
}
|
|
1926
1936
|
showError(errorMessage) {
|
|
1927
1937
|
this.chatContainer.addChild(new Spacer(1));
|
|
@@ -1941,10 +1951,10 @@ index a27754f..3403ca8 100644
|
|
|
1941
1951
|
}
|
|
1942
1952
|
showWarning(warningMessage) {
|
|
1943
1953
|
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
|
|
1944
|
-
index
|
|
1954
|
+
index 3f4c698..e0152d0 100644
|
|
1945
1955
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json
|
|
1946
1956
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/dark.json
|
|
1947
|
-
@@ -
|
|
1957
|
+
@@ -42,9 +42,9 @@
|
|
1948
1958
|
"customMessageBg": "customMsgBg",
|
|
1949
1959
|
"customMessageText": "text",
|
|
1950
1960
|
"customMessageLabel": "#9575cd",
|
|
@@ -1958,10 +1968,10 @@ index 01d1e02..c591e1d 100644
|
|
|
1958
1968
|
"toolOutput": "gray",
|
|
1959
1969
|
|
|
1960
1970
|
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
|
|
1961
|
-
index
|
|
1971
|
+
index 20f6783..49fc151 100644
|
|
1962
1972
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json
|
|
1963
1973
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/light.json
|
|
1964
|
-
@@ -
|
|
1974
|
+
@@ -41,9 +41,9 @@
|
|
1965
1975
|
"customMessageBg": "customMsgBg",
|
|
1966
1976
|
"customMessageText": "text",
|
|
1967
1977
|
"customMessageLabel": "#7e57c2",
|
package/src/cli/chat.ts
CHANGED
|
@@ -53,8 +53,14 @@ async function main() {
|
|
|
53
53
|
const { modelRegistryOf } = await import("../providers/piAuthStore.ts");
|
|
54
54
|
const { agentVersion } = await import("../config/version.ts");
|
|
55
55
|
const { pickerCatalog, hiddenAccountNotice, hiddenAccountTitleSuffix } = await import("../providers/modelCatalog.ts");
|
|
56
|
-
const {
|
|
57
|
-
|
|
56
|
+
const {
|
|
57
|
+
resolveDefaultModel,
|
|
58
|
+
resolveSignedInModel,
|
|
59
|
+
savedPiDefaultSpec,
|
|
60
|
+
writePiDefaultModel,
|
|
61
|
+
visionWarningAcknowledged,
|
|
62
|
+
acknowledgeVisionWarning,
|
|
63
|
+
} = await import("../providers/defaultModel.ts");
|
|
58
64
|
const { acceptsImages } = await import("../providers/vision.ts");
|
|
59
65
|
const { postOutbox } = await import("../outbox/cloudOutbox.ts");
|
|
60
66
|
const { addPendingCloud } = await import("../routines/store.ts");
|
|
@@ -440,8 +446,11 @@ async function main() {
|
|
|
440
446
|
// blocks for a model that doesn't declare the modality with NO error — `read` on a
|
|
441
447
|
// screenshot just quietly answers about nothing — so make that failure visible
|
|
442
448
|
// instead of letting a user discover it turn by turn. /model to switch.
|
|
443
|
-
|
|
449
|
+
// Shown once per distinct non-vision spec (visionWarningAcknowledged), not on every
|
|
450
|
+
// launch — a user who keeps a deliberate non-vision pick already knows the tradeoff.
|
|
451
|
+
if (!acceptsImages(spec) && !visionWarningAcknowledged(spec)) {
|
|
444
452
|
console.log(`${YELLOW}⚠ ${provider}/${modelId} can't see images — @file/read on a picture or video frame will be dropped silently. Run /model to switch.${RESET}`);
|
|
453
|
+
acknowledgeVisionWarning(spec);
|
|
445
454
|
}
|
|
446
455
|
if (noQuarterActive()) applyNoQuarter(true); // launched with --no-quarter: say so up front
|
|
447
456
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Mirrors the patched clampMaxTokensToContext in pi-ai (api/simple-options.js) —
|
|
4
4
|
// that copy is what actually runs; this one is where the behaviour is specified and
|
|
5
|
-
// tested. See patches/@earendil-works+pi-ai
|
|
5
|
+
// tested. See patches/@earendil-works+pi-ai+*.patch.
|
|
6
6
|
//
|
|
7
7
|
// ── The bug this exists to fix ───────────────────────────────────────────────
|
|
8
8
|
//
|
|
@@ -227,6 +227,43 @@ export function resolveSignedInModel(env: NodeJS.ProcessEnv = process.env): stri
|
|
|
227
227
|
return resolveDefaultModel({ env, signedIn: true, saved: null });
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
// Whether the startup "can't see images" nag (cli/chat.ts) has already been shown for
|
|
231
|
+
// this exact spec. Without this, a user who deliberately keeps a non-vision saved pick
|
|
232
|
+
// (speed over sight, say) sees the same warning on every single launch forever, which
|
|
233
|
+
// trains people to stop reading warnings at all. Keyed by spec so switching to a
|
|
234
|
+
// DIFFERENT non-vision model warns again once — this tracks "have they seen this
|
|
235
|
+
// specific tradeoff", not "should we ever mention it again".
|
|
236
|
+
export function visionWarningAcknowledged(spec: string): boolean {
|
|
237
|
+
try {
|
|
238
|
+
const raw = readFileSync(join(agentDir(), "settings.json"), "utf8").trim();
|
|
239
|
+
if (!raw) return false;
|
|
240
|
+
const s = JSON.parse(raw) as Record<string, unknown>;
|
|
241
|
+
return s.visionWarningAcknowledgedFor === spec;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Record that the nag has been shown for `spec`, so the next launch on the same pick
|
|
248
|
+
// stays quiet. Best-effort and silent like the writers below — losing this write just
|
|
249
|
+
// means the warning repeats once more, never a crash.
|
|
250
|
+
export function acknowledgeVisionWarning(spec: string): void {
|
|
251
|
+
const dir = agentDir();
|
|
252
|
+
const settingsPath = join(dir, "settings.json");
|
|
253
|
+
try {
|
|
254
|
+
mkdirSync(dir, { recursive: true });
|
|
255
|
+
let settings: Record<string, unknown> = {};
|
|
256
|
+
if (existsSync(settingsPath)) {
|
|
257
|
+
const raw = readFileSync(settingsPath, "utf8").trim();
|
|
258
|
+
if (raw) settings = JSON.parse(raw) as Record<string, unknown>;
|
|
259
|
+
}
|
|
260
|
+
settings.visionWarningAcknowledgedFor = spec;
|
|
261
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
262
|
+
} catch {
|
|
263
|
+
// best-effort: the warning already printed either way.
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
230
267
|
// Split a "provider/id" spec on its first slash (model ids themselves contain "/", so
|
|
231
268
|
// only the first delimiter separates provider from model). Returns null for a spec
|
|
232
269
|
// with no provider prefix.
|
package/src/tools/media.ts
CHANGED
|
@@ -738,6 +738,10 @@ interface AudioResponse {
|
|
|
738
738
|
audioBase64?: string;
|
|
739
739
|
mimeType?: string;
|
|
740
740
|
model?: string;
|
|
741
|
+
/** The exact wire voice the provider actually received (post-resolution —
|
|
742
|
+
* e.g. an aura-2 override of 'jupiter' comes back as 'aura-2-jupiter-en').
|
|
743
|
+
* Absent only on models that take no voice at all. */
|
|
744
|
+
voice?: string;
|
|
741
745
|
/** Present only for the models that take a length — an sfx model always does. */
|
|
742
746
|
durationSeconds?: number;
|
|
743
747
|
}
|
|
@@ -749,11 +753,21 @@ export const generateSpeechToolDefinition = {
|
|
|
749
753
|
"Turn text into spoken audio and save it to disk. Use it to narrate a video you are assembling, or " +
|
|
750
754
|
"to produce a spoken version of a written answer. Billed to the user's Privateer account; the " +
|
|
751
755
|
"account's default voice model is a confidential-compute one, so the text is processed inside an " +
|
|
752
|
-
"enclave rather than by a retaining provider. Mux the result onto video with video_compose."
|
|
756
|
+
"enclave rather than by a retaining provider. Mux the result onto video with video_compose. " +
|
|
757
|
+
"Call media_capabilities first if you want to override `voice` or `model` — it lists the exact " +
|
|
758
|
+
"wire voice ids for the account's current TTS model (and any model you pass it), which is not " +
|
|
759
|
+
"the same as a spoken character or brand name.",
|
|
753
760
|
parameters: Type.Object({
|
|
754
761
|
text: Type.String({ description: "The words to speak. Write them as they should be read aloud." }),
|
|
755
762
|
path: Type.String({ description: "Where to write the audio, relative to cwd or absolute (e.g. 'audio/narration.mp3')." }),
|
|
756
|
-
voice: Type.Optional(Type.String({
|
|
763
|
+
voice: Type.Optional(Type.String({
|
|
764
|
+
description:
|
|
765
|
+
"Voice id, if the account's TTS model offers a choice. Leave unset for its default. These are " +
|
|
766
|
+
"PROVIDER wire ids, not free text — e.g. Deepgram Aura-2 voices are 'aura-2-<name>-<lang>' " +
|
|
767
|
+
"('aura-2-jupiter-en', not 'jupiter' or 'Jupiter'). Get the exact legal list for the model in " +
|
|
768
|
+
"play from media_capabilities' `speech.voices` before guessing one; an unrecognised id is " +
|
|
769
|
+
"refused with VOICE_UNSUPPORTED rather than silently served on a different voice.",
|
|
770
|
+
})),
|
|
757
771
|
model: Type.Optional(Type.String({ description: "Override the account's text-to-speech model." })),
|
|
758
772
|
}),
|
|
759
773
|
async execute(
|
|
@@ -783,7 +797,13 @@ export const generateSpeechToolDefinition = {
|
|
|
783
797
|
const target = abs(cwd, params.path);
|
|
784
798
|
const ext = extname(target) || extForMime(r.data.mimeType ?? "", ".mp3");
|
|
785
799
|
const out = `${target.slice(0, target.length - extname(target).length)}${ext}`;
|
|
786
|
-
|
|
800
|
+
// Report what actually ran, not what was requested — the two can differ
|
|
801
|
+
// (a resolved default, a normalized voice id) and the caller has no other
|
|
802
|
+
// way to find out short of listening to the file.
|
|
803
|
+
const via = [r.data.model, r.data.voice].filter(Boolean).join(" / ");
|
|
804
|
+
return text(
|
|
805
|
+
`Generated speech${via ? ` with ${via}` : ""}: ${writeOut(out, Buffer.from(r.data.audioBase64, "base64"))}`,
|
|
806
|
+
);
|
|
787
807
|
},
|
|
788
808
|
};
|
|
789
809
|
|
|
@@ -951,6 +971,20 @@ interface CapabilitiesResponse {
|
|
|
951
971
|
* the account's own privacy setting. They are different refusals with different
|
|
952
972
|
* remedies, and only one of them is the user's to fix. */
|
|
953
973
|
sfx?: { model?: string; configured?: boolean; blockedByZdr?: boolean; maxDurationSeconds?: number };
|
|
974
|
+
/** `voices` (on the described model) is populated only for models with an
|
|
975
|
+
* enumerable, closed voice set (Deepgram Aura-2, Tinfoil, fal) — pass
|
|
976
|
+
* `ttsModel` to describe one other than the account default. Empty for
|
|
977
|
+
* models whose voices aren't data this server holds (Gemini, OpenAI-shaped);
|
|
978
|
+
* guessing one there is on you. `catalog` is EVERY TTS model this account
|
|
979
|
+
* can reach, summarized — the only place other than the default to
|
|
980
|
+
* discover an id, since there is no TTS picker in this CLI. */
|
|
981
|
+
speech?: {
|
|
982
|
+
model?: string; blockedByZdr?: boolean; voices?: string[];
|
|
983
|
+
catalog?: {
|
|
984
|
+
id: string; name?: string; provider?: string;
|
|
985
|
+
isZdr?: boolean; isTee?: boolean; blockedByZdr?: boolean; voiceCount?: number;
|
|
986
|
+
}[];
|
|
987
|
+
};
|
|
954
988
|
privacy?: { requireZdr?: boolean; allowNonZdrMedia?: boolean };
|
|
955
989
|
}
|
|
956
990
|
|
|
@@ -1060,6 +1094,44 @@ export function describeSfx(sfx: CapabilitiesResponse["sfx"]): string[] {
|
|
|
1060
1094
|
];
|
|
1061
1095
|
}
|
|
1062
1096
|
|
|
1097
|
+
/**
|
|
1098
|
+
* Speech, worded the same way describeSfx is: a [BLOCKED] model is the
|
|
1099
|
+
* account's own ZDR setting, not something retrying fixes. `voices` prints
|
|
1100
|
+
* only when the model publishes an enumerable set — printing all 90 of
|
|
1101
|
+
* Aura-2's inline is the whole point (a bare character name is not a legal
|
|
1102
|
+
* id on the wire), but a model with none gets no fabricated list either.
|
|
1103
|
+
*/
|
|
1104
|
+
export function describeSpeech(speech: CapabilitiesResponse["speech"]): string[] {
|
|
1105
|
+
const blocked = speech?.blockedByZdr ? " [BLOCKED by this account's ZDR setting]" : "";
|
|
1106
|
+
const lines = [`Speech model: ${speech?.model ?? "unknown"}${blocked}`];
|
|
1107
|
+
if (speech?.blockedByZdr) {
|
|
1108
|
+
lines.push(
|
|
1109
|
+
" This voice model is non-ZDR, so this account cannot use it until its owner enables non-ZDR " +
|
|
1110
|
+
"media (Settings → Privacy). Omit `model` to use the account's confidential-compute default " +
|
|
1111
|
+
"instead of retrying this one.",
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
if (speech?.voices?.length) {
|
|
1115
|
+
lines.push(
|
|
1116
|
+
` ${speech.voices.length} voice id(s) for this model — pass one of these EXACTLY as generate_speech's ` +
|
|
1117
|
+
`\`voice\`, never a guessed name: ${speech.voices.join(", ")}`,
|
|
1118
|
+
);
|
|
1119
|
+
} else {
|
|
1120
|
+
lines.push(" No enumerable voice list for this model; omit `voice` to use its default.");
|
|
1121
|
+
}
|
|
1122
|
+
if (speech?.catalog?.length) {
|
|
1123
|
+
lines.push(" every TTS model available (pass `ttsModel` to this tool to see its voices):");
|
|
1124
|
+
for (const m of speech.catalog) {
|
|
1125
|
+
const tags = [m.isTee ? "confidential" : m.isZdr ? "ZDR" : "non-ZDR", `${m.voiceCount ?? 0} voice(s)`];
|
|
1126
|
+
if (m.blockedByZdr) tags.push("BLOCKED");
|
|
1127
|
+
lines.push(
|
|
1128
|
+
` ${m.id} (${tags.join(", ")})${m.id === speech.model ? " [described above]" : ""}`,
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return lines;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1063
1135
|
export const mediaCapabilitiesToolDefinition = {
|
|
1064
1136
|
name: "media_capabilities",
|
|
1065
1137
|
label: "Media Capabilities",
|
|
@@ -1072,7 +1144,10 @@ export const mediaCapabilitiesToolDefinition = {
|
|
|
1072
1144
|
"It is also the ONLY way to find out which 3D models exist and what options each one takes: they " +
|
|
1073
1145
|
"range from $0.14 to $2.41 a mesh and no two take the same options, so call this with `model` set " +
|
|
1074
1146
|
"to the id you are considering BEFORE generate_model, or you will pay the default model's price " +
|
|
1075
|
-
"for a job a cheaper one could have done
|
|
1147
|
+
"for a job a cheaper one could have done.\n" +
|
|
1148
|
+
"It is also the ONLY way to find a text-to-speech model's real voice ids — Deepgram Aura-2's are " +
|
|
1149
|
+
"'aura-2-<name>-<lang>', not a bare character name — so call this with `ttsModel` set BEFORE " +
|
|
1150
|
+
"generate_speech whenever you plan to pass `voice`.",
|
|
1076
1151
|
parameters: Type.Object({
|
|
1077
1152
|
model: Type.Optional(
|
|
1078
1153
|
Type.String({
|
|
@@ -1082,13 +1157,27 @@ export const mediaCapabilitiesToolDefinition = {
|
|
|
1082
1157
|
"their legal values and the price — is per-model.",
|
|
1083
1158
|
}),
|
|
1084
1159
|
),
|
|
1160
|
+
ttsModel: Type.Optional(
|
|
1161
|
+
Type.String({
|
|
1162
|
+
description:
|
|
1163
|
+
"A text-to-speech model id to describe instead of the account default (e.g. 'deepgram/aura-2'). " +
|
|
1164
|
+
"The response's `speech.voices` lists every legal voice id for THIS model — voice ids are not " +
|
|
1165
|
+
"shared across models.",
|
|
1166
|
+
}),
|
|
1167
|
+
),
|
|
1085
1168
|
}),
|
|
1086
|
-
async execute(_toolCallId: string, params: { model?: string }, signal?: AbortSignal) {
|
|
1087
|
-
const query =
|
|
1088
|
-
|
|
1169
|
+
async execute(_toolCallId: string, params: { model?: string; ttsModel?: string }, signal?: AbortSignal) {
|
|
1170
|
+
const query = new URLSearchParams();
|
|
1171
|
+
if (params?.model) query.set("model", params.model);
|
|
1172
|
+
if (params?.ttsModel) query.set("ttsModel", params.ttsModel);
|
|
1173
|
+
const qs = query.toString();
|
|
1174
|
+
const r = await callAccount<CapabilitiesResponse>(
|
|
1175
|
+
`/api/agent/media/capabilities${qs ? `?${qs}` : ""}`,
|
|
1176
|
+
{ method: "GET", signal },
|
|
1177
|
+
);
|
|
1089
1178
|
if (!r.ok) return text(`Could not read media capabilities: ${r.message}`);
|
|
1090
1179
|
|
|
1091
|
-
const { image, video, model3d, sfx, privacy } = r.data;
|
|
1180
|
+
const { image, video, model3d, sfx, speech, privacy } = r.data;
|
|
1092
1181
|
const lines = [
|
|
1093
1182
|
`Image model: ${image?.model ?? "unknown"}${image?.blockedByZdr ? " [BLOCKED by this account's ZDR setting]" : ""}`,
|
|
1094
1183
|
` up to ${image?.maxPerCall ?? 1} image(s) per call`,
|
|
@@ -1099,18 +1188,19 @@ export const mediaCapabilitiesToolDefinition = {
|
|
|
1099
1188
|
|
|
1100
1189
|
lines.push(...describeModel3d(model3d));
|
|
1101
1190
|
lines.push(...describeSfx(sfx));
|
|
1191
|
+
lines.push(...describeSpeech(speech));
|
|
1102
1192
|
|
|
1103
1193
|
lines.push(`Privacy: requireZdr=${privacy?.requireZdr ?? "?"}, allowNonZdrMedia=${privacy?.allowNonZdrMedia ?? "?"}`);
|
|
1104
|
-
if (image?.blockedByZdr || video?.blockedByZdr || model3d?.blockedByZdr || sfx?.blockedByZdr) {
|
|
1194
|
+
if (image?.blockedByZdr || video?.blockedByZdr || model3d?.blockedByZdr || sfx?.blockedByZdr || speech?.blockedByZdr) {
|
|
1105
1195
|
lines.push(
|
|
1106
1196
|
"A [BLOCKED] model means the account requires Zero Data Retention and that model has no ZDR endpoint. " +
|
|
1107
1197
|
"Only the account owner can change it (Settings → Privacy); do not keep retrying.",
|
|
1108
1198
|
);
|
|
1109
1199
|
}
|
|
1110
1200
|
lines.push(
|
|
1111
|
-
"
|
|
1112
|
-
"
|
|
1113
|
-
"
|
|
1201
|
+
"Music is always available; sound effects and (per above) speech's CURRENT model may not be. Music has " +
|
|
1202
|
+
"no ZDR gate at all; effects are gated like image and video, so a blocked effect must never be " +
|
|
1203
|
+
"answered with music instead.",
|
|
1114
1204
|
);
|
|
1115
1205
|
return text(lines.join("\n"));
|
|
1116
1206
|
},
|
|
File without changes
|
|
File without changes
|