privateer-agent 0.12.53 → 0.12.55

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.
@@ -285,9 +285,10 @@ function contextLine(p: Palette): string {
285
285
  // or three things a returning user should notice. `cmd`, when present, is rendered in the
286
286
  // accent color so the actionable bit stands out from the prose. Trim this as it ages.
287
287
  const WHATS_NEW: Array<{ text: string; cmd?: string }> = [
288
- { text: "Privateer agent CLI is live —", cmd: "npm i -g privateer-agent" },
289
- { text: "PRIVATEER.md project context —", cmd: "/init" },
290
- { text: "Self-update built in —", cmd: "privateer update" },
288
+ { text: "Searchable model picker —", cmd: "/models" },
289
+ { text: "Start over with a new agent —", cmd: "/fresh" },
290
+ { text: "Text-only models can read images now" },
291
+ { text: "Live crypto prices and Solana wallets" },
291
292
  ];
292
293
 
293
294
  function whatsNewRows(p: Palette): string[] {
@@ -25,6 +25,7 @@ import {
25
25
  RUNTIME_GUIDELINES_MARKER,
26
26
  runtimeGuidelinesBlock,
27
27
  } from "../src/context.ts";
28
+ import privateerIdentity from "../src/identity.ts";
28
29
 
29
30
  // Honor Pi's own "disable context files" switch, so --no-context-files / -nc silences
30
31
  // everything this extension injects, not just PRIVATEER.md — otherwise the flag would
@@ -33,6 +34,10 @@ const CONTEXT_FILES_DISABLED =
33
34
  process.argv.includes("--no-context-files") || process.argv.includes("-nc");
34
35
 
35
36
  export default function privateerContext(pi: any): void {
37
+ // Privateer's name and CLI in place of Pi's (src/identity.ts). Its own handler, ahead
38
+ // of the -nc gate below: that flag silences context files, not which command is safe.
39
+ privateerIdentity(pi);
40
+
36
41
  // Inject the runtime guidelines + PRIVATEER.md into every turn's system prompt. The
37
42
  // prompt is rebuilt per turn and chained across before_agent_start handlers, so
38
43
  // appending here is idempotent for the turn; each marker guard makes its own block a
@@ -41,7 +41,6 @@ import {
41
41
  fetchAccountCatalog,
42
42
  } from "../src/providers/account.ts";
43
43
  import { writePiDefaultModel } from "../src/providers/defaultModel.ts";
44
- import { acceptsImages } from "../src/providers/vision.ts";
45
44
 
46
45
  // A minimal view of Pi's theme (passed to the ui.custom factory) — enough to color
47
46
  // text without pulling Pi's internal theme types into an extension.
@@ -462,9 +461,6 @@ export default function privateerModels(pi: {
462
461
  `Model: ${match.provider}/${match.id} · ${TIERS[tier].label}`,
463
462
  "info",
464
463
  );
465
- if (!acceptsImages(`${match.provider}/${match.id}`)) {
466
- ui?.notify?.(`${match.provider}/${match.id} can't see images — files will be dropped silently.`, "warning");
467
- }
468
464
  return;
469
465
  } catch (e) {
470
466
  ui?.notify?.(`Couldn't switch model: ${(e as Error).message}`, "error");
@@ -511,9 +507,6 @@ export default function privateerModels(pi: {
511
507
  `Model: ${chosen.provider}/${chosen.id} · ${TIERS[chosen.tier].label}`,
512
508
  "info",
513
509
  );
514
- if (!acceptsImages(`${chosen.provider}/${chosen.id}`)) {
515
- ui.notify?.(`${chosen.provider}/${chosen.id} can't see images — files will be dropped silently.`, "warning");
516
- }
517
510
  } catch (e) {
518
511
  ui.notify?.(`Couldn't switch model: ${(e as Error).message}`, "error");
519
512
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateer-agent",
3
- "version": "0.12.53",
3
+ "version": "0.12.55",
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",
package/src/cli/chat.ts CHANGED
@@ -754,9 +754,6 @@ async function main() {
754
754
  writePiDefaultModel(sp);
755
755
  const m = `model → ${sp}`;
756
756
  console.log(`${DIM}${m}${RESET}`);
757
- if (!acceptsImages(sp)) {
758
- console.log(`${YELLOW}⚠ ${sp} can't see images — @file/read on a picture will be dropped silently.${RESET}`);
759
- }
760
757
  relay?.sendContext({ model: currentSpec, cwd, version: agentVersion() }); // banner follows the switch
761
758
  if (remote) relay?.sendNotice(m);
762
759
  } catch (e) {
@@ -291,7 +291,11 @@ export async function buildMoat(opts: MoatOptions): Promise<ExtensionFactory[]>
291
291
 
292
292
  if (caps.context) {
293
293
  const { default: privateerContext } = await import("../../extensions/privateer-context.ts");
294
- factories.push(privateerContext);
294
+ factories.push(privateerContext); // carries privateerIdentity itself
295
+ } else {
296
+ // Every kind: the model is told it runs Privateer, never `pi`. See src/identity.ts.
297
+ const { default: privateerIdentity } = await import("../identity.ts");
298
+ factories.push(privateerIdentity);
295
299
  }
296
300
 
297
301
  if (caps.spawnSkills) {
@@ -0,0 +1,58 @@
1
+ // Who the model thinks it is, and which command it reaches for.
2
+ //
3
+ // THE PROBLEM. Pi's stock system prompt opens "You are an expert coding assistant
4
+ // operating inside pi" and points the model at Pi's own README for questions about
5
+ // "pi itself" — a README that documents `pi -p`. So an agent asked to run a one-shot
6
+ // job, script itself, or hand work to another agent process typed `pi -p "…"`. That is
7
+ // not a cosmetic slip: bare `pi` runs with none of the moat — no permission gate, no
8
+ // --allow-spend / --approve-in-app, no privacy routing, no account — so the one
9
+ // invocation that most needs the spending gate was the one that skipped it.
10
+ //
11
+ // THE FIX. Rewrite Pi's self-description to name Privateer, relabel the Pi docs as the
12
+ // SDK docs they are (still the right reference for writing extensions), and append a
13
+ // short block stating the CLI the model must use. Registered on every surface: the
14
+ // discovered privateer-context shim (TUI, -p, subagent children, desktop) and the
15
+ // in-code moat for kinds that don't load that shim (src/config/moat.ts).
16
+ //
17
+ // Same "append, or say nothing" contract as privateer-context: a host that hands us no
18
+ // prompt string gets no rewrite, and a prompt already carrying the marker is left alone.
19
+
20
+ export const IDENTITY_MARKER = "<!-- privateer:identity -->";
21
+
22
+ const PI_PREAMBLE = "operating inside pi, a coding agent harness.";
23
+ const PI_DOCS_HEADER =
24
+ "Pi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):";
25
+
26
+ export function privateerCliBlock(cmd: string = process.env.PRIVATEER_CMD || "privateer"): string {
27
+ return `\n\n${IDENTITY_MARKER}\n<privateer_cli>
28
+ You are running inside Privateer; its command is \`${cmd}\`. Privateer is built on the Pi SDK, but \`pi\` is NOT a command you may use: never run \`pi\`, \`pi -p\` or any other \`pi …\` invocation. It bypasses Privateer's permission gate, spending caps, privacy routing and account. Wherever Pi's docs show \`pi\`, the command here is \`${cmd}\`.
29
+ - One-shot / headless run (a script, a cron job, handing a task to a separate agent process): \`${cmd} -p "…"\`. It has no screen, so anything that needs approval is DENIED, including every billed media tool.
30
+ - Let that run spend on named billed tools, capped: \`${cmd} -p --allow-spend <tool[,tool…]> --max-calls <n> [--max-spend <usd>] "…"\`
31
+ - Or send each approval to the user's Privateer app: \`${cmd} -p --approve-in-app [--approval-timeout <seconds>] "…"\`
32
+ - A program that must answer approvals itself drives Privateer over ACP: \`${cmd} acp\`.
33
+ - For a sub-task inside this session, use a subagent tool if one is available rather than shelling out to a new process.
34
+ </privateer_cli>\n`;
35
+ }
36
+
37
+ /** Rewrite Pi's self-description and append the CLI block. Returns `prompt` unchanged when already applied. */
38
+ export function applyIdentity(prompt: string, cmd?: string): string {
39
+ if (prompt.includes(IDENTITY_MARKER)) return prompt;
40
+ const out = prompt
41
+ .replace(PI_PREAMBLE, "operating inside Privateer, a privacy-first coding agent built on the Pi SDK.")
42
+ .replace(
43
+ PI_DOCS_HEADER,
44
+ "Pi SDK documentation (Privateer is built on Pi; read only when asked about writing extensions, themes, skills, prompt templates or TUI components — every `pi` command in these docs is `privateer` here):",
45
+ );
46
+ return out + privateerCliBlock(cmd);
47
+ }
48
+
49
+ /** A before_agent_start extension that applies the identity rewrite. */
50
+ export default function privateerIdentity(pi: any): void {
51
+ pi.on("before_agent_start", (event: any) => {
52
+ const base = event?.systemPrompt;
53
+ if (typeof base !== "string") return;
54
+ const prompt = applyIdentity(base);
55
+ if (prompt === base) return;
56
+ return { systemPrompt: prompt };
57
+ });
58
+ }