@pi-unipi/unipi 2.0.13 → 2.1.1

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 (168) hide show
  1. package/LICENSE +0 -0
  2. package/package.json +28 -28
  3. package/packages/ask-user/handoff.ts +0 -0
  4. package/packages/ask-user/index.ts +3 -1
  5. package/packages/ask-user/launcher-ui.ts +0 -0
  6. package/packages/ask-user/package.json +5 -5
  7. package/packages/ask-user/tools.ts +20 -11
  8. package/packages/autocomplete/README.md +0 -0
  9. package/packages/autocomplete/package.json +4 -4
  10. package/packages/autocomplete/src/__tests__/command-registry.audit.test.ts +0 -0
  11. package/packages/autocomplete/src/__tests__/provider.sorting.test.ts +0 -0
  12. package/packages/autocomplete/src/constants.ts +0 -0
  13. package/packages/autocomplete/src/provider.ts +0 -0
  14. package/packages/autocomplete/src/sorting.ts +0 -0
  15. package/packages/btw/package.json +6 -6
  16. package/packages/cocoindex/README.md +0 -0
  17. package/packages/cocoindex/bridge.ts +0 -0
  18. package/packages/cocoindex/commands.ts +0 -0
  19. package/packages/cocoindex/index.ts +0 -0
  20. package/packages/cocoindex/installer.ts +0 -0
  21. package/packages/cocoindex/package.json +6 -6
  22. package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -0
  23. package/packages/cocoindex/tools.ts +0 -0
  24. package/packages/compactor/package.json +7 -7
  25. package/packages/compactor/src/commands/index.ts +0 -0
  26. package/packages/compactor/src/compaction/auto-trigger.ts +0 -0
  27. package/packages/compactor/src/compaction/hooks.ts +0 -0
  28. package/packages/compactor/src/config/manager.ts +0 -0
  29. package/packages/compactor/src/config/presets.ts +0 -0
  30. package/packages/compactor/src/config/schema.ts +0 -0
  31. package/packages/compactor/src/display/diff-presentation.ts +0 -0
  32. package/packages/compactor/src/display/diff-renderer.ts +0 -0
  33. package/packages/compactor/src/display/diff-width-safety.ts +0 -0
  34. package/packages/compactor/src/display/line-width-safety.ts +0 -0
  35. package/packages/compactor/src/index.ts +0 -0
  36. package/packages/compactor/src/info-screen.ts +0 -0
  37. package/packages/compactor/src/session/analytics.ts +0 -0
  38. package/packages/compactor/src/session/db.ts +0 -0
  39. package/packages/compactor/src/session/recall-blocks.ts +0 -0
  40. package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -0
  41. package/packages/compactor/src/tools/ctx-doctor.ts +0 -0
  42. package/packages/compactor/src/tools/ctx-stats.ts +0 -0
  43. package/packages/compactor/src/tools/register.ts +0 -0
  44. package/packages/compactor/src/tui/settings-overlay.ts +0 -0
  45. package/packages/compactor/src/types.ts +0 -0
  46. package/packages/core/constants.ts +0 -0
  47. package/packages/core/events.ts +0 -0
  48. package/packages/core/global-types.ts +0 -0
  49. package/packages/core/global.d.ts +0 -0
  50. package/packages/core/package.json +3 -3
  51. package/packages/core/sandbox.ts +0 -0
  52. package/packages/core/utils.ts +26 -0
  53. package/packages/footer/README.md +0 -0
  54. package/packages/footer/index.ts +0 -0
  55. package/packages/footer/package.json +5 -5
  56. package/packages/footer/src/commands.ts +0 -0
  57. package/packages/footer/src/config.ts +0 -0
  58. package/packages/footer/src/events.ts +0 -0
  59. package/packages/footer/src/help.ts +0 -0
  60. package/packages/footer/src/index.ts +71 -4
  61. package/packages/footer/src/presets.ts +0 -0
  62. package/packages/footer/src/registry/index.ts +0 -0
  63. package/packages/footer/src/rendering/icons.ts +0 -0
  64. package/packages/footer/src/rendering/renderer.ts +0 -0
  65. package/packages/footer/src/rendering/separators.ts +0 -0
  66. package/packages/footer/src/rendering/theme.ts +0 -0
  67. package/packages/footer/src/segments/compactor.ts +0 -0
  68. package/packages/footer/src/segments/core.ts +0 -0
  69. package/packages/footer/src/segments/kanboard.ts +0 -0
  70. package/packages/footer/src/segments/mcp.ts +0 -0
  71. package/packages/footer/src/segments/memory.ts +0 -0
  72. package/packages/footer/src/segments/notify.ts +0 -0
  73. package/packages/footer/src/segments/ralph.ts +0 -0
  74. package/packages/footer/src/segments/status-ext.ts +0 -0
  75. package/packages/footer/src/segments/workflow.ts +0 -0
  76. package/packages/footer/src/tps-tracker.ts +109 -33
  77. package/packages/footer/src/tui/settings-tui.ts +0 -0
  78. package/packages/footer/src/types.ts +0 -0
  79. package/packages/footer/types.ts +0 -0
  80. package/packages/info-screen/index.ts +3 -1
  81. package/packages/info-screen/package.json +5 -5
  82. package/packages/info-screen/tui/info-overlay.ts +16 -7
  83. package/packages/input-shortcuts/README.md +0 -0
  84. package/packages/input-shortcuts/index.ts +0 -0
  85. package/packages/input-shortcuts/package.json +5 -5
  86. package/packages/input-shortcuts/src/chord-overlay.ts +0 -0
  87. package/packages/input-shortcuts/src/clipboard.ts +0 -0
  88. package/packages/input-shortcuts/src/index.ts +0 -0
  89. package/packages/input-shortcuts/src/registers.ts +0 -0
  90. package/packages/input-shortcuts/src/settings-overlay.ts +0 -0
  91. package/packages/input-shortcuts/src/status.ts +0 -0
  92. package/packages/input-shortcuts/src/types.ts +0 -0
  93. package/packages/input-shortcuts/src/undo-redo.ts +0 -0
  94. package/packages/kanboard/index.ts +0 -0
  95. package/packages/kanboard/package.json +5 -5
  96. package/packages/mcp/package.json +5 -5
  97. package/packages/mcp/src/index.ts +3 -1
  98. package/packages/mcp/src/tui/settings-overlay.ts +0 -0
  99. package/packages/memory/README.md +51 -7
  100. package/packages/memory/index.ts +7 -4
  101. package/packages/memory/mempalace.ts +260 -0
  102. package/packages/memory/package.json +8 -5
  103. package/packages/memory/storage.ts +253 -22
  104. package/packages/memory/tools.ts +0 -0
  105. package/packages/milestone/commands.ts +3 -2
  106. package/packages/milestone/package.json +5 -5
  107. package/packages/notify/ask-user-prompt-message.ts +0 -0
  108. package/packages/notify/commands.ts +0 -0
  109. package/packages/notify/events.ts +0 -0
  110. package/packages/notify/index.ts +3 -1
  111. package/packages/notify/ntfy-config.ts +0 -0
  112. package/packages/notify/package.json +8 -6
  113. package/packages/notify/platforms/focus-win.ts +0 -0
  114. package/packages/notify/platforms/focus.ts +0 -0
  115. package/packages/notify/platforms/native.ts +0 -0
  116. package/packages/notify/settings.ts +0 -0
  117. package/packages/notify/src/__tests__/ask-user-prompt-message.test.ts +0 -0
  118. package/packages/notify/src/__tests__/event-bus.test.ts +0 -0
  119. package/packages/notify/tui/settings-overlay.ts +0 -0
  120. package/packages/notify/types.ts +0 -0
  121. package/packages/ralph/index.ts +3 -1
  122. package/packages/ralph/package.json +6 -6
  123. package/packages/subagents/package.json +8 -8
  124. package/packages/subagents/src/index.ts +27 -23
  125. package/packages/unipi/index.ts +0 -0
  126. package/packages/updater/README.md +0 -0
  127. package/packages/updater/index.ts +0 -0
  128. package/packages/updater/package.json +5 -5
  129. package/packages/updater/skills/configure-updater/SKILL.md +0 -0
  130. package/packages/updater/src/cache.ts +0 -0
  131. package/packages/updater/src/changelog.ts +0 -0
  132. package/packages/updater/src/checker.ts +0 -0
  133. package/packages/updater/src/commands.ts +0 -0
  134. package/packages/updater/src/installer.ts +0 -0
  135. package/packages/updater/src/markdown.ts +0 -0
  136. package/packages/updater/src/readme.ts +3 -1
  137. package/packages/updater/src/settings.ts +0 -0
  138. package/packages/updater/src/tui/changelog-overlay.ts +0 -0
  139. package/packages/updater/src/tui/readme-overlay.ts +0 -0
  140. package/packages/updater/src/tui/settings-overlay.ts +0 -0
  141. package/packages/updater/src/tui/update-overlay.ts +0 -0
  142. package/packages/updater/src/version.ts +0 -0
  143. package/packages/updater/types.ts +0 -0
  144. package/packages/utility/package.json +4 -4
  145. package/packages/utility/src/diff/renderer.ts +0 -0
  146. package/packages/utility/src/diff/theme.ts +0 -0
  147. package/packages/utility/src/diff/wrapper.ts +0 -0
  148. package/packages/utility/src/display/capabilities.ts +0 -0
  149. package/packages/utility/src/index.ts +3 -1
  150. package/packages/web-api/package.json +4 -4
  151. package/packages/web-api/skills/web/SKILL.md +0 -0
  152. package/packages/web-api/src/engine/constants.ts +0 -0
  153. package/packages/web-api/src/engine/dependencies.ts +0 -0
  154. package/packages/web-api/src/engine/dom.ts +0 -0
  155. package/packages/web-api/src/engine/extract.ts +0 -0
  156. package/packages/web-api/src/engine/format.ts +0 -0
  157. package/packages/web-api/src/engine/profiles.ts +0 -0
  158. package/packages/web-api/src/engine/types.ts +0 -0
  159. package/packages/web-api/src/index.ts +3 -1
  160. package/packages/web-api/src/providers/base.ts +0 -0
  161. package/packages/web-api/src/settings.ts +0 -0
  162. package/packages/web-api/src/tools.ts +0 -0
  163. package/packages/web-api/src/tui/progress.ts +0 -0
  164. package/packages/web-api/src/tui/result.ts +0 -0
  165. package/packages/web-api/src/tui/settings-dialog.ts +0 -0
  166. package/packages/workflow/commands.ts +0 -0
  167. package/packages/workflow/index.ts +3 -1
  168. package/packages/workflow/package.json +5 -5
@@ -4,37 +4,112 @@
4
4
  * Per-message TPS calculation for live generation rate display.
5
5
  * Tracks individual assistant messages with start/stop timestamps
6
6
  * to measure generation rate excluding idle/tool-execution time.
7
+ *
8
+ * ## Token counting
9
+ *
10
+ * Output tokens are counted from the assistant message *content* (text +
11
+ * thinking blocks) via a lightweight word/char heuristic, NOT from
12
+ * `usage.output`. Rationale:
13
+ * - `usage.output` is only populated by the provider at stream end (or in
14
+ * a final `message_delta`), so it is 0 throughout generation and useless
15
+ * for a live rate.
16
+ * - Counting the streamed text ourselves gives a real, monotonically
17
+ * increasing numerator that reflects how much the model has actually
18
+ * produced so far.
19
+ * - This matches what users perceive as "tokens per second" (visible
20
+ * output rate) and avoids the previous bug where the displayed TPS was
21
+ * always inflated to 90+ t/s (caused by dividing a post-hoc `usage.output`
22
+ * total by a too-short, incorrectly measured elapsed window).
23
+ *
24
+ * The heuristic intentionally over-counts slightly (whitespace + punctuation
25
+ * are treated as tokens). That is acceptable for a live speed indicator and
26
+ * keeps the estimate in the right order of magnitude (within ~10-20% of the
27
+ * provider's reported output tokens for typical English/code output).
7
28
  */
8
29
 
9
30
  /** Per-message TPS record */
10
31
  interface MessageTpsRecord {
11
- /** Message index in the session */
32
+ /** Message index in the session (sequential, assistant-only) */
12
33
  messageIndex: number;
13
- /** Output tokens for this message */
34
+ /** Estimated output tokens produced so far for this message */
14
35
  outputTokens: number;
15
- /** When generation started (Date.now()) */
36
+ /** When generation started (Date.now(), ms) */
16
37
  startedAt: number;
17
- /** When generation completed (Date.now()), 0 if still generating */
38
+ /** When generation completed (Date.now(), ms), 0 if still generating */
18
39
  completedAt: number;
19
- /** Computed TPS for this message */
40
+ /** Computed TPS for this message (final, once completed) */
20
41
  tps: number;
21
42
  }
22
43
 
44
+ /**
45
+ * Estimate the number of tokens in a string.
46
+ *
47
+ * Uses a cheap word + punctuation split. Good enough for a live TPS display;
48
+ * we deliberately avoid pulling in a full BPE tokenizer for performance and
49
+ * footprint reasons.
50
+ */
51
+ function estimateTokens(text: string): number {
52
+ if (!text) return 0;
53
+ // Count word-ish tokens and standalone punctuation.
54
+ // \p{L} / \p{N} keep this Unicode-aware for non-ASCII content.
55
+ const wordish = text.match(/[\p{L}\p{N}]+/gu);
56
+ const punct = text.match(/[^\s\p{L}\p{N}]+/gu);
57
+ const wordCount = wordish ? wordish.length : 0;
58
+ const punctCount = punct ? punct.length : 0;
59
+ return wordCount + punctCount;
60
+ }
61
+
62
+ /**
63
+ * Estimate output tokens from an assistant message by summing text and
64
+ * thinking content lengths. Tool-call argument JSON is also counted since
65
+ * it represents model output.
66
+ *
67
+ * Accepts the raw message shape from pi's session entries (loosely typed).
68
+ */
69
+ function estimateOutputTokens(message: unknown): number {
70
+ if (!message || typeof message !== "object") return 0;
71
+ const m = message as Record<string, unknown>;
72
+ if (m.role !== "assistant") return 0;
73
+ const content = m.content;
74
+ if (!Array.isArray(content)) return 0;
75
+
76
+ let total = 0;
77
+ for (const block of content) {
78
+ if (!block || typeof block !== "object") continue;
79
+ const b = block as Record<string, unknown>;
80
+ const type = b.type;
81
+ if (type === "text" && typeof b.text === "string") {
82
+ total += estimateTokens(b.text);
83
+ } else if (type === "thinking" && typeof b.thinking === "string") {
84
+ total += estimateTokens(b.thinking);
85
+ } else if (type === "tool_use" || type === "toolcall") {
86
+ // Count tool name + serialized arguments as model output.
87
+ const name = typeof b.name === "string" ? b.name : "";
88
+ const input = b.input ?? b.arguments;
89
+ let inputStr: string;
90
+ try {
91
+ inputStr = typeof input === "string" ? input : JSON.stringify(input ?? "");
92
+ } catch {
93
+ inputStr = "";
94
+ }
95
+ total += estimateTokens(name) + estimateTokens(inputStr);
96
+ }
97
+ }
98
+ return total;
99
+ }
100
+
23
101
  /**
24
102
  * Tracks per-message TPS and computes live/session metrics.
25
103
  *
26
- * Usage: Call `onMessageUpdate()` whenever output tokens change.
27
- * The tracker records generation start/stop per message and computes
28
- * live TPS from the current message and session averages excluding idle time.
104
+ * Usage: Call `onMessageUpdate()` on every tick (e.g. 1s interval) with the
105
+ * current assistant message state. The tracker records generation start/stop
106
+ * per message and computes live TPS from elapsed wall-clock time.
29
107
  */
30
108
  export class TpsTracker {
31
- /** Per-message records */
109
+ /** Per-message records (one per assistant message, in order) */
32
110
  private records: MessageTpsRecord[] = [];
33
111
 
34
- /** Highest message index seen so far (for dedup) */
35
- private lastSeenMessageCount = 0;
36
-
37
- /** Total output tokens across all completed messages */
112
+ /** Total estimated output tokens across all messages */
38
113
  private totalOutput = 0;
39
114
 
40
115
  /**
@@ -42,15 +117,16 @@ export class TpsTracker {
42
117
  * Call this on every tick (e.g. 1s interval) with the current state.
43
118
  *
44
119
  * @param messageIndex - Index of the assistant message (0-based, sequential)
45
- * @param outputTokens - Output tokens for this message
120
+ * @param message - The assistant message object (content used to count tokens)
46
121
  * @param hasStopReason - Whether this message has completed (has stopReason)
47
122
  */
48
- onMessageUpdate(messageIndex: number, outputTokens: number, hasStopReason: boolean): void {
123
+ onMessageUpdate(messageIndex: number, message: unknown, hasStopReason: boolean): void {
49
124
  const now = Date.now();
125
+ const outputTokens = estimateOutputTokens(message);
50
126
 
51
127
  // New message — create a record
52
128
  if (messageIndex >= this.records.length) {
53
- // Fill gaps if indices jump
129
+ // Fill gaps if indices jump (shouldn't normally happen)
54
130
  while (this.records.length < messageIndex) {
55
131
  this.records.push({
56
132
  messageIndex: this.records.length,
@@ -62,11 +138,12 @@ export class TpsTracker {
62
138
  }
63
139
 
64
140
  if (hasStopReason && outputTokens > 0) {
65
- // Fast message: already completed on first sighting
66
- // Estimate duration: floor of 1 second, or outputTokens/100, whichever is smaller
67
- const estimatedDuration = Math.max(1, outputTokens / 100);
141
+ // Fast message: already completed on first sighting.
142
+ // We never observed it streaming, so we can't measure real duration.
143
+ // Fall back to a conservative estimate instead of inventing a 1s
144
+ // window (which previously produced inflated TPS values).
145
+ const estimatedDuration = Math.max(0.5, outputTokens / 60);
68
146
  const tps = outputTokens / estimatedDuration;
69
-
70
147
  this.records.push({
71
148
  messageIndex,
72
149
  outputTokens,
@@ -85,7 +162,6 @@ export class TpsTracker {
85
162
  tps: 0,
86
163
  });
87
164
  }
88
- this.lastSeenMessageCount = messageIndex + 1;
89
165
  return;
90
166
  }
91
167
 
@@ -93,23 +169,24 @@ export class TpsTracker {
93
169
  const record = this.records[messageIndex];
94
170
  if (!record) return;
95
171
 
96
- record.outputTokens = outputTokens;
172
+ if (record.completedAt === 0) {
173
+ // Still generating — update token count (live TPS computed on demand)
174
+ record.outputTokens = outputTokens;
97
175
 
98
- if (record.completedAt === 0 && hasStopReason) {
99
- // Message just completed
100
- record.completedAt = now;
101
- const durationSec = (record.completedAt - record.startedAt) / 1000;
102
- record.tps = durationSec > 0 ? outputTokens / durationSec : outputTokens;
103
- this.totalOutput += outputTokens;
104
- } else if (record.completedAt === 0) {
105
- // Still generating — update output tokens (live TPS computed on demand)
176
+ if (hasStopReason) {
177
+ // Message just completed
178
+ record.completedAt = now;
179
+ const durationSec = (record.completedAt - record.startedAt) / 1000;
180
+ record.tps = durationSec > 0 ? record.outputTokens / durationSec : 0;
181
+ this.totalOutput += record.outputTokens;
182
+ }
106
183
  }
107
184
  }
108
185
 
109
186
  /**
110
187
  * Get the live TPS from the currently generating message.
111
188
  * Returns the instantaneous rate based on tokens generated so far
112
- * in the current message divided by elapsed time.
189
+ * in the current message divided by elapsed wall-clock time.
113
190
  */
114
191
  getLiveTps(): number {
115
192
  // Find the last record that's still generating
@@ -177,7 +254,7 @@ export class TpsTracker {
177
254
  }
178
255
 
179
256
  /**
180
- * Get total output tokens for the session.
257
+ * Get total output tokens for the session (estimated).
181
258
  */
182
259
  getTotalOutput(): number {
183
260
  // Include tokens from incomplete messages too
@@ -195,7 +272,6 @@ export class TpsTracker {
195
272
  */
196
273
  reset(): void {
197
274
  this.records = [];
198
- this.lastSeenMessageCount = 0;
199
275
  this.totalOutput = 0;
200
276
  }
201
277
  }
File without changes
File without changes
File without changes
@@ -9,6 +9,8 @@
9
9
  * /unipi:info-settings - Configure info display
10
10
  */
11
11
 
12
+ import { dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
12
14
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
13
15
  import { UNIPI_EVENTS, MODULES, UNIPI_PREFIX, emitEvent, getPackageVersion, type UnipiModuleEvent, type UnipiInfoGroupEvent } from "@pi-unipi/core";
14
16
  import { infoRegistry } from "./registry.js";
@@ -21,7 +23,7 @@ import { InfoOverlay } from "./tui/info-overlay.js";
21
23
  import { SettingsOverlay } from "./settings/settings-tui.js";
22
24
 
23
25
  /** Package version */
24
- const VERSION = getPackageVersion(new URL(".", import.meta.url).pathname);
26
+ const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
25
27
 
26
28
  export default function (pi: ExtensionAPI) {
27
29
  setPiApi(pi);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/info-screen",
3
- "version": "2.0.13",
4
- "description": "Dashboard and module registry for Unipi \u2014 configurable info overlay with tabbed groups",
3
+ "version": "2.1.1",
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",
@@ -33,11 +33,11 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@pi-unipi/core": "2.0.13"
36
+ "@pi-unipi/core": "2.1.1"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-coding-agent": "^0.78.0",
40
- "@earendil-works/pi-tui": "^0.78.0",
39
+ "@earendil-works/pi-coding-agent": "^0.80.0",
40
+ "@earendil-works/pi-tui": "^0.80.0",
41
41
  "typebox": "^1.1.38"
42
42
  },
43
43
  "devDependencies": {
@@ -94,15 +94,24 @@ export class InfoOverlay implements Component {
94
94
 
95
95
  /**
96
96
  * Fetch all groups in background. Each resolves independently.
97
+ *
98
+ * Each fetch is deferred to a macrotask (setTimeout 0) so the constructor
99
+ * returns immediately. Without this, getGroupData() runs each group's
100
+ * dataProvider synchronously up to its first `await` before yielding —
101
+ * heavy providers (usage stats parse 1GB+ of session files, memory scans)
102
+ * blocked the session_start handler for seconds.
97
103
  */
98
- private async fetchAllBackground(): Promise<void> {
104
+ private fetchAllBackground(): void {
99
105
  for (const group of this.groups) {
100
- // Fire each fetch independently don't await sequentially
101
- infoRegistry.getGroupData(group.id).then(() => {
102
- this.groupLoading.set(group.id, false);
103
- }).catch(() => {
104
- this.groupLoading.set(group.id, false);
105
- });
106
+ // Defer each fetch to a macrotask so the overlay constructs instantly.
107
+ setTimeout(() => {
108
+ if (this._destroyed) return;
109
+ infoRegistry.getGroupData(group.id).then(() => {
110
+ this.groupLoading.set(group.id, false);
111
+ }).catch(() => {
112
+ this.groupLoading.set(group.id, false);
113
+ });
114
+ }, 0);
106
115
  }
107
116
  }
108
117
 
File without changes
File without changes
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/input-shortcuts",
3
- "version": "2.0.13",
4
- "description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle \u2014 chord-based overlay system",
3
+ "version": "2.1.1",
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": "2.0.13"
36
+ "@pi-unipi/core": "2.1.1"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-coding-agent": "^0.78.0",
40
- "@earendil-works/pi-tui": "^0.78.0"
39
+ "@earendil-works/pi-coding-agent": "^0.80.0",
40
+ "@earendil-works/pi-tui": "^0.80.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^25.6.0",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/kanboard",
3
- "version": "2.0.13",
4
- "description": "Visualization layer for unipi workflow \u2014 HTTP server with htmx/Alpine.js UI, modular parsers, TUI overlay, and kanban board",
3
+ "version": "2.1.1",
4
+ "description": "Visualization layer for unipi workflow HTTP server with htmx/Alpine.js UI, modular parsers, TUI overlay, and kanban board",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "license": "MIT",
@@ -39,11 +39,11 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@pi-unipi/core": "2.0.13"
42
+ "@pi-unipi/core": "2.1.1"
43
43
  },
44
44
  "peerDependencies": {
45
- "@earendil-works/pi-coding-agent": "^0.78.0",
46
- "@earendil-works/pi-tui": "^0.78.0"
45
+ "@earendil-works/pi-coding-agent": "^0.80.0",
46
+ "@earendil-works/pi-tui": "^0.80.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^25.6.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/mcp",
3
- "version": "2.0.13",
4
- "description": "MCP server management extension for Pi coding agent \u2014 browse, add, configure, and use MCP servers",
3
+ "version": "2.1.1",
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",
7
7
  "license": "MIT",
@@ -27,11 +27,11 @@
27
27
  "README.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@pi-unipi/core": "2.0.13"
30
+ "@pi-unipi/core": "2.1.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@earendil-works/pi-coding-agent": "^0.78.0",
34
- "@earendil-works/pi-tui": "^0.78.0",
33
+ "@earendil-works/pi-coding-agent": "^0.80.0",
34
+ "@earendil-works/pi-tui": "^0.80.0",
35
35
  "typebox": "^1.1.38"
36
36
  },
37
37
  "devDependencies": {
@@ -4,6 +4,8 @@
4
4
  * Registers commands, handles session lifecycle, wires up MCP server management.
5
5
  */
6
6
 
7
+ import { dirname } from "node:path";
8
+ import { fileURLToPath } from "node:url";
7
9
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
8
10
  import {
9
11
  UNIPI_EVENTS,
@@ -20,7 +22,7 @@ import { renderMcpAddOverlay } from "./tui/add-overlay.js";
20
22
  import { renderMcpSettingsOverlay } from "./tui/settings-overlay.js";
21
23
 
22
24
  /** Package version */
23
- const VERSION = getPackageVersion(new URL(".", import.meta.url).pathname);
25
+ const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
24
26
 
25
27
  /** Module-local registry instance */
26
28
  let registry: ServerRegistry | null = null;
File without changes
@@ -1,8 +1,10 @@
1
1
  # @pi-unipi/memory
2
2
 
3
- Persistent memory that survives across sessions. Stores facts, preferences, and decisions in SQLite with vector search, so the agent remembers what you told it last week.
3
+ Persistent memory that survives across sessions. Stores facts, preferences, and decisions with semantic vector search, so the agent remembers what you told it last week.
4
4
 
5
- Two storage tiers: SQLite + sqlite-vec for vector similarity search, markdown files for human-readable memories you can edit by hand. Project-scoped memories stay separate per codebase, global memories are accessible everywhere.
5
+ **Primary backend: [MemPalace](https://github.com/mempalace/mempalace)** auto-installed via `uv` on first load, with one-way auto-migration of any existing legacy memories. If MemPalace or `uv` is unavailable, the package transparently falls back to the bundled SQLite + sqlite-vec store, so memory never hard-fails.
6
+
7
+ Two storage tiers: MemPalace (or SQLite) for vector similarity search, markdown files for a durable human-readable copy you can edit by hand. Project-scoped memories stay separate per codebase, global memories are accessible everywhere.
6
8
 
7
9
  ## Commands
8
10
 
@@ -72,19 +74,61 @@ Examples:
72
74
  Memory has no configuration file. Storage paths are fixed:
73
75
 
74
76
  ```
75
- ~/.unipi/memory/
77
+ ~/.unipi/memory/ # UniPi memory root (legacy + markdown tier)
78
+ ├── .mempalace-install # Cached MemPalace venv detection
79
+ ├── .mempalace-migrated # One-way migration completion flag
76
80
  ├── global/
77
- │ ├── memory.db # Global vector DB
81
+ │ ├── memory.db # Global vector DB (SQLite fallback)
78
82
  │ └── *.md # Global memory files
79
83
  └── <project_name>/
80
- ├── memory.db # Project vector DB
84
+ ├── memory.db # Project vector DB (SQLite fallback)
81
85
  └── *.md # Project memory files
86
+
87
+ ~/.mempalace/palace/ # MemPalace palace (primary backend)
88
+ ```
89
+
90
+ ## MemPalace backend
91
+
92
+ On first load, the memory package:
93
+ 1. Detects MemPalace; if missing and `uv` is available, runs
94
+ `uv tool install mempalace` once (caches the venv python path in
95
+ `~/.unipi/memory/.mempalace-install`).
96
+ 2. Pings the bridge to confirm the palace is usable.
97
+ 3. If `~/.unipi/memory/.mempalace-migrated` is absent, performs a one-way
98
+ read-only migration of every legacy memory (SQLite rows + markdown files
99
+ across all projects) into MemPalace drawers, then writes the flag.
100
+ Migration is idempotent (deterministic drawer IDs) and never deletes or
101
+ mutates legacy files.
102
+
103
+ Each memory operation invokes a bundled Python bridge
104
+ (`bridge/mempalace_bridge.py`) once via `spawnSync` (~0.5s per call). The
105
+ first MemPalace use on a machine also downloads the default ONNX embedding
106
+ model (~80MB, cached at `~/.cache/chroma/onnx_models/`).
107
+
108
+ ### Forcing re-detection / re-migration
109
+
110
+ ```bash
111
+ rm ~/.unipi/memory/.mempalace-install # re-detect MemPalace next session
112
+ rm ~/.unipi/memory/.mempalace-migrated # re-run one-way migration next session
82
113
  ```
83
114
 
115
+ ### Backend override
116
+
117
+ Set `UNIPI_MEMPALACE_BACKEND` to force a MemPalace backend
118
+ (`sqlite_exact`, `qdrant`, `pgvector`, default `chroma`).
119
+
120
+ ### Embedder identity
121
+
122
+ MemPalace enforces embedder identity. If a palace was created with a
123
+ different embedding model, writes are rejected — the package then falls
124
+ back to SQLite for that session. Use `mempalace palace set-embedder`
125
+ intentionally to realign, then remove the install cache to re-detect.
126
+
84
127
  ## Dependencies
85
128
 
86
- - `better-sqlite3` — SQLite database
87
- - `sqlite-vec` — Vector search extension
129
+ - `mempalace` (Python, auto-installed via `uv`)primary backend
130
+ - `better-sqlite3` — SQLite fallback database
131
+ - `sqlite-vec` — Vector search extension (fallback)
88
132
  - `js-yaml` — YAML frontmatter parsing
89
133
  - `@pi-unipi/core` — Shared utilities
90
134
 
@@ -7,6 +7,8 @@
7
7
  * Auto-consolidates on compaction.
8
8
  */
9
9
 
10
+ import { dirname } from "node:path";
11
+ import { fileURLToPath } from "node:url";
10
12
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
11
13
  import {
12
14
  UNIPI_EVENTS,
@@ -30,7 +32,7 @@ import { registerMemoryCommands } from "./commands.js";
30
32
  import { isEmbeddingReady, hasModelChanged } from "./settings.js";
31
33
 
32
34
  /** Package version */
33
- const VERSION = getPackageVersion(new URL(".", import.meta.url).pathname);
35
+ const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
34
36
 
35
37
  /** Storage instance for current project */
36
38
  let projectStorage: MemoryStorage | null = null;
@@ -170,11 +172,12 @@ export default function (pi: ExtensionAPI) {
170
172
  } catch (_err) {
171
173
  // Count unavailable — status bar shows 0.
172
174
  }
173
- const vecReady = isEmbeddingReady();
174
- const vecIcon = vecReady ? "⚡" : "📝";
175
+ const mempalaceActive = projectStorage?.isMempalace() ?? false;
176
+ const backendIcon = mempalaceActive ? "🧠" : (isEmbeddingReady() ? "⚡" : "📝");
177
+ const warn = hasModelChanged() ? " ⚠" : "";
175
178
  ctx.ui.setStatus(
176
179
  "unipi-memory",
177
- `${vecIcon} mem ${projectCount}p/${projectCountAll}all${hasModelChanged() ? " ⚠" : ""}`
180
+ `${backendIcon} mem ${projectCount}p/${projectCountAll}all${warn}`
178
181
  );
179
182
  }
180
183
  });