@pi-unipi/unipi 3.0.0-alpha.0 → 3.0.0-alpha.2

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +2 -2
  3. package/package.json +29 -29
  4. package/packages/ask-user/package.json +5 -5
  5. package/packages/autocomplete/package.json +4 -4
  6. package/packages/autocomplete/src/constants.ts +13 -11
  7. package/packages/background-tasks/package.json +6 -6
  8. package/packages/btw/package.json +6 -6
  9. package/packages/compactor/package.json +7 -7
  10. package/packages/core/package.json +5 -5
  11. package/packages/core/src/settings/migrations.ts +15 -0
  12. package/packages/core/src/workspace/state-migration.ts +21 -10
  13. package/packages/footer/package.json +6 -6
  14. package/packages/footer/src/segments/memory.ts +23 -0
  15. package/packages/fusion/package.json +6 -6
  16. package/packages/image/package.json +5 -5
  17. package/packages/info-screen/package.json +5 -5
  18. package/packages/input-shortcuts/package.json +5 -5
  19. package/packages/kanboard/index.ts +3 -0
  20. package/packages/kanboard/package.json +9 -9
  21. package/packages/kanboard/src/commands.ts +5 -8
  22. package/packages/long-horizon/package.json +5 -5
  23. package/packages/mcp/package.json +5 -5
  24. package/packages/memory/README.md +102 -132
  25. package/packages/memory/commands.ts +262 -127
  26. package/packages/memory/convert.ts +1072 -0
  27. package/packages/memory/daemon.ts +312 -0
  28. package/packages/memory/files.ts +186 -0
  29. package/packages/memory/index.ts +306 -215
  30. package/packages/memory/mempalace.ts +140 -863
  31. package/packages/memory/package.json +15 -12
  32. package/packages/memory/paths.ts +95 -0
  33. package/packages/memory/pending.ts +78 -0
  34. package/packages/memory/reader.ts +272 -0
  35. package/packages/memory/session.ts +387 -0
  36. package/packages/memory/settings.ts +69 -262
  37. package/packages/memory/skills/memory/SKILL.md +59 -140
  38. package/packages/memory/tools.ts +344 -326
  39. package/packages/notify/package.json +5 -5
  40. package/packages/skill-registry/package.json +1 -1
  41. package/packages/subagents/package.json +6 -6
  42. package/packages/unipi/bundled.js +4081 -2914
  43. package/packages/updater/package.json +5 -5
  44. package/packages/utility/package.json +5 -5
  45. package/packages/watchdog/package.json +6 -6
  46. package/packages/web-api/package.json +4 -4
  47. package/packages/workflow/package.json +5 -5
  48. package/packages/memory/bridge/mempalace_bridge.py +0 -683
  49. package/packages/memory/embedding.ts +0 -222
  50. package/packages/memory/storage.ts +0 -609
package/CHANGELOG.md CHANGED
@@ -6,6 +6,37 @@ 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.2] — 2026-09-26
10
+
11
+ ### Fixed
12
+
13
+ - **kanboard: the crate reports the right version.** `crates/kanboard/Cargo.toml` (and its `Cargo.lock`) stayed at alpha.0 through the alpha.1 bump, so `unipi-kanboard --version` lied. `scripts/sync-pins.mjs` is now the single bump entry point — one run sets every `version` field (root, workspaces, `packages/kanboard-bin/<platform>`) and the `[package] version =` line of every `crates/*/Cargo.toml`.
14
+ - **memory: the migration counter now counts every finished record.** `/unipi:memory status` and the completion notify showed `done 4281/4288` on a clean run because records with no old drawer were finished without incrementing `done`. A clean migration now ends `4288/4288 · 0 failed` (failed units reported separately as `· N failed`).
15
+ - **tests: no more real-HOME/env leakage.** The workflow permission-settings test and the utility skill-judge test now sandbox `HOME` and clear `OPENROUTER_API_KEY`/`TYPESAFE_API_KEY`, so `npm test` passes in a normal shell; the kanboard binary-resolution test honors the env → platform-package → dev-build precedence when a published platform package is installed.
16
+
17
+ ### Removed
18
+
19
+ - `scripts/test-subagents-tarball.mjs` — stale v2-era script expecting a `dist/` build `@pi-unipi/subagents` never produces (the package ships `src/`); unreferenced by package.json, CI, or the release chore.
20
+
21
+ ## [3.0.0-alpha.1] — 2026-09-25
22
+
23
+ ### Breaking Changes
24
+
25
+ - **BREAKING: `memory` is rebuilt on upstream MemPalace 3.10** — the Python bridge (`mempalace_bridge.py`), SQLite/embedding backends, and the direct-`upsert` write path are gone. **Migration path:** v2 data is never converted automatically — pi shows `v2 memories found — run /unipi:memory migrate` on the session card + footer, and `/unipi:memory migrate` prints a plan, asks to confirm, then converts with backups (`~/.mempalace/palace.bak-unipi-<ts>` + `~/.unipi/memory-v2-backup-<ts>`). Rollback instructions live in `packages/memory/README.md` ("Going back to v2").
26
+ - **BREAKING: memory embedding settings removed** — `provider`, `model`, `baseUrl`, `dimensions`, `apiKey`, `suppressMigrationWarning`, the re-embed action, and `/unipi:memory-reembed` are gone; MemPalace embeds itself. Migration path: none needed — stale settings are ignored.
27
+ - **BREAKING: `memory.autoStartDaemon` now defaults OFF** — v3 writes directly (`mempalace mine`, honoring the user's `write_routing`) unless a daemon is already running, so MemPalace MCP servers in other tools are unaffected. Turn it on in `/unipi:settings` → Memory if you want parallel pi sessions to never collide.
28
+ - **BREAKING: pi SDK floor raised to `^0.87.1`** — all `@earendil-works/pi-*` pins moved to 0.87.1 (extension boundary events, `SessionManager`-canonical context). Older pi releases still load most extensions but npm flags the peer mismatch; pin `@pi-unipi/*@3.0.0-alpha.0` to stay on 0.86.
29
+ - `core`: `~/.unipi/state/fusion` and `~/.unipi/trajectory` are **archived, never purged** — they move to `~/.unipi/v2-archive/<name>-<ts>/` on first v3 start (v2 may still write them on shared machines; delete the archive by hand when you're sure).
30
+
31
+ ### Added
32
+
33
+ - **memory: the whole module rewritten onto MemPalace's native model.** Markdown files at `~/.unipi/memory/<project>/<type>/<id>.md` (type ∈ `preference | decision | pattern | summary`, wing = sanitized project name) are the durable tier; a per-project `mempalace.yaml` maps the type rooms and a `.gitignore` (`/*.md`) keeps whole-dir mines away from legacy flat files. Writes go through the MemPalace daemon's `/jobs` queue (`mine` jobs for store, `mempalace_delete_by_source` for delete); with no daemon pi writes directly via `mempalace mine` (upstream "prefer" routing) or a one-shot write-mode MCP server, and failures land in `.pending.json` (replayed at session start, after writes, and every 2 min). Reads go through one long-lived read-only `mempalace-mcp` stdio process (~33 ms per call) so search sees **every** drawer — pi, Devin, zcode, diaries.
34
+ - **memory: `/unipi:memory migrate`** — explicit one-way v2 conversion: plan + confirm, palace + md-tree backups, dir normalization (collisions keep newer `updated`, losers → `.conflicts/`), typed layout, daemon-driven mining, per-record verification before any old drawer is deleted, resumable `.conversion.json` under a `.conversion.lock` single-runner guard, temp daemon auto-stopped at the end. Loose files added later are adopted in the background post-conversion.
35
+ - **memory: version gate + markdown-only mode.** `MIN_MEMPALACE = 3.10.0`: older installs get a background `uv tool upgrade` attempt and fall back to local-mode (md files still work; search/list use term scoring and label hits `local`). No install + no uv → same local mode with an install hint on the session card.
36
+ - **memory: v2 command surface restored** — `/unipi:memory-process`, `/unipi:memory-consolidate`, `/unipi:memory-search`, `/unipi:global-memory-search`, `/unipi:memory-forget`, `/unipi:global-memory-list`, plus `/unipi:memory status|recall|write|migrate` with arg completions.
37
+ - **memory: tools + UI.** `memory_store/search/list/delete` (+`global_*` aliases) with rail-framed cards (score bars, per-source labels like `pi`/`devin`/`zcode`), a session card with the wake-up summary folded under it, badge-rendered recall/save reminders, footer stats (recall/write/pending/migration), and `MEMORY_STORED`/`MEMORY_DELETED` events. `recallAtStart`/`write`/`wakeUp`/`autoStartDaemon`/`mempalaceAutoUpdate` switches in the settings hub.
38
+
39
+
9
40
  ### Added
10
41
 
11
42
  - **kanboard: the v3 daemon and web UI** (`unipi-kanboard serve`). Single instance via `flock(daemon.lock)` (a second `serve` prints the running `daemon.json` and exits 0), binds `127.0.0.1` with `--port` or an OS-assigned port, writes `daemon.json` atomically, removes it on exit (SIGTERM/SIGINT/idle), and shuts down after `--idle-min` (default 10) with no SSE clients and no requests. A `notify` watcher on `projects/` bumps one revision per content change (access events ignored, bursts coalesced) and `GET /events?project=<slug>` streams it, so CLI and agent writes appear live. `status` reports the daemon + liveness, `stop` SIGTERMs it and waits ≤3s.
package/README.md CHANGED
@@ -11,7 +11,7 @@ pi install npm:@pi-unipi/unipi
11
11
 
12
12
  ## Requirements
13
13
 
14
- - **Pi (`@earendil-works/pi-coding-agent`) `^0.86.0`** — Unipi 3.0.0-alpha tracks the Pi 0.86 SDK (JSON-strict `ToolCall.arguments` / `ToolResultMessage.details`, `TranscriptContext` custom-provider inputs, native prompt-cache warming, per-model compaction budgets, `ctx.modelRegistry.stream()`). Older Pi releases (0.80–0.85) still load most extensions, but npm will flag the peer dependency mismatch; pin `@pi-unipi/*@<3.0.0` if you must stay on an older Pi.
14
+ - **Pi (`@earendil-works/pi-coding-agent`) `^0.87.1`** — Unipi 3.0.0-alpha tracks the Pi 0.87 SDK (extension boundary events, `SessionManager`-canonical context, `TranscriptContext` custom-provider inputs, native prompt-cache warming). Older Pi releases still load most extensions, but npm will flag the peer dependency mismatch; pin `@pi-unipi/*@<3.0.0` if you must stay on an older Pi.
15
15
 
16
16
  ## What You Get
17
17
 
@@ -19,7 +19,7 @@ pi install npm:@pi-unipi/unipi
19
19
 
20
20
  **[Long-Horizon](./packages/long-horizon/)** — Mode-gated long-horizon execution: `/goal` (one objective until verifiably true, propose+verify), `/ralph` (task-file iteration loops), `/swarm` (independent fan-out + synthesis), `/graph` (dependent multi-step work). A TypeSafe jev prompt judge routes each turn; one automation owner per session with park/resume; runaway-guard steering; token/turn/stall budgets.
21
21
 
22
- **[Memory](./packages/memory/README.md)** — SQLite + vector search stores facts, preferences, and decisions. Project-scoped and global. The agent remembers what you told it last week.
22
+ **[Memory](./packages/memory/README.md)** — Markdown files + [MemPalace](https://github.com/mempalace/mempalace) semantic search share a memory palace across pi, Devin, and zcode. Facts, preferences, and decisions persist across sessions; v2 data converts via `/unipi:memory migrate`.
23
23
 
24
24
  **[Compactor](./packages/compactor/README.md)** — Zero-LLM context engine. 6-stage pipeline hits 95%+ token reduction at zero API cost. Session continuity, percentage auto-compaction, session recall, and sandbox execution.
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/unipi",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "All-in-one extension suite for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -77,39 +77,39 @@
77
77
  ]
78
78
  },
79
79
  "peerDependencies": {
80
- "@earendil-works/pi-ai": "^0.86.0",
81
- "@earendil-works/pi-coding-agent": "^0.86.0",
82
- "@earendil-works/pi-tui": "^0.86.0",
80
+ "@earendil-works/pi-ai": "^0.87.1",
81
+ "@earendil-works/pi-coding-agent": "^0.87.1",
82
+ "@earendil-works/pi-tui": "^0.87.1",
83
83
  "typebox": "^1.1.38"
84
84
  },
85
85
  "dependencies": {
86
- "@pi-unipi/ask-user": "3.0.0-alpha.0",
87
- "@pi-unipi/background-tasks": "3.0.0-alpha.0",
88
- "@pi-unipi/btw": "3.0.0-alpha.0",
89
- "@pi-unipi/command-enchantment": "3.0.0-alpha.0",
90
- "@pi-unipi/compactor": "3.0.0-alpha.0",
91
- "@pi-unipi/core": "3.0.0-alpha.0",
92
- "@pi-unipi/footer": "3.0.0-alpha.0",
93
- "@pi-unipi/image": "3.0.0-alpha.0",
94
- "@pi-unipi/fusion": "3.0.0-alpha.0",
95
- "@pi-unipi/info-screen": "3.0.0-alpha.0",
96
- "@pi-unipi/input-shortcuts": "3.0.0-alpha.0",
97
- "@pi-unipi/kanboard": "3.0.0-alpha.0",
98
- "@pi-unipi/mcp": "3.0.0-alpha.0",
99
- "@pi-unipi/memory": "3.0.0-alpha.0",
100
- "@pi-unipi/notify": "3.0.0-alpha.0",
101
- "@pi-unipi/skill-registry": "3.0.0-alpha.0",
102
- "@pi-unipi/subagents": "3.0.0-alpha.0",
103
- "@pi-unipi/updater": "3.0.0-alpha.0",
104
- "@pi-unipi/utility": "3.0.0-alpha.0",
105
- "@pi-unipi/web-api": "3.0.0-alpha.0",
106
- "@pi-unipi/workflow": "3.0.0-alpha.0"
86
+ "@pi-unipi/ask-user": "3.0.0-alpha.2",
87
+ "@pi-unipi/background-tasks": "3.0.0-alpha.2",
88
+ "@pi-unipi/btw": "3.0.0-alpha.2",
89
+ "@pi-unipi/command-enchantment": "3.0.0-alpha.2",
90
+ "@pi-unipi/compactor": "3.0.0-alpha.2",
91
+ "@pi-unipi/core": "3.0.0-alpha.2",
92
+ "@pi-unipi/footer": "3.0.0-alpha.2",
93
+ "@pi-unipi/image": "3.0.0-alpha.2",
94
+ "@pi-unipi/fusion": "3.0.0-alpha.2",
95
+ "@pi-unipi/info-screen": "3.0.0-alpha.2",
96
+ "@pi-unipi/input-shortcuts": "3.0.0-alpha.2",
97
+ "@pi-unipi/kanboard": "3.0.0-alpha.2",
98
+ "@pi-unipi/mcp": "3.0.0-alpha.2",
99
+ "@pi-unipi/memory": "3.0.0-alpha.2",
100
+ "@pi-unipi/notify": "3.0.0-alpha.2",
101
+ "@pi-unipi/skill-registry": "3.0.0-alpha.2",
102
+ "@pi-unipi/subagents": "3.0.0-alpha.2",
103
+ "@pi-unipi/updater": "3.0.0-alpha.2",
104
+ "@pi-unipi/utility": "3.0.0-alpha.2",
105
+ "@pi-unipi/web-api": "3.0.0-alpha.2",
106
+ "@pi-unipi/workflow": "3.0.0-alpha.2"
107
107
  },
108
108
  "devDependencies": {
109
- "@earendil-works/pi-agent-core": "^0.86.0",
110
- "@earendil-works/pi-ai": "^0.86.0",
111
- "@earendil-works/pi-coding-agent": "^0.86.0",
112
- "@earendil-works/pi-tui": "^0.86.0",
109
+ "@earendil-works/pi-agent-core": "^0.87.1",
110
+ "@earendil-works/pi-ai": "^0.87.1",
111
+ "@earendil-works/pi-coding-agent": "^0.87.1",
112
+ "@earendil-works/pi-tui": "^0.87.1",
113
113
  "@types/better-sqlite3": "^9.6.0",
114
114
  "@types/node": "^25.6.0",
115
115
  "diff": "^7.0.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/ask-user",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Structured user input tool for Pi coding agent \u2014 single-select, multi-select, freeform",
3
+ "version": "3.0.0-alpha.2",
4
+ "description": "Structured user input tool for Pi coding agent — single-select, multi-select, freeform",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "license": "MIT",
@@ -40,11 +40,11 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@pi-unipi/core": "3.0.0-alpha.0"
43
+ "@pi-unipi/core": "3.0.0-alpha.2"
44
44
  },
45
45
  "peerDependencies": {
46
- "@earendil-works/pi-coding-agent": "^0.86.0",
47
- "@earendil-works/pi-tui": "^0.86.0",
46
+ "@earendil-works/pi-coding-agent": "^0.87.1",
47
+ "@earendil-works/pi-tui": "^0.87.1",
48
48
  "typebox": "^1.1.38"
49
49
  },
50
50
  "pi": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/command-enchantment",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Enhanced TUI autocomplete for /unipi:* commands \u2014 colored, sorted, and grouped by package",
3
+ "version": "3.0.0-alpha.2",
4
+ "description": "Enhanced TUI autocomplete for /unipi:* commands — colored, sorted, and grouped by package",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "license": "MIT",
@@ -29,8 +29,8 @@
29
29
  "!src/**/*.test.ts"
30
30
  ],
31
31
  "peerDependencies": {
32
- "@earendil-works/pi-coding-agent": "^0.86.0",
33
- "@earendil-works/pi-tui": "^0.86.0"
32
+ "@earendil-works/pi-coding-agent": "^0.87.1",
33
+ "@earendil-works/pi-tui": "^0.87.1"
34
34
  },
35
35
  "scripts": {
36
36
  "test": "vitest run"
@@ -50,12 +50,13 @@ export const COMMAND_REGISTRY: Record<string, string> = {
50
50
  "unipi:graph": "long-horizon",
51
51
 
52
52
  // memory (7 commands)
53
- "unipi:memory-process": "memory",
54
- "unipi:memory-search": "memory",
55
- "unipi:memory-consolidate": "memory",
56
- "unipi:memory-forget": "memory",
53
+ "unipi:memory": "memory",
54
+ "unipi:memory-process": "memory",
55
+ "unipi:memory-consolidate": "memory",
56
+ "unipi:memory-search": "memory",
57
57
  "unipi:global-memory-search": "memory",
58
- "unipi:global-memory-list": "memory",
58
+ "unipi:memory-forget": "memory",
59
+ "unipi:global-memory-list": "memory",
59
60
 
60
61
  // btw (6 commands)
61
62
  "unipi:btw": "btw",
@@ -141,12 +142,13 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
141
142
  "unipi:swarm": "Parallel fan-out + synthesis · complex decomposable · higher cost, high coverage",
142
143
  "unipi:graph": "Dependent multi-step work · later steps need earlier results · highest cost",
143
144
 
144
- "unipi:memory-process": "Process and store conversation learnings",
145
- "unipi:memory-search": "Search project memory for past context",
146
- "unipi:memory-consolidate": "Consolidate memory entries",
147
- "unipi:memory-forget": "Remove memory entries",
148
- "unipi:global-memory-search": "Search across all project memories",
149
- "unipi:global-memory-list": "List all project memories",
145
+ "unipi:memory": "Memory palace — status, migrate, recall/write toggles",
146
+ "unipi:memory-process": "Analyze text and store extracted memories",
147
+ "unipi:memory-consolidate": "Consolidate the current session into memory",
148
+ "unipi:memory-search": "Search project memories",
149
+ "unipi:global-memory-search": "Search memories across all projects",
150
+ "unipi:memory-forget": "Delete a memory by title",
151
+ "unipi:global-memory-list": "List all memories across all projects",
150
152
 
151
153
  "unipi:btw": "Run a parallel side conversation",
152
154
  "unipi:btw-tangent": "Start a contextless BTW tangent thread",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/background-tasks",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Background tasks for UniPi \u2014 durable shell jobs and delegated background agents with a footer dock",
3
+ "version": "3.0.0-alpha.2",
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",
7
7
  "types": "src/index.ts",
@@ -9,8 +9,8 @@
9
9
  "test": "npx tsx --test src/__tests__/*.test.ts"
10
10
  },
11
11
  "dependencies": {
12
- "@earendil-works/pi-ai": "^0.86.0",
13
- "@pi-unipi/core": "3.0.0-alpha.0",
12
+ "@earendil-works/pi-ai": "^0.87.1",
13
+ "@pi-unipi/core": "3.0.0-alpha.2",
14
14
  "turndown": "^7.2.4"
15
15
  },
16
16
  "devDependencies": {
@@ -18,8 +18,8 @@
18
18
  "typescript": "^5.8.3"
19
19
  },
20
20
  "peerDependencies": {
21
- "@earendil-works/pi-coding-agent": "^0.86.0",
22
- "@earendil-works/pi-tui": "^0.86.0",
21
+ "@earendil-works/pi-coding-agent": "^0.87.1",
22
+ "@earendil-works/pi-tui": "^0.87.1",
23
23
  "typebox": "^1.1.38"
24
24
  },
25
25
  "files": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/btw",
3
- "version": "3.0.0-alpha.0",
4
- "description": "A pi extension for parallel side conversations with /unipi:btw \u2014 part of the Unipi suite",
3
+ "version": "3.0.0-alpha.2",
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",
7
7
  "license": "MIT",
@@ -32,12 +32,12 @@
32
32
  "access": "public"
33
33
  },
34
34
  "peerDependencies": {
35
- "@earendil-works/pi-ai": "^0.86.0",
36
- "@earendil-works/pi-coding-agent": "^0.86.0",
37
- "@earendil-works/pi-tui": "^0.86.0"
35
+ "@earendil-works/pi-ai": "^0.87.1",
36
+ "@earendil-works/pi-coding-agent": "^0.87.1",
37
+ "@earendil-works/pi-tui": "^0.87.1"
38
38
  },
39
39
  "dependencies": {
40
- "@pi-unipi/core": "3.0.0-alpha.0"
40
+ "@pi-unipi/core": "3.0.0-alpha.2"
41
41
  },
42
42
  "pi": {
43
43
  "extensions": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/compactor",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Context engine for Pi \u2014 zero-LLM compaction, session continuity, sandbox execution, and tool display optimization",
3
+ "version": "3.0.0-alpha.2",
4
+ "description": "Context engine for Pi — zero-LLM compaction, session continuity, sandbox execution, and tool display optimization",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "license": "MIT",
@@ -34,13 +34,13 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.0",
38
- "@pi-unipi/info-screen": "3.0.0-alpha.0",
39
- "@earendil-works/pi-agent-core": "^0.86.0"
37
+ "@pi-unipi/core": "3.0.0-alpha.2",
38
+ "@pi-unipi/info-screen": "3.0.0-alpha.2",
39
+ "@earendil-works/pi-agent-core": "^0.87.1"
40
40
  },
41
41
  "peerDependencies": {
42
- "@earendil-works/pi-coding-agent": "^0.86.0",
43
- "@earendil-works/pi-tui": "^0.86.0",
42
+ "@earendil-works/pi-coding-agent": "^0.87.1",
43
+ "@earendil-works/pi-tui": "^0.87.1",
44
44
  "typebox": "^1.1.38"
45
45
  },
46
46
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/core",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "Shared utilities, event types, and constants for Unipi extension suite",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,13 +24,13 @@
24
24
  "access": "public"
25
25
  },
26
26
  "peerDependencies": {
27
- "@earendil-works/pi-coding-agent": "^0.86.0",
28
- "@earendil-works/pi-tui": "^0.86.0",
27
+ "@earendil-works/pi-coding-agent": "^0.87.1",
28
+ "@earendil-works/pi-tui": "^0.87.1",
29
29
  "typebox": "^1.1.38"
30
30
  },
31
31
  "devDependencies": {
32
- "@earendil-works/pi-coding-agent": "^0.86.0",
33
- "@earendil-works/pi-tui": "^0.86.0",
32
+ "@earendil-works/pi-coding-agent": "^0.87.1",
33
+ "@earendil-works/pi-tui": "^0.87.1",
34
34
  "typebox": "^1.1.38",
35
35
  "@types/node": "^22.0.0"
36
36
  },
@@ -66,6 +66,15 @@ const A_KEY_MODULES: ReadonlyArray<{ namespace: string; key: string }> = [
66
66
  { namespace: "long-horizon", key: "longHorizon" },
67
67
  ];
68
68
 
69
+ /**
70
+ * Global flat config files outside the canonical <module>/config.json shape
71
+ * (layout D — e.g. v2 memory settings lived inside the memory dir itself).
72
+ * Copied, never removed (v2 still reads them).
73
+ */
74
+ const D_GLOBAL_MOVES: ReadonlyArray<{ namespace: string; from: string }> = [
75
+ { namespace: "memory", from: join(".unipi", "memory", "config.json") },
76
+ ];
77
+
69
78
  /** Project override files that need shape unification (layout C). */
70
79
  const C_OVERRIDE_MOVES: ReadonlyArray<{ namespace: string; from: string }> = [
71
80
  { namespace: "compactor", from: join(".unipi", "config", "compactor.json") },
@@ -165,6 +174,12 @@ export function importGlobalScope(): MigrationResult {
165
174
  const log: MigrationLogEntry[] = [];
166
175
  let touched = false;
167
176
 
177
+ for (const { namespace, from } of D_GLOBAL_MOVES) {
178
+ if (copyWithSafety({ from: join(homedir(), from), to: globalSettingsPath(namespace) }, log)) {
179
+ touched = true;
180
+ }
181
+ }
182
+
168
183
  const piSettingsFile = piSettingsPath();
169
184
  if (existsSync(piSettingsFile)) {
170
185
  try {
@@ -11,9 +11,11 @@
11
11
  * ~/.unipi/images/ → ~/.unipi/global/image/
12
12
  * ~/.unipi/analytics/ → ~/.unipi/global/utility/analytics/
13
13
  * ~/.unipi/cache/updater/ → ~/.unipi/global/updater/
14
- * PURGE (ephemeral leak, safe to drop — sessions are dead):
15
- * ~/.unipi/state/fusion/ (26 orphaned sidekick transcripts, 61 MB)
16
- * ~/.unipi/trajectory/ (removed module; may already be gone)
14
+ * ARCHIVE (dead in v3 but user data — never deleted):
15
+ * ~/.unipi/state/fusion/ → ~/.unipi/v2-archive/fusion-<ts>/
16
+ * ~/.unipi/trajectory/ → ~/.unipi/v2-archive/trajectory-<ts>/
17
+ * (v2 may still be writing these on shared machines; the archive keeps
18
+ * them intact and can be deleted by hand.)
17
19
  *
18
20
  * Identity-keyed moves (memory basename→uuid, long-horizon project state) are
19
21
  * intentionally NOT auto-remapped here: basename cannot be reversed to a
@@ -30,7 +32,7 @@ import { tryRead } from "../../utils.js";
30
32
  export const CURRENT_STATE_VERSION = "3.0.0";
31
33
 
32
34
  export interface StateMigrationLogEntry {
33
- action: "moved" | "purged" | "skipped:missing" | "skipped:conflict" | "error";
35
+ action: "moved" | "archived" | "skipped:missing" | "skipped:conflict" | "error";
34
36
  from: string;
35
37
  to?: string;
36
38
  detail?: string;
@@ -98,14 +100,22 @@ function moveDir(from: string, to: string, log: StateMigrationLogEntry[]): void
98
100
  }
99
101
  }
100
102
 
101
- function purgeDir(from: string, log: StateMigrationLogEntry[]): void {
103
+ /**
104
+ * Archive a dir: instant same-filesystem rename into
105
+ * `~/.unipi/v2-archive/<name>-<ts>/` — the data stays on disk for the user
106
+ * to keep or delete by hand. Nothing is ever destroyed here.
107
+ */
108
+ function archiveDir(from: string, root: string, name: string, ts: string, log: StateMigrationLogEntry[]): void {
102
109
  if (!existsSync(from)) {
103
110
  log.push({ action: "skipped:missing", from });
104
111
  return;
105
112
  }
106
113
  try {
107
- rmSync(from, { recursive: true, force: true });
108
- log.push({ action: "purged", from });
114
+ const archiveRoot = join(root, "v2-archive");
115
+ mkdirSync(archiveRoot, { recursive: true });
116
+ const to = join(archiveRoot, `${name}-${ts}`);
117
+ renameSync(from, to);
118
+ log.push({ action: "archived", from, to });
109
119
  } catch (err) {
110
120
  log.push({ action: "error", from, detail: err instanceof Error ? err.message : String(err) });
111
121
  }
@@ -121,6 +131,7 @@ export function migrateState(home: string = homedir()): StateMigrationResult {
121
131
  return { ran: false, version: CURRENT_STATE_VERSION, log: [] };
122
132
  }
123
133
  const log: StateMigrationLogEntry[] = [];
134
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
124
135
  const g = (m: string) => join(root, "global", m);
125
136
 
126
137
  // Global relocations.
@@ -129,9 +140,9 @@ export function migrateState(home: string = homedir()): StateMigrationResult {
129
140
  moveDir(join(root, "analytics"), join(g("utility"), "analytics"), log);
130
141
  moveDir(join(root, "cache", "updater"), g("updater"), log);
131
142
 
132
- // Ephemeral leaks: dead sessions, removed module.
133
- purgeDir(join(root, "state", "fusion"), log);
134
- purgeDir(join(root, "trajectory"), log);
143
+ // v2 leftovers: dead to v3, kept under ~/.unipi/v2-archive/ forever.
144
+ archiveDir(join(root, "state", "fusion"), root, "fusion", ts, log);
145
+ archiveDir(join(root, "trajectory"), root, "trajectory", ts, log);
135
146
 
136
147
  // Stamp the version so we never run again.
137
148
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/footer",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Persistent status bar for Unipi \u2014 subscribes to UNIPI_EVENTS and renders key stats from all unipi packages",
3
+ "version": "3.0.0-alpha.2",
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",
7
7
  "license": "MIT",
@@ -32,12 +32,12 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/core": "3.0.0-alpha.0",
36
- "@pi-unipi/background-tasks": "3.0.0-alpha.0"
35
+ "@pi-unipi/core": "3.0.0-alpha.2",
36
+ "@pi-unipi/background-tasks": "3.0.0-alpha.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-coding-agent": "^0.86.0",
40
- "@earendil-works/pi-tui": "^0.86.0"
39
+ "@earendil-works/pi-coding-agent": "^0.87.1",
40
+ "@earendil-works/pi-tui": "^0.87.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^25.6.0",
@@ -35,6 +35,9 @@ interface InfoMemoryData {
35
35
  projectCount?: { value: string };
36
36
  totalCount?: { value: string };
37
37
  consolidations?: { value: string };
38
+ recall?: { value: string };
39
+ write?: { value: string };
40
+ pending?: { value: string };
38
41
  [key: string]: unknown;
39
42
  }
40
43
 
@@ -138,8 +141,28 @@ function renderConsolidationsSegment(ctx: FooterSegmentContext): RenderedSegment
138
141
  return { content: applyColor("memory", content, ctx.theme, ctx.colors), visible: true };
139
142
  }
140
143
 
144
+ /** recall/write state + pending count, from the memory info group's dataProvider. */
145
+ function renderMemoryStateSegment(ctx: FooterSegmentContext): RenderedSegment {
146
+ const infoData = getInfoRegistryMemoryData();
147
+ const recall = infoData?.recall?.value;
148
+ const write = infoData?.write?.value;
149
+ const pending = infoData?.pending?.value;
150
+ if (recall === undefined && write === undefined && pending === undefined) {
151
+ return { content: "", visible: false };
152
+ }
153
+ const parts = [
154
+ recall !== undefined ? `r:${recall}` : null,
155
+ write !== undefined ? `w:${write}` : null,
156
+ pending !== undefined && pending !== "0" ? `+${pending}⧗` : null,
157
+ ].filter(Boolean).join(" ");
158
+ if (!parts) return { content: "", visible: false };
159
+ const content = withIcon("memoryState", parts);
160
+ return { content: applyColor("memory", content, ctx.theme, ctx.colors), visible: true };
161
+ }
162
+
141
163
  export const MEMORY_SEGMENTS: FooterSegment[] = [
142
164
  { id: "project_count", label: "Project Memory", shortLabel: "MEM", description: "Memory entries for this project", zone: "center", render: renderProjectCountSegment, defaultShow: true },
143
165
  { id: "total_count", label: "Total Memory", shortLabel: "TOT", description: "Total memory entries across projects", zone: "center", render: renderTotalCountSegment, defaultShow: true },
166
+ { id: "memory_state", label: "Memory State", shortLabel: "MST", description: "Recall/write switches + pending ops", zone: "center", render: renderMemoryStateSegment, defaultShow: true },
144
167
  { id: "consolidations", label: "Consolidations", shortLabel: "CNS", description: "Number of memory consolidations", zone: "center", render: renderConsolidationsSegment, defaultShow: false },
145
168
  ];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/fusion",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
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,13 +32,13 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/core": "3.0.0-alpha.0",
36
- "@pi-unipi/subagents": "3.0.0-alpha.0"
35
+ "@pi-unipi/core": "3.0.0-alpha.2",
36
+ "@pi-unipi/subagents": "3.0.0-alpha.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-ai": "^0.86.0",
40
- "@earendil-works/pi-coding-agent": "^0.86.0",
41
- "@earendil-works/pi-tui": "^0.86.0"
39
+ "@earendil-works/pi-ai": "^0.87.1",
40
+ "@earendil-works/pi-coding-agent": "^0.87.1",
41
+ "@earendil-works/pi-tui": "^0.87.1"
42
42
  },
43
43
  "scripts": {
44
44
  "test": "npx tsx --test tests/**/*.test.ts"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/image",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "Image generation and image recognition tools for the Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -34,12 +34,12 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.0"
37
+ "@pi-unipi/core": "3.0.0-alpha.2"
38
38
  },
39
39
  "peerDependencies": {
40
- "@earendil-works/pi-ai": "^0.86.0",
41
- "@earendil-works/pi-coding-agent": "^0.86.0",
42
- "@earendil-works/pi-tui": "^0.86.0",
40
+ "@earendil-works/pi-ai": "^0.87.1",
41
+ "@earendil-works/pi-coding-agent": "^0.87.1",
42
+ "@earendil-works/pi-tui": "^0.87.1",
43
43
  "typebox": "^1.1.38"
44
44
  },
45
45
  "devDependencies": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/info-screen",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Dashboard and module registry for Unipi \u2014 configurable info overlay with tabbed groups",
3
+ "version": "3.0.0-alpha.2",
4
+ "description": "Dashboard and module registry for Unipi — configurable info overlay with tabbed groups",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "license": "MIT",
@@ -32,11 +32,11 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@pi-unipi/core": "3.0.0-alpha.0"
35
+ "@pi-unipi/core": "3.0.0-alpha.2"
36
36
  },
37
37
  "peerDependencies": {
38
- "@earendil-works/pi-coding-agent": "^0.86.0",
39
- "@earendil-works/pi-tui": "^0.86.0",
38
+ "@earendil-works/pi-coding-agent": "^0.87.1",
39
+ "@earendil-works/pi-tui": "^0.87.1",
40
40
  "typebox": "^1.1.38"
41
41
  },
42
42
  "devDependencies": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/input-shortcuts",
3
- "version": "3.0.0-alpha.0",
4
- "description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle \u2014 chord-based overlay system",
3
+ "version": "3.0.0-alpha.2",
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",
7
7
  "license": "MIT",
@@ -33,11 +33,11 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@pi-unipi/core": "3.0.0-alpha.0"
36
+ "@pi-unipi/core": "3.0.0-alpha.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-coding-agent": "^0.86.0",
40
- "@earendil-works/pi-tui": "^0.86.0"
39
+ "@earendil-works/pi-coding-agent": "^0.87.1",
40
+ "@earendil-works/pi-tui": "^0.87.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^25.6.0",
@@ -158,6 +158,9 @@ export default function (pi: ExtensionAPI) {
158
158
  });
159
159
 
160
160
  pi.on("session_start", async (_event, ctx) => {
161
+ // Kanboard's own ambient children (summaries, --list-models) get this env:
162
+ // they must not re-sync the runtime or spawn a daemon.
163
+ if (process.env.UNIPI_KANBOARD_CHILD) return;
161
164
  initUnipiDirs();
162
165
  if (attach(ctx as unknown as ExtensionContext)) {
163
166
  const deps = buildDeps();