@pi-unipi/core 0.1.16 → 2.0.0

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/constants.ts CHANGED
@@ -38,6 +38,7 @@ export const MODULES = {
38
38
  FOOTER: "@pi-unipi/footer",
39
39
  UPDATER: "@pi-unipi/updater",
40
40
  INPUT_SHORTCUTS: "@pi-unipi/input-shortcuts",
41
+ COCOINDEX: "@pi-unipi/cocoindex",
41
42
  } as const;
42
43
 
43
44
  /** Workflow command names */
@@ -215,6 +216,11 @@ export const MCP_DEFAULTS = {
215
216
  TOOL_NAME_SEPARATOR: "__",
216
217
  } as const;
217
218
 
219
+ /** Compactor sentinel — when passed as customInstructions to ctx.compact(),
220
+ * the compactor extension recognizes it and uses its zero-LLM pipeline.
221
+ * Other extensions can use this to trigger compactor-aware compaction. */
222
+ export const COMPACTOR_INSTRUCTION = "__compactor__" as const;
223
+
218
224
  /** Compactor tool names */
219
225
  export const COMPACTOR_TOOLS = {
220
226
  COMPACT: "compact",
@@ -222,24 +228,19 @@ export const COMPACTOR_TOOLS = {
222
228
  CTX_EXECUTE: "ctx_execute",
223
229
  CTX_EXECUTE_FILE: "ctx_execute_file",
224
230
  CTX_BATCH_EXECUTE: "ctx_batch_execute",
225
- CTX_INDEX: "ctx_index",
226
- CTX_SEARCH: "ctx_search",
227
- CTX_FETCH_AND_INDEX: "ctx_fetch_and_index",
228
231
  CTX_STATS: "ctx_stats",
229
232
  CTX_DOCTOR: "ctx_doctor",
230
233
  } as const;
231
234
 
232
235
  /** Compactor command names */
233
236
  export const COMPACTOR_COMMANDS = {
237
+ LOSSLESS_COMPACT: "lossless-compact",
234
238
  COMPACT: "compact",
235
239
  COMPACT_RECALL: "compact-recall",
236
240
  COMPACT_STATS: "compact-stats",
237
241
  COMPACT_DOCTOR: "compact-doctor",
238
242
  COMPACT_SETTINGS: "compact-settings",
239
243
  COMPACT_PRESET: "compact-preset",
240
- COMPACT_INDEX: "compact-index",
241
- COMPACT_SEARCH: "compact-search",
242
- COMPACT_PURGE: "compact-purge",
243
244
  } as const;
244
245
 
245
246
  /** Compactor directory paths */
@@ -334,5 +335,25 @@ export const COMPACTOR_DEFAULTS = {
334
335
  DEFAULT_TIMEOUT_MS: 30000,
335
336
  SESSION_TTL_DAYS: 7,
336
337
  CACHE_TTL_HOURS: 24,
337
- FTS5_CHUNK_SIZE: 4096,
338
+ } as const;
339
+
340
+ /** CocoIndex minimum supported CLI version. */
341
+ export const COCOINDEX_MIN_VERSION = "1.0" as const;
342
+
343
+ /** CocoIndex package spec installed by the interactive installer. */
344
+ export const COCOINDEX_PACKAGE_SPEC = `cocoindex[lancedb]>=${COCOINDEX_MIN_VERSION}` as const;
345
+
346
+ /** CocoIndex tool names */
347
+ export const COCOINDEX_TOOLS = {
348
+ SEARCH: "cocoindex_search",
349
+ STATUS: "cocoindex_status",
350
+ } as const;
351
+
352
+ /** CocoIndex command names */
353
+ export const COCOINDEX_COMMANDS = {
354
+ UPDATE: "cocoindex-update",
355
+ STATUS: "cocoindex-status",
356
+ INIT: "cocoindex-init",
357
+ SETTINGS: "cocoindex-settings",
358
+ SEARCH: "cocoindex-search",
338
359
  } as const;
package/events.ts CHANGED
@@ -90,6 +90,13 @@ export const UNIPI_EVENTS = {
90
90
  UPDATE_APPLIED: "unipi:update:applied",
91
91
  /** Update error */
92
92
  UPDATE_ERROR: "unipi:update:error",
93
+
94
+ /** CocoIndex: update started */
95
+ COCOINDEX_UPDATE_STARTED: "unipi:cocoindex:update:started",
96
+ /** CocoIndex: update completed */
97
+ COCOINDEX_UPDATE_COMPLETED: "unipi:cocoindex:update:completed",
98
+ /** CocoIndex: search performed */
99
+ COCOINDEX_SEARCH_PERFORMED: "unipi:cocoindex:search:performed",
93
100
  } as const;
94
101
 
95
102
  /** Payload for MODULE_READY / MODULE_GONE */
@@ -102,6 +109,8 @@ export interface UnipiModuleEvent {
102
109
  commands: string[];
103
110
  /** Tools registered by this module */
104
111
  tools: string[];
112
+ /** Load time in milliseconds (optional) */
113
+ loadTimeMs?: number;
105
114
  }
106
115
 
107
116
  /** Payload for WORKFLOW_START / WORKFLOW_END */
@@ -268,8 +277,6 @@ export interface UnipiCompactorStatsEvent {
268
277
  compactions: number;
269
278
  /** Tokens saved total */
270
279
  tokensSaved: number;
271
- /** Indexed documents count */
272
- indexedDocs: number;
273
280
  /** Sandbox executions count */
274
281
  sandboxRuns: number;
275
282
  /** Search queries count */
@@ -368,6 +375,32 @@ export interface UnipiUpdateErrorEvent {
368
375
  phase: "check" | "install";
369
376
  }
370
377
 
378
+ /** Payload for COCOINDEX_UPDATE_STARTED */
379
+ export interface UnipiCocoindexUpdateStartedEvent {
380
+ /** Project directory being indexed */
381
+ projectDir: string;
382
+ }
383
+
384
+ /** Payload for COCOINDEX_UPDATE_COMPLETED */
385
+ export interface UnipiCocoindexUpdateCompletedEvent {
386
+ /** Whether the update succeeded */
387
+ success: boolean;
388
+ /** Number of chunks processed */
389
+ chunksProcessed: number;
390
+ /** Duration in ms */
391
+ durationMs: number;
392
+ /** Error message if failed */
393
+ error?: string;
394
+ }
395
+
396
+ /** Payload for COCOINDEX_SEARCH_PERFORMED */
397
+ export interface UnipiCocoindexSearchPerformedEvent {
398
+ /** Search query */
399
+ query: string;
400
+ /** Number of results */
401
+ resultCount: number;
402
+ }
403
+
371
404
  /** Payload for NOTIFICATION_SENT */
372
405
  export interface UnipiNotificationSentEvent {
373
406
  /** Event type that triggered notification */
@@ -408,4 +441,7 @@ export type UnipiEventPayload =
408
441
  | UnipiUpdateCheckEvent
409
442
  | UnipiUpdateAvailableEvent
410
443
  | UnipiUpdateAppliedEvent
411
- | UnipiUpdateErrorEvent;
444
+ | UnipiUpdateErrorEvent
445
+ | UnipiCocoindexUpdateStartedEvent
446
+ | UnipiCocoindexUpdateCompletedEvent
447
+ | UnipiCocoindexSearchPerformedEvent;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @unipi/core — Minimal interfaces for global registry objects
3
+ *
4
+ * These interfaces cover the methods that external packages call.
5
+ * The actual implementations live in their respective packages
6
+ * (info-screen, footer, mcp) and may have richer APIs.
7
+ */
8
+
9
+ /** Stat data shape returned by info data providers */
10
+ export interface StatDataLike {
11
+ value: string;
12
+ detail?: string;
13
+ }
14
+
15
+ /** Minimal InfoRegistry interface for external consumers */
16
+ export interface InfoRegistryLike {
17
+ registerGroup(group: {
18
+ id: string;
19
+ name: string;
20
+ icon: string;
21
+ priority: number;
22
+ config: {
23
+ showByDefault: boolean;
24
+ stats: Array<{ id: string; label: string; show: boolean }>;
25
+ };
26
+ dataProvider: () => Promise<Record<string, unknown>>;
27
+ }): void;
28
+ getGroupData(groupId: string): Promise<Record<string, unknown>>;
29
+ getCachedData(groupId: string): Record<string, unknown> | null;
30
+ invalidateCache(groupId: string): void;
31
+ refreshGroup(groupId: string): Record<string, unknown> | null;
32
+ refreshAll(): void;
33
+ }
34
+
35
+ /** Minimal FooterRegistry interface for external consumers */
36
+ export interface FooterRegistryLike {
37
+ registerGroup(group: unknown): void;
38
+ }
39
+
40
+ /** MCP stats shape exposed via globalThis */
41
+ export interface McpStatsLike {
42
+ serversTotal?: number;
43
+ serversActive?: number;
44
+ serversFailed?: number;
45
+ toolsTotal?: number;
46
+ }
package/global.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @unipi/core — Global type augmentations
3
+ *
4
+ * Declares `__unipi_*` properties on `globalThis` so packages
5
+ * can access shared registries without `as any` casts.
6
+ */
7
+
8
+ declare global {
9
+ // eslint-disable-next-line no-var
10
+ var __unipi_info_registry: import("./global-types.js").InfoRegistryLike | undefined;
11
+
12
+ // eslint-disable-next-line no-var
13
+ var __unipi_footer_registry: import("./global-types.js").FooterRegistryLike | undefined;
14
+
15
+ // eslint-disable-next-line no-var
16
+ var __unipi_kanboard_registry: unknown;
17
+
18
+ // eslint-disable-next-line no-var
19
+ var __unipi_mcp_stats: import("./global-types.js").McpStatsLike | undefined;
20
+ }
21
+
22
+ // Force this to be treated as a module
23
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/core",
3
- "version": "0.1.16",
3
+ "version": "2.0.0",
4
4
  "description": "Shared utilities, event types, and constants for Unipi extension suite",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/sandbox.ts CHANGED
@@ -10,20 +10,40 @@ import { WORKFLOW_COMMANDS } from "./constants.js";
10
10
  /** Sandbox levels */
11
11
  export type SandboxLevel = "read_only" | "brainstorm" | "write_unipi" | "review" | "full";
12
12
 
13
- /** Tool sets per sandbox level */
14
- const SANDBOX_TOOLS: Record<SandboxLevel, readonly string[]> = {
15
- /** Only read-only tools — no bash, no write, no edit */
13
+ /**
14
+ * Built-in workflow tools used when no active tool list is supplied.
15
+ *
16
+ * Workflow commands should normally call filterToolsForLevel() with the current
17
+ * active tool list. That keeps safe extension tools (memory, ask-user, web,
18
+ * notify, etc.) available while removing only tools that violate the sandbox.
19
+ */
20
+ const FALLBACK_TOOLS: Record<SandboxLevel, readonly string[]> = {
21
+ /** Only read-only file tools — no bash, no write, no edit */
16
22
  read_only: ["read", "grep", "find", "ls"],
17
- /** Read + constrained write + bash (setup only) — only to .unipi/docs/specs/ */
23
+ /** Read + constrained write + bash (setup only) — only to .unipi/docs/specs/ by instruction */
18
24
  brainstorm: ["read", "grep", "find", "ls", "write", "bash"],
19
25
  /** Read + write/edit + file discovery — no bash */
20
26
  write_unipi: ["read", "write", "edit", "grep", "find", "ls"],
21
- /** Read + write + bash for git operations — no code editing outside .unipi */
27
+ /** Read + write/edit + bash for git/checks — no code editing by instruction */
22
28
  review: ["read", "write", "edit", "grep", "find", "ls", "bash"],
23
- /** All tools */
29
+ /** Full workflow access */
24
30
  full: ["read", "write", "edit", "bash"],
25
31
  };
26
32
 
33
+ /** Tools that are removed from the current active tool set at each level. */
34
+ const BLOCKED_TOOLS: Record<SandboxLevel, readonly string[]> = {
35
+ /** Read-only workflows must not mutate files or run shell commands. */
36
+ read_only: ["write", "edit", "bash"],
37
+ /** Brainstorm may write specs and run limited setup commands, but should not edit existing files. */
38
+ brainstorm: ["edit"],
39
+ /** Planning/docs workflows may write .unipi docs, but should not run shell commands. */
40
+ write_unipi: ["bash"],
41
+ /** Review workflows rely on command instructions to constrain writes to reviewer remarks. */
42
+ review: [],
43
+ /** Full workflows do not filter active tools. */
44
+ full: [],
45
+ };
46
+
27
47
  /** Command to sandbox level mapping */
28
48
  const COMMAND_SANDBOX: Record<string, SandboxLevel> = {
29
49
  [WORKFLOW_COMMANDS.BRAINSTORM]: "brainstorm",
@@ -56,25 +76,53 @@ export function getSandboxLevel(commandName: string): SandboxLevel {
56
76
  }
57
77
 
58
78
  /**
59
- * Get allowed tools for a sandbox level.
79
+ * Get fallback tools for a sandbox level.
80
+ *
81
+ * Prefer filterToolsForLevel(level, activeTools) when applying a sandbox so
82
+ * extension tools are preserved unless explicitly blocked.
60
83
  */
61
84
  export function getToolsForLevel(level: SandboxLevel): readonly string[] {
62
- return SANDBOX_TOOLS[level];
85
+ return FALLBACK_TOOLS[level];
86
+ }
87
+
88
+ /**
89
+ * Get tool names explicitly blocked by a sandbox level.
90
+ */
91
+ export function getBlockedToolsForLevel(level: SandboxLevel): readonly string[] {
92
+ return BLOCKED_TOOLS[level];
93
+ }
94
+
95
+ /**
96
+ * Filter the current active tool set for a sandbox level.
97
+ *
98
+ * This preserves safe extension tools (memory_search, memory_store, ask_user,
99
+ * web tools, notifications, etc.) instead of replacing the tool list with a
100
+ * small built-in allow-list. Only tools that violate the level are removed.
101
+ */
102
+ export function filterToolsForLevel(
103
+ level: SandboxLevel,
104
+ activeTools: readonly string[],
105
+ ): readonly string[] {
106
+ const blocked = new Set(BLOCKED_TOOLS[level]);
107
+ return activeTools.filter((tool) => !blocked.has(tool));
63
108
  }
64
109
 
65
110
  /**
66
111
  * Get allowed tools for a command.
67
112
  */
68
- export function getToolsForCommand(commandName: string): readonly string[] {
113
+ export function getToolsForCommand(
114
+ commandName: string,
115
+ activeTools?: readonly string[],
116
+ ): readonly string[] {
69
117
  const level = getSandboxLevel(commandName);
70
- return getToolsForLevel(level);
118
+ return activeTools ? filterToolsForLevel(level, activeTools) : getToolsForLevel(level);
71
119
  }
72
120
 
73
121
  /**
74
122
  * Check if a tool is allowed at a sandbox level.
75
123
  */
76
124
  export function isToolAllowed(level: SandboxLevel, toolName: string): boolean {
77
- return SANDBOX_TOOLS[level].includes(toolName);
125
+ return !BLOCKED_TOOLS[level].includes(toolName);
78
126
  }
79
127
 
80
128
  /**
@@ -82,7 +130,7 @@ export function isToolAllowed(level: SandboxLevel, toolName: string): boolean {
82
130
  */
83
131
  export function hasWriteAccess(commandName: string): boolean {
84
132
  const level = getSandboxLevel(commandName);
85
- return level === "write_unipi" || level === "full";
133
+ return !BLOCKED_TOOLS[level].includes("write");
86
134
  }
87
135
 
88
136
  /**
@@ -90,5 +138,5 @@ export function hasWriteAccess(commandName: string): boolean {
90
138
  */
91
139
  export function hasBashAccess(commandName: string): boolean {
92
140
  const level = getSandboxLevel(commandName);
93
- return level === "full" || level === "brainstorm";
141
+ return !BLOCKED_TOOLS[level].includes("bash");
94
142
  }