@pi-unipi/unipi 3.0.0-alpha.31 → 3.0.0-alpha.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.0.0-alpha.32] — 2026-10-07
10
+
11
+ ### Fixed
12
+ - `app-bridge`: long sessions no longer fail to open on the phone with "hello message too large". Run logs inside tool and custom-message details (sidekick event lists, diffs) are dropped from what the phone gets, and the history is sized in UTF-8 bytes with room left for the command and model lists. (UNI-113)
13
+
9
14
  ## [3.0.0-alpha.31] — 2026-10-07
10
15
 
11
16
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/unipi",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "All-in-one extension suite for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -83,27 +83,27 @@
83
83
  "typebox": "^1.1.38"
84
84
  },
85
85
  "dependencies": {
86
- "@pi-unipi/app-bridge": "3.0.0-alpha.31",
87
- "@pi-unipi/ask-user": "3.0.0-alpha.31",
88
- "@pi-unipi/background-tasks": "3.0.0-alpha.31",
89
- "@pi-unipi/btw": "3.0.0-alpha.31",
90
- "@pi-unipi/command-enchantment": "3.0.0-alpha.31",
91
- "@pi-unipi/compactor": "3.0.0-alpha.31",
92
- "@pi-unipi/core": "3.0.0-alpha.31",
93
- "@pi-unipi/footer": "3.0.0-alpha.31",
94
- "@pi-unipi/fusion": "3.0.0-alpha.31",
95
- "@pi-unipi/info-screen": "3.0.0-alpha.31",
96
- "@pi-unipi/input-shortcuts": "3.0.0-alpha.31",
97
- "@pi-unipi/kanboard": "3.0.0-alpha.31",
98
- "@pi-unipi/mcp": "3.0.0-alpha.31",
99
- "@pi-unipi/memory": "3.0.0-alpha.31",
100
- "@pi-unipi/notify": "3.0.0-alpha.31",
101
- "@pi-unipi/skill-registry": "3.0.0-alpha.31",
102
- "@pi-unipi/subagents": "3.0.0-alpha.31",
103
- "@pi-unipi/updater": "3.0.0-alpha.31",
104
- "@pi-unipi/utility": "3.0.0-alpha.31",
105
- "@pi-unipi/web-api": "3.0.0-alpha.31",
106
- "@pi-unipi/workflow": "3.0.0-alpha.31"
86
+ "@pi-unipi/app-bridge": "3.0.0-alpha.32",
87
+ "@pi-unipi/ask-user": "3.0.0-alpha.32",
88
+ "@pi-unipi/background-tasks": "3.0.0-alpha.32",
89
+ "@pi-unipi/btw": "3.0.0-alpha.32",
90
+ "@pi-unipi/command-enchantment": "3.0.0-alpha.32",
91
+ "@pi-unipi/compactor": "3.0.0-alpha.32",
92
+ "@pi-unipi/core": "3.0.0-alpha.32",
93
+ "@pi-unipi/footer": "3.0.0-alpha.32",
94
+ "@pi-unipi/fusion": "3.0.0-alpha.32",
95
+ "@pi-unipi/info-screen": "3.0.0-alpha.32",
96
+ "@pi-unipi/input-shortcuts": "3.0.0-alpha.32",
97
+ "@pi-unipi/kanboard": "3.0.0-alpha.32",
98
+ "@pi-unipi/mcp": "3.0.0-alpha.32",
99
+ "@pi-unipi/memory": "3.0.0-alpha.32",
100
+ "@pi-unipi/notify": "3.0.0-alpha.32",
101
+ "@pi-unipi/skill-registry": "3.0.0-alpha.32",
102
+ "@pi-unipi/subagents": "3.0.0-alpha.32",
103
+ "@pi-unipi/updater": "3.0.0-alpha.32",
104
+ "@pi-unipi/utility": "3.0.0-alpha.32",
105
+ "@pi-unipi/web-api": "3.0.0-alpha.32",
106
+ "@pi-unipi/workflow": "3.0.0-alpha.32"
107
107
  },
108
108
  "devDependencies": {
109
109
  "@earendil-works/pi-agent-core": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/app-bridge",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Live bridge between a pi TUI session and the UniPi phone app — streaming chat, prompts, steering and dialogs from the phone",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@pi-unipi/core": "3.0.0-alpha.31"
34
+ "@pi-unipi/core": "3.0.0-alpha.32"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@earendil-works/pi-coding-agent": "^0.87.1"
@@ -13,7 +13,7 @@ import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync
13
13
  import { homedir } from "node:os";
14
14
  import { join } from "node:path";
15
15
  import { DialogHub, wrapUi } from "./dialogs.js";
16
- import { fitLine, phoneSafe, snapshotEntries, clipText, wantedEntry } from "./snapshot.js";
16
+ import { ENTRIES_BUDGET, LINE_BUDGET, fitLine, jsonBytes, phoneSafe, snapshotEntries, clipText, wantedEntry } from "./snapshot.js";
17
17
  import {
18
18
  BRIDGE_PROTOCOL,
19
19
  LineSplitter,
@@ -216,23 +216,24 @@ export function createBridge(pi: ExtensionAPI) {
216
216
 
217
217
  const hello = (): OutMsg => {
218
218
  const c = ctx!;
219
- const { entries, truncated } = snapshotEntries(c.sessionManager.getBranch());
220
- return {
221
- t: "hello",
219
+ const rest = {
220
+ t: "hello" as const,
222
221
  v: BRIDGE_PROTOCOL,
223
222
  pid: process.pid,
224
223
  piVersion: process.env.PI_VERSION,
225
224
  session: sessionInfo(),
226
225
  state: runState(),
227
- entries,
228
- truncated,
229
- streaming: streaming ? { id: streaming.id, role: "assistant", content: phoneSafe(streaming.content) as unknown[] } : undefined,
230
- tools: [...tools.values()],
231
- commands: commands(),
226
+ streaming: streaming ? { id: streaming.id, role: "assistant" as const, content: phoneSafe(streaming.content) as unknown[] } : undefined,
227
+ tools: [...tools.values()].map((t) => ({ ...t, text: t.text ? clipText(t.text, 8 * 1024) : t.text })),
228
+ commands: commands().map((x) => ({ ...x, description: x.description ? clipText(x.description, 160) : x.description })),
232
229
  models: models(),
233
230
  dialogs: hub.list(),
234
231
  queue: [...queue],
235
232
  };
233
+ // Entries get whatever the rest of the hello leaves of the line budget.
234
+ const spare = LINE_BUDGET - jsonBytes(rest) - 64 * 1024;
235
+ const { entries, truncated } = snapshotEntries(c.sessionManager.getBranch(), Math.max(64 * 1024, Math.min(ENTRIES_BUDGET, spare)));
236
+ return { ...rest, entries, truncated };
236
237
  };
237
238
 
238
239
  const writeRecord = () => {
@@ -3,8 +3,39 @@
3
3
  * text clipped, the hello snapshot capped so its line fits one relay frame.
4
4
  */
5
5
 
6
- /** One relay frame is 1 MiB; keep every line well under it. */
6
+ /** One relay frame is 1 MiB; keep every line well under it (UTF-8 bytes). */
7
7
  export const LINE_BUDGET = 900 * 1024;
8
+ /** The entries of a hello snapshot get this much; the rest of the hello
9
+ * (commands, models, state) fits in what is left of LINE_BUDGET. */
10
+ export const ENTRIES_BUDGET = 600 * 1024;
11
+ /** Tool-result / custom-message `details` kept per entry: the phone reads
12
+ * only small scalar fields (status, exit code, durations, usage, report text). */
13
+ export const DETAILS_CLIP = 4 * 1024;
14
+ /** Fields of `details` the phone never shows, dropped outright (run logs). */
15
+ const DETAILS_DROP = new Set(["events", "messages", "transcript", "steps", "history", "output", "fullOutput", "raw", "diff", "patch"]);
16
+
17
+ /** UTF-8 size of a JSON value as sent. */
18
+ export function jsonBytes(value: unknown): number {
19
+ return Buffer.byteLength(JSON.stringify(value) ?? "null", "utf8");
20
+ }
21
+
22
+ /** A small, phone-useful copy of a `details` object: run logs dropped, strings clipped, total capped. */
23
+ export function slimDetails(details: unknown): Json {
24
+ if (!details || typeof details !== "object" || Array.isArray(details)) return phoneSafe(details, 2 * 1024);
25
+ const out: Record<string, Json> = {};
26
+ let used = 0;
27
+ for (const [k, v] of Object.entries(details as Record<string, unknown>)) {
28
+ if (DETAILS_DROP.has(k) || typeof v === "function" || v === undefined) continue;
29
+ // Report-like text the phone shows in a card keeps more room.
30
+ const max = k === "report" || k === "text" ? 32 * 1024 : 2 * 1024;
31
+ const safe = phoneSafe(v, max, 1);
32
+ const size = jsonBytes(safe);
33
+ if (k !== "report" && k !== "text" && used + size > DETAILS_CLIP) continue;
34
+ out[k] = safe;
35
+ used += size;
36
+ }
37
+ return out;
38
+ }
8
39
  /** Longest text block kept inside one entry. */
9
40
  export const TEXT_CLIP = 64 * 1024;
10
41
  /** Tool-result text kept per entry (the phone shows a tail anyway). */
@@ -35,6 +66,11 @@ export function phoneSafe(value: unknown, textMax = TEXT_CLIP, depth = 0): Json
35
66
  const out: Record<string, Json> = {};
36
67
  for (const [k, v] of Object.entries(o)) {
37
68
  if (typeof v === "function" || v === undefined) continue;
69
+ // Message / entry `details` (tool results, custom messages): slimmed.
70
+ if (k === "details" && depth <= 2) {
71
+ out[k] = slimDetails(v);
72
+ continue;
73
+ }
38
74
  // Tool results: clip their text harder than prose.
39
75
  const max = k === "content" && o.role === "toolResult" ? TOOL_TEXT_CLIP : textMax;
40
76
  out[k] = phoneSafe(v, max, depth + 1);
@@ -83,7 +119,7 @@ export function editedAway(branch: readonly unknown[]): Set<string> {
83
119
  * included), phone-safe, trimmed from the oldest end until the serialized
84
120
  * size fits `budget`.
85
121
  */
86
- export function snapshotEntries(branch: readonly unknown[], budget = LINE_BUDGET): { entries: Json[]; truncated: boolean } {
122
+ export function snapshotEntries(branch: readonly unknown[], budget = ENTRIES_BUDGET): { entries: Json[]; truncated: boolean } {
87
123
  let start = 0;
88
124
  for (let i = branch.length - 1; i >= 0; i--) {
89
125
  if ((branch[i] as { type?: string })?.type === "compaction") {
@@ -97,7 +133,7 @@ export function snapshotEntries(branch: readonly unknown[], budget = LINE_BUDGET
97
133
  .filter((e) => wantedEntry(e) && !hidden.has((e as { id?: string }).id ?? ""))
98
134
  .map((e) => phoneSafe(e));
99
135
  let truncated = start > 0;
100
- const sizes = picked.map((e) => JSON.stringify(e).length + 1);
136
+ const sizes = picked.map((e) => jsonBytes(e) + 1);
101
137
  let total = sizes.reduce((a, b) => a + b, 0);
102
138
  let from = 0;
103
139
  while (total > budget && from < picked.length - 1) {
@@ -116,9 +152,9 @@ export function snapshotEntries(branch: readonly unknown[], budget = LINE_BUDGET
116
152
  /** A line that is sure to fit the relay frame (clip harder, then give up on the payload). */
117
153
  export function fitLine(msg: object): string {
118
154
  let line = JSON.stringify(msg);
119
- if (line.length <= LINE_BUDGET) return line;
155
+ if (Buffer.byteLength(line, "utf8") <= LINE_BUDGET) return line;
120
156
  line = JSON.stringify(phoneSafe(msg, 8 * 1024));
121
- if (line.length <= LINE_BUDGET) return line;
157
+ if (Buffer.byteLength(line, "utf8") <= LINE_BUDGET) return line;
122
158
  const t = (msg as { t?: string }).t ?? "unknown";
123
159
  return JSON.stringify({ t: "error", message: `A ${t} message was too large to send to the phone.` });
124
160
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/ask-user",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Structured user input tool for Pi coding agent — single-select, multi-select, freeform",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@pi-unipi/core": "3.0.0-alpha.31"
43
+ "@pi-unipi/core": "3.0.0-alpha.32"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/command-enchantment",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Enhanced TUI autocomplete for /unipi:* commands — colored, sorted, and grouped by package",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/background-tasks",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Background tasks for UniPi — durable shell jobs and delegated background agents with a footer dock",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@earendil-works/pi-ai": "^0.87.1",
13
- "@pi-unipi/core": "3.0.0-alpha.31",
13
+ "@pi-unipi/core": "3.0.0-alpha.32",
14
14
  "turndown": "^7.2.4"
15
15
  },
16
16
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/btw",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "A pi extension for parallel side conversations with /unipi:btw — part of the Unipi suite",
5
5
  "type": "module",
6
6
  "main": "extensions/btw.ts",
@@ -37,7 +37,7 @@
37
37
  "@earendil-works/pi-tui": "^0.87.1"
38
38
  },
39
39
  "dependencies": {
40
- "@pi-unipi/core": "3.0.0-alpha.31"
40
+ "@pi-unipi/core": "3.0.0-alpha.32"
41
41
  },
42
42
  "pi": {
43
43
  "extensions": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/compactor",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Compaction for Pi — lossless zero-LLM or model summaries, rebuilt from full history, with session recall",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -33,8 +33,8 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@pi-unipi/core": "3.0.0-alpha.31",
37
- "@pi-unipi/info-screen": "3.0.0-alpha.31",
36
+ "@pi-unipi/core": "3.0.0-alpha.32",
37
+ "@pi-unipi/info-screen": "3.0.0-alpha.32",
38
38
  "@earendil-works/pi-agent-core": "^0.87.1"
39
39
  },
40
40
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/core",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Shared utilities, event types, and constants for Unipi extension suite",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/footer",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Persistent status bar for Unipi — subscribes to UNIPI_EVENTS and renders key stats from all unipi packages",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -32,8 +32,8 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/core": "3.0.0-alpha.31",
36
- "@pi-unipi/background-tasks": "3.0.0-alpha.31"
35
+ "@pi-unipi/core": "3.0.0-alpha.32",
36
+ "@pi-unipi/background-tasks": "3.0.0-alpha.32"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/fusion",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Devin-style model picker, fusion presets (lead + sidekick), and Local Fusion runtime for UniPi",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -32,9 +32,9 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/core": "3.0.0-alpha.31",
36
- "@pi-unipi/subagents": "3.0.0-alpha.31",
37
- "@pi-unipi/utility": "3.0.0-alpha.31"
35
+ "@pi-unipi/core": "3.0.0-alpha.32",
36
+ "@pi-unipi/subagents": "3.0.0-alpha.32",
37
+ "@pi-unipi/utility": "3.0.0-alpha.32"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-ai": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/info-screen",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Unicrab startup splash and the /unipi:info dashboard — this session, context, usage, compaction savings",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -33,7 +33,7 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@pi-unipi/core": "3.0.0-alpha.31"
36
+ "@pi-unipi/core": "3.0.0-alpha.32"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/input-shortcuts",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle — chord-based overlay system",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -33,7 +33,7 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@pi-unipi/core": "3.0.0-alpha.31"
36
+ "@pi-unipi/core": "3.0.0-alpha.32"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/kanboard",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Kanboard v3 — deferred-work board: CLI/daemon bridge, task runner and skill",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@pi-unipi/core": "3.0.0-alpha.31"
34
+ "@pi-unipi/core": "3.0.0-alpha.32"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -48,10 +48,10 @@
48
48
  "themes": []
49
49
  },
50
50
  "optionalDependencies": {
51
- "@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.31",
52
- "@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.31",
53
- "@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.31",
54
- "@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.31",
55
- "@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.31"
51
+ "@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.32",
52
+ "@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.32",
53
+ "@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.32",
54
+ "@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.32",
55
+ "@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.32"
56
56
  }
57
57
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/long-horizon",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Long-horizon execution for Pi — mode-gated /goal /ralph /swarm /graph orchestration with a TypeSafe prompt judge",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -37,7 +37,7 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@pi-unipi/core": "3.0.0-alpha.31"
40
+ "@pi-unipi/core": "3.0.0-alpha.32"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/mcp",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "MCP server management extension for Pi coding agent — browse, add, configure, and use MCP servers",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "test": "npx tsx --test tests/**/*.test.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@pi-unipi/core": "3.0.0-alpha.31"
33
+ "@pi-unipi/core": "3.0.0-alpha.32"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/memory",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Persistent cross-session memory on MemPalace (auto-installed): daemon-routed writes, shared palace with other agents, for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -43,8 +43,8 @@
43
43
  "README.md"
44
44
  ],
45
45
  "dependencies": {
46
- "@pi-unipi/core": "3.0.0-alpha.31",
47
- "@pi-unipi/info-screen": "3.0.0-alpha.31",
46
+ "@pi-unipi/core": "3.0.0-alpha.32",
47
+ "@pi-unipi/info-screen": "3.0.0-alpha.32",
48
48
  "js-yaml": "^4.1.0"
49
49
  },
50
50
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/notify",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Cross-platform notification extension for Pi — native OS, Gotify, and Telegram notifications for agent lifecycle events",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.31",
37
+ "@pi-unipi/core": "3.0.0-alpha.32",
38
38
  "node-notifier": "^10.0.1"
39
39
  },
40
40
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/skill-registry",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Skill registry for pi — per-project skill on/off, a skill vault, and jev-judged skill exposure",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "main": "index.ts",
35
35
  "dependencies": {
36
- "@pi-unipi/core": "3.0.0-alpha.31"
36
+ "@pi-unipi/core": "3.0.0-alpha.32"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/subagents",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Subagents for UniPi — Devin-model independent child agents",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -9,8 +9,8 @@
9
9
  "test": "npx tsx --test src/__tests__/*.test.ts"
10
10
  },
11
11
  "dependencies": {
12
- "@pi-unipi/core": "3.0.0-alpha.31",
13
- "@pi-unipi/utility": "3.0.0-alpha.31",
12
+ "@pi-unipi/core": "3.0.0-alpha.32",
13
+ "@pi-unipi/utility": "3.0.0-alpha.32",
14
14
  "@earendil-works/pi-agent-core": "^0.87.1",
15
15
  "acorn": "8.18.0"
16
16
  },
@@ -53637,6 +53637,27 @@ function wrapUi(ui, hub) {
53637
53637
 
53638
53638
  // packages/app-bridge/src/snapshot.ts
53639
53639
  var LINE_BUDGET = 900 * 1024;
53640
+ var ENTRIES_BUDGET = 600 * 1024;
53641
+ var DETAILS_CLIP = 4 * 1024;
53642
+ var DETAILS_DROP = /* @__PURE__ */ new Set(["events", "messages", "transcript", "steps", "history", "output", "fullOutput", "raw", "diff", "patch"]);
53643
+ function jsonBytes(value) {
53644
+ return Buffer.byteLength(JSON.stringify(value) ?? "null", "utf8");
53645
+ }
53646
+ function slimDetails(details) {
53647
+ if (!details || typeof details !== "object" || Array.isArray(details)) return phoneSafe(details, 2 * 1024);
53648
+ const out = {};
53649
+ let used = 0;
53650
+ for (const [k, v] of Object.entries(details)) {
53651
+ if (DETAILS_DROP.has(k) || typeof v === "function" || v === void 0) continue;
53652
+ const max = k === "report" || k === "text" ? 32 * 1024 : 2 * 1024;
53653
+ const safe = phoneSafe(v, max, 1);
53654
+ const size = jsonBytes(safe);
53655
+ if (k !== "report" && k !== "text" && used + size > DETAILS_CLIP) continue;
53656
+ out[k] = safe;
53657
+ used += size;
53658
+ }
53659
+ return out;
53660
+ }
53640
53661
  var TEXT_CLIP = 64 * 1024;
53641
53662
  var TOOL_TEXT_CLIP = 16 * 1024;
53642
53663
  function clipText(text, max) {
@@ -53662,6 +53683,10 @@ function phoneSafe(value, textMax = TEXT_CLIP, depth = 0) {
53662
53683
  const out = {};
53663
53684
  for (const [k, v] of Object.entries(o)) {
53664
53685
  if (typeof v === "function" || v === void 0) continue;
53686
+ if (k === "details" && depth <= 2) {
53687
+ out[k] = slimDetails(v);
53688
+ continue;
53689
+ }
53665
53690
  const max = k === "content" && o.role === "toolResult" ? TOOL_TEXT_CLIP : textMax;
53666
53691
  out[k] = phoneSafe(v, max, depth + 1);
53667
53692
  }
@@ -53694,7 +53719,7 @@ function editedAway(branch) {
53694
53719
  }
53695
53720
  return hidden;
53696
53721
  }
53697
- function snapshotEntries(branch, budget = LINE_BUDGET) {
53722
+ function snapshotEntries(branch, budget = ENTRIES_BUDGET) {
53698
53723
  let start = 0;
53699
53724
  for (let i = branch.length - 1; i >= 0; i--) {
53700
53725
  if (branch[i]?.type === "compaction") {
@@ -53705,7 +53730,7 @@ function snapshotEntries(branch, budget = LINE_BUDGET) {
53705
53730
  const hidden = editedAway(branch);
53706
53731
  const picked = branch.slice(start).filter((e) => wantedEntry(e) && !hidden.has(e.id ?? "")).map((e) => phoneSafe(e));
53707
53732
  let truncated = start > 0;
53708
- const sizes = picked.map((e) => JSON.stringify(e).length + 1);
53733
+ const sizes = picked.map((e) => jsonBytes(e) + 1);
53709
53734
  let total = sizes.reduce((a, b) => a + b, 0);
53710
53735
  let from = 0;
53711
53736
  while (total > budget && from < picked.length - 1) {
@@ -53721,9 +53746,9 @@ function snapshotEntries(branch, budget = LINE_BUDGET) {
53721
53746
  }
53722
53747
  function fitLine(msg) {
53723
53748
  let line = JSON.stringify(msg);
53724
- if (line.length <= LINE_BUDGET) return line;
53749
+ if (Buffer.byteLength(line, "utf8") <= LINE_BUDGET) return line;
53725
53750
  line = JSON.stringify(phoneSafe(msg, 8 * 1024));
53726
- if (line.length <= LINE_BUDGET) return line;
53751
+ if (Buffer.byteLength(line, "utf8") <= LINE_BUDGET) return line;
53727
53752
  const t = msg.t ?? "unknown";
53728
53753
  return JSON.stringify({ t: "error", message: `A ${t} message was too large to send to the phone.` });
53729
53754
  }
@@ -53957,23 +53982,23 @@ function createBridge(pi) {
53957
53982
  };
53958
53983
  const hello = () => {
53959
53984
  const c2 = ctx;
53960
- const { entries, truncated } = snapshotEntries(c2.sessionManager.getBranch());
53961
- return {
53985
+ const rest = {
53962
53986
  t: "hello",
53963
53987
  v: BRIDGE_PROTOCOL,
53964
53988
  pid: process.pid,
53965
53989
  piVersion: process.env.PI_VERSION,
53966
53990
  session: sessionInfo(),
53967
53991
  state: runState(),
53968
- entries,
53969
- truncated,
53970
53992
  streaming: streaming ? { id: streaming.id, role: "assistant", content: phoneSafe(streaming.content) } : void 0,
53971
- tools: [...tools.values()],
53972
- commands: commands(),
53993
+ tools: [...tools.values()].map((t) => ({ ...t, text: t.text ? clipText(t.text, 8 * 1024) : t.text })),
53994
+ commands: commands().map((x) => ({ ...x, description: x.description ? clipText(x.description, 160) : x.description })),
53973
53995
  models: models(),
53974
53996
  dialogs: hub.list(),
53975
53997
  queue: [...queue]
53976
53998
  };
53999
+ const spare = LINE_BUDGET - jsonBytes(rest) - 64 * 1024;
54000
+ const { entries, truncated } = snapshotEntries(c2.sessionManager.getBranch(), Math.max(64 * 1024, Math.min(ENTRIES_BUDGET, spare)));
54001
+ return { ...rest, entries, truncated };
53977
54002
  };
53978
54003
  const writeRecord = () => {
53979
54004
  if (!ctx || !socketPath) return;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/updater",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Auto-updater, changelog browser, and readme browser for Unipi — checks npm registry, renders CHANGELOG.md and README.md files in TUI overlays",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.31"
37
+ "@pi-unipi/core": "3.0.0-alpha.32"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/utility",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Utility commands and tools for Pi coding agent — lifecycle, diagnostics, cache, analytics, display, batch execution",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -36,7 +36,7 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@pi-unipi/core": "3.0.0-alpha.31"
39
+ "@pi-unipi/core": "3.0.0-alpha.32"
40
40
  },
41
41
  "devDependencies": {},
42
42
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/watchdog",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Jev watchdog for long-running tool calls and background tasks — judges stuck work via TypeSafe System One and kills or warns",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -32,8 +32,8 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/background-tasks": "3.0.0-alpha.31",
36
- "@pi-unipi/core": "3.0.0-alpha.31"
35
+ "@pi-unipi/background-tasks": "3.0.0-alpha.32",
36
+ "@pi-unipi/core": "3.0.0-alpha.32"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/web-api",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Web search, read, and summarize tools with provider-based backend selection for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -31,7 +31,7 @@
31
31
  "README.md"
32
32
  ],
33
33
  "dependencies": {
34
- "@pi-unipi/core": "3.0.0-alpha.31",
34
+ "@pi-unipi/core": "3.0.0-alpha.32",
35
35
  "defuddle": "^0.18.1",
36
36
  "linkedom": "^0.18.12",
37
37
  "wreq-js": "^2.3.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/workflow",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.32",
4
4
  "description": "Plan mode and permission modes for the Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -27,7 +27,7 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@pi-unipi/core": "3.0.0-alpha.31"
30
+ "@pi-unipi/core": "3.0.0-alpha.32"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@earendil-works/pi-ai": "^0.87.1",