@pi-unipi/unipi 2.5.0 → 2.6.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/CHANGELOG.md +24 -0
- package/README.md +2 -0
- package/docs/prefix-cache-architecture.md +89 -0
- package/package.json +22 -21
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -0
- package/packages/btw/package.json +2 -2
- package/packages/cocoindex/README.md +2 -1
- package/packages/cocoindex/index.ts +6 -13
- package/packages/cocoindex/package.json +4 -3
- package/packages/cocoindex/tools.ts +45 -11
- package/packages/compactor/README.md +4 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/src/session/snapshot.ts +3 -2
- package/packages/compactor/src/tools/register.ts +6 -2
- package/packages/compactor/src/tools/vcc-recall.ts +18 -3
- package/packages/core/bounded-output.ts +106 -0
- package/packages/core/constants.ts +2 -0
- package/packages/core/index.ts +1 -0
- package/packages/core/package.json +1 -1
- package/packages/footer/package.json +2 -2
- package/packages/image/package.json +2 -2
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/README.md +4 -0
- package/packages/mcp/package.json +2 -2
- package/packages/mcp/src/bridge/translator.ts +19 -12
- package/packages/memory/index.ts +56 -34
- package/packages/memory/package.json +3 -3
- package/packages/milestone/hooks.ts +1 -1
- package/packages/milestone/package.json +2 -2
- package/packages/notify/package.json +2 -2
- package/packages/ralph/index.ts +12 -18
- package/packages/ralph/package.json +6 -3
- package/packages/ralph/reminder.ts +40 -0
- package/packages/ralph/tools.ts +5 -1
- package/packages/subagents/README.md +2 -0
- package/packages/subagents/package.json +1 -1
- package/packages/subagents/src/agent-manager.ts +5 -1
- package/packages/subagents/src/agent-runner.ts +2 -2
- package/packages/subagents/src/core-compat.ts +73 -0
- package/packages/subagents/src/custom-agents.ts +10 -2
- package/packages/subagents/src/index.ts +14 -3
- package/packages/subagents/src/types.ts +2 -0
- package/packages/unipi/bundled.js +786 -312
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +9 -0
- package/packages/utility/package.json +2 -2
- package/packages/utility/src/index.ts +48 -0
- package/packages/utility/src/lifecycle/cleanup.ts +29 -0
- package/packages/utility/src/prefix-cache.ts +263 -0
- package/packages/utility/src/types.ts +1 -1
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/index.ts +2 -2
- package/packages/workflow/package.json +2 -2
|
@@ -162,7 +162,8 @@ var init_constants = __esm({
|
|
|
162
162
|
BADGE_GEN: "badge-gen",
|
|
163
163
|
BADGE_TOGGLE: "badge-toggle",
|
|
164
164
|
BADGE_SETTINGS: "badge-settings",
|
|
165
|
-
UTIL_SETTINGS: "util-settings"
|
|
165
|
+
UTIL_SETTINGS: "util-settings",
|
|
166
|
+
PREFIX_CACHE: "prefix-cache"
|
|
166
167
|
};
|
|
167
168
|
UTILITY_TOOLS = {
|
|
168
169
|
CONTINUE: "continue_task",
|
|
@@ -206,7 +207,8 @@ var init_constants = __esm({
|
|
|
206
207
|
SYNC_INTERVAL_MS: 864e5,
|
|
207
208
|
STARTUP_TIMEOUT_MS: 1e4,
|
|
208
209
|
MAX_SERVERS: 20,
|
|
209
|
-
TOOL_NAME_SEPARATOR: "__"
|
|
210
|
+
TOOL_NAME_SEPARATOR: "__",
|
|
211
|
+
MAX_MODEL_OUTPUT_BYTES: 64 * 1024
|
|
210
212
|
};
|
|
211
213
|
COMPACTOR_INSTRUCTION = "__compactor__";
|
|
212
214
|
COMPACTOR_TOOLS = {
|
|
@@ -758,6 +760,92 @@ var init_tui_width = __esm({
|
|
|
758
760
|
}
|
|
759
761
|
});
|
|
760
762
|
|
|
763
|
+
// packages/core/bounded-output.ts
|
|
764
|
+
import { chmodSync, lstatSync, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
765
|
+
import { homedir } from "node:os";
|
|
766
|
+
import { join as join3 } from "node:path";
|
|
767
|
+
import { randomUUID } from "node:crypto";
|
|
768
|
+
function byteSlice(text, start, end) {
|
|
769
|
+
return Buffer.from(text, "utf8").subarray(start, end).toString("utf8").replace(/\uFFFD+$/u, "");
|
|
770
|
+
}
|
|
771
|
+
function secureArtifactDir(customDir) {
|
|
772
|
+
const dir = customDir ?? join3(homedir(), ".unipi", "tool-results");
|
|
773
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
774
|
+
let stat = lstatSync(dir);
|
|
775
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
776
|
+
throw new Error(`Refusing unsafe tool-result directory: ${dir}`);
|
|
777
|
+
}
|
|
778
|
+
if ((stat.mode & 63) !== 0) {
|
|
779
|
+
chmodSync(dir, 448);
|
|
780
|
+
stat = lstatSync(dir);
|
|
781
|
+
if ((stat.mode & 63) !== 0) {
|
|
782
|
+
throw new Error(`Refusing non-private tool-result directory: ${dir}`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return dir;
|
|
786
|
+
}
|
|
787
|
+
function safePrefix(prefix) {
|
|
788
|
+
const safe = prefix.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").slice(0, 48);
|
|
789
|
+
return safe || "tool-result";
|
|
790
|
+
}
|
|
791
|
+
function boundModelOutput(text, options = {}) {
|
|
792
|
+
const maxBytes = Math.max(1024, Math.floor(options.maxBytes ?? DEFAULT_MODEL_OUTPUT_BYTES));
|
|
793
|
+
const originalBytes = Buffer.byteLength(text, "utf8");
|
|
794
|
+
if (originalBytes <= maxBytes) {
|
|
795
|
+
return { text, truncated: false, originalBytes, visibleBytes: originalBytes };
|
|
796
|
+
}
|
|
797
|
+
let artifactPath;
|
|
798
|
+
let artifactWarning;
|
|
799
|
+
if (originalBytes <= MAX_RAW_ARTIFACT_BYTES) {
|
|
800
|
+
try {
|
|
801
|
+
const dir = secureArtifactDir(options.artifactDir);
|
|
802
|
+
artifactPath = join3(dir, `${safePrefix(options.artifactPrefix ?? "tool-result")}-${randomUUID()}.txt`);
|
|
803
|
+
writeFileSync3(artifactPath, text, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
804
|
+
} catch (error) {
|
|
805
|
+
artifactWarning = `Full-output artifact unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
806
|
+
artifactPath = void 0;
|
|
807
|
+
}
|
|
808
|
+
} else {
|
|
809
|
+
artifactWarning = `Full output exceeded the ${MAX_RAW_ARTIFACT_BYTES}-byte local artifact safety cap and was not retained.`;
|
|
810
|
+
}
|
|
811
|
+
const marker = [
|
|
812
|
+
"",
|
|
813
|
+
"--- output bounded by UniPi ---",
|
|
814
|
+
artifactPath ? `Full output: ${artifactPath}` : artifactWarning,
|
|
815
|
+
`Original size: ${originalBytes} bytes; model-visible ceiling: ${maxBytes} bytes.`,
|
|
816
|
+
...artifactPath ? ["Use the read tool with offset/limit to inspect only the needed region."] : []
|
|
817
|
+
].join("\n");
|
|
818
|
+
const omissionReserve = 80;
|
|
819
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
820
|
+
const contentBudget = Math.max(1, maxBytes - markerBytes - omissionReserve);
|
|
821
|
+
const headBytes = Math.ceil(contentBudget * 0.75);
|
|
822
|
+
const tailBytes = Math.max(0, contentBudget - headBytes);
|
|
823
|
+
const head = byteSlice(text, 0, headBytes);
|
|
824
|
+
const tail2 = tailBytes > 0 ? byteSlice(text, originalBytes - tailBytes) : "";
|
|
825
|
+
const omission = `
|
|
826
|
+
\u2026 ${Math.max(0, originalBytes - contentBudget)} bytes omitted \u2026
|
|
827
|
+
`;
|
|
828
|
+
let bounded = `${head}${omission}${tail2}${marker}`;
|
|
829
|
+
if (Buffer.byteLength(bounded, "utf8") > maxBytes) {
|
|
830
|
+
bounded = byteSlice(bounded, 0, maxBytes);
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
text: bounded,
|
|
834
|
+
truncated: true,
|
|
835
|
+
originalBytes,
|
|
836
|
+
visibleBytes: Buffer.byteLength(bounded, "utf8"),
|
|
837
|
+
artifactPath
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
var DEFAULT_MODEL_OUTPUT_BYTES, MAX_RAW_ARTIFACT_BYTES;
|
|
841
|
+
var init_bounded_output = __esm({
|
|
842
|
+
"packages/core/bounded-output.ts"() {
|
|
843
|
+
"use strict";
|
|
844
|
+
DEFAULT_MODEL_OUTPUT_BYTES = 64 * 1024;
|
|
845
|
+
MAX_RAW_ARTIFACT_BYTES = 16 * 1024 * 1024;
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
|
|
761
849
|
// packages/core/index.ts
|
|
762
850
|
var core_exports = {};
|
|
763
851
|
__export(core_exports, {
|
|
@@ -773,6 +861,7 @@ __export(core_exports, {
|
|
|
773
861
|
COMPACTOR_DIRS: () => COMPACTOR_DIRS,
|
|
774
862
|
COMPACTOR_INSTRUCTION: () => COMPACTOR_INSTRUCTION,
|
|
775
863
|
COMPACTOR_TOOLS: () => COMPACTOR_TOOLS,
|
|
864
|
+
DEFAULT_MODEL_OUTPUT_BYTES: () => DEFAULT_MODEL_OUTPUT_BYTES,
|
|
776
865
|
FOOTER_COMMANDS: () => FOOTER_COMMANDS,
|
|
777
866
|
IMAGE_COMMANDS: () => IMAGE_COMMANDS,
|
|
778
867
|
IMAGE_DIRS: () => IMAGE_DIRS,
|
|
@@ -781,6 +870,7 @@ __export(core_exports, {
|
|
|
781
870
|
KANBOARD_COMMANDS: () => KANBOARD_COMMANDS,
|
|
782
871
|
KANBOARD_DEFAULTS: () => KANBOARD_DEFAULTS,
|
|
783
872
|
KANBOARD_DIRS: () => KANBOARD_DIRS,
|
|
873
|
+
MAX_RAW_ARTIFACT_BYTES: () => MAX_RAW_ARTIFACT_BYTES,
|
|
784
874
|
MCP_COMMANDS: () => MCP_COMMANDS,
|
|
785
875
|
MCP_DEFAULTS: () => MCP_DEFAULTS,
|
|
786
876
|
MCP_DIRS: () => MCP_DIRS,
|
|
@@ -815,6 +905,7 @@ __export(core_exports, {
|
|
|
815
905
|
WORKFLOW_COMMANDS: () => WORKFLOW_COMMANDS,
|
|
816
906
|
WidthKeyedCache: () => WidthKeyedCache,
|
|
817
907
|
adaptiveInnerWidth: () => adaptiveInnerWidth,
|
|
908
|
+
boundModelOutput: () => boundModelOutput,
|
|
818
909
|
boxInnerWidth: () => boxInnerWidth,
|
|
819
910
|
contentWidth: () => contentWidth,
|
|
820
911
|
emitEvent: () => emitEvent,
|
|
@@ -863,6 +954,7 @@ var init_core = __esm({
|
|
|
863
954
|
init_utils();
|
|
864
955
|
init_model_cache();
|
|
865
956
|
init_tui_width();
|
|
957
|
+
init_bounded_output();
|
|
866
958
|
}
|
|
867
959
|
});
|
|
868
960
|
|
|
@@ -2330,12 +2422,12 @@ var init_embedding = __esm({
|
|
|
2330
2422
|
});
|
|
2331
2423
|
|
|
2332
2424
|
// packages/info-screen/usage-parser.ts
|
|
2333
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync5, existsSync as existsSync9, mkdirSync as
|
|
2334
|
-
import { join as
|
|
2335
|
-
import { homedir as
|
|
2425
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync5, existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9, renameSync as renameSync3, openSync, readSync, closeSync } from "node:fs";
|
|
2426
|
+
import { join as join10, dirname as dirname9 } from "node:path";
|
|
2427
|
+
import { homedir as homedir6 } from "node:os";
|
|
2336
2428
|
function getCachePath() {
|
|
2337
|
-
const base = process.env.UNIPI_DIR ||
|
|
2338
|
-
return
|
|
2429
|
+
const base = process.env.UNIPI_DIR || join10(homedir6(), ".unipi");
|
|
2430
|
+
return join10(base, "cache", "usage-stats.json");
|
|
2339
2431
|
}
|
|
2340
2432
|
function readCache() {
|
|
2341
2433
|
const empty = { version: CACHE_VERSION, models: [], files: {} };
|
|
@@ -2353,16 +2445,16 @@ function readCache() {
|
|
|
2353
2445
|
function writeCache(cache) {
|
|
2354
2446
|
try {
|
|
2355
2447
|
const path28 = getCachePath();
|
|
2356
|
-
|
|
2448
|
+
mkdirSync8(dirname9(path28), { recursive: true });
|
|
2357
2449
|
const tmp = `${path28}.${process.pid}.tmp`;
|
|
2358
|
-
|
|
2450
|
+
writeFileSync9(tmp, JSON.stringify(cache), "utf-8");
|
|
2359
2451
|
renameSync3(tmp, path28);
|
|
2360
2452
|
} catch {
|
|
2361
2453
|
}
|
|
2362
2454
|
}
|
|
2363
2455
|
function getSessionsDir() {
|
|
2364
|
-
const agentDir = process.env.PI_CODING_AGENT_DIR ||
|
|
2365
|
-
return
|
|
2456
|
+
const agentDir = process.env.PI_CODING_AGENT_DIR || join10(homedir6(), ".pi", "agent");
|
|
2457
|
+
return join10(agentDir, "sessions");
|
|
2366
2458
|
}
|
|
2367
2459
|
function getPeriodBounds() {
|
|
2368
2460
|
const now2 = /* @__PURE__ */ new Date();
|
|
@@ -2454,7 +2546,7 @@ function collectSessionFiles(dir, files) {
|
|
|
2454
2546
|
try {
|
|
2455
2547
|
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
2456
2548
|
for (const entry of entries) {
|
|
2457
|
-
const entryPath =
|
|
2549
|
+
const entryPath = join10(dir, entry.name);
|
|
2458
2550
|
if (entry.isDirectory()) {
|
|
2459
2551
|
collectSessionFiles(entryPath, files);
|
|
2460
2552
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -2684,8 +2776,8 @@ __export(env_exports, {
|
|
|
2684
2776
|
getEnvironmentInfo: () => getEnvironmentInfo
|
|
2685
2777
|
});
|
|
2686
2778
|
import { existsSync as existsSync18, readdirSync as readdirSync11 } from "node:fs";
|
|
2687
|
-
import { join as
|
|
2688
|
-
import { homedir as
|
|
2779
|
+
import { join as join20, resolve as resolve6 } from "node:path";
|
|
2780
|
+
import { homedir as homedir16 } from "node:os";
|
|
2689
2781
|
function getEnvironmentInfo() {
|
|
2690
2782
|
const unipiModules = [];
|
|
2691
2783
|
const configPaths = [];
|
|
@@ -2693,11 +2785,11 @@ function getEnvironmentInfo() {
|
|
|
2693
2785
|
try {
|
|
2694
2786
|
const nodeModules = resolve6(process.cwd(), "node_modules");
|
|
2695
2787
|
if (existsSync18(nodeModules)) {
|
|
2696
|
-
const scopePath =
|
|
2788
|
+
const scopePath = join20(nodeModules, "@pi-unipi");
|
|
2697
2789
|
if (existsSync18(scopePath)) {
|
|
2698
2790
|
for (const entry of readdirSync11(scopePath)) {
|
|
2699
2791
|
if (entry.startsWith(".")) continue;
|
|
2700
|
-
const pkgPath =
|
|
2792
|
+
const pkgPath = join20(scopePath, entry, "package.json");
|
|
2701
2793
|
if (existsSync18(pkgPath)) {
|
|
2702
2794
|
unipiModules.push(`@pi-unipi/${entry}`);
|
|
2703
2795
|
}
|
|
@@ -2706,7 +2798,7 @@ function getEnvironmentInfo() {
|
|
|
2706
2798
|
}
|
|
2707
2799
|
} catch {
|
|
2708
2800
|
}
|
|
2709
|
-
const globalConfig =
|
|
2801
|
+
const globalConfig = join20(homedir16(), ".unipi", "config");
|
|
2710
2802
|
const projectConfig = resolve6(process.cwd(), ".unipi", "config");
|
|
2711
2803
|
if (existsSync18(globalConfig)) {
|
|
2712
2804
|
configPaths.push(globalConfig);
|
|
@@ -2715,7 +2807,7 @@ function getEnvironmentInfo() {
|
|
|
2715
2807
|
configPaths.push(projectConfig);
|
|
2716
2808
|
}
|
|
2717
2809
|
try {
|
|
2718
|
-
const piDir =
|
|
2810
|
+
const piDir = join20(homedir16(), ".pi");
|
|
2719
2811
|
if (existsSync18(piDir)) {
|
|
2720
2812
|
extensionPaths.push(piDir);
|
|
2721
2813
|
}
|
|
@@ -3952,11 +4044,11 @@ var init_schema = __esm({
|
|
|
3952
4044
|
});
|
|
3953
4045
|
|
|
3954
4046
|
// packages/compactor/src/config/manager.ts
|
|
3955
|
-
import { existsSync as existsSync31, mkdirSync as
|
|
3956
|
-
import { homedir as
|
|
3957
|
-
import { dirname as dirname24, join as
|
|
4047
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync21, readFileSync as readFileSync29, writeFileSync as writeFileSync25 } from "node:fs";
|
|
4048
|
+
import { homedir as homedir22 } from "node:os";
|
|
4049
|
+
import { dirname as dirname24, join as join34 } from "node:path";
|
|
3958
4050
|
function projectConfigPath(cwd) {
|
|
3959
|
-
return
|
|
4051
|
+
return join34(cwd, ".unipi", "config", "compactor.json");
|
|
3960
4052
|
}
|
|
3961
4053
|
function deepMerge(base, override) {
|
|
3962
4054
|
const result = { ...base };
|
|
@@ -3992,8 +4084,8 @@ function saveConfig3(config, opts) {
|
|
|
3992
4084
|
try {
|
|
3993
4085
|
const targetPath = opts?.perProject && opts?.cwd ? projectConfigPath(opts.cwd) : COMPACTOR_CONFIG_PATH;
|
|
3994
4086
|
const dir = dirname24(targetPath);
|
|
3995
|
-
if (!existsSync31(dir))
|
|
3996
|
-
|
|
4087
|
+
if (!existsSync31(dir)) mkdirSync21(dir, { recursive: true });
|
|
4088
|
+
writeFileSync25(targetPath, `${JSON.stringify(config, null, 2)}
|
|
3997
4089
|
`);
|
|
3998
4090
|
return { success: true };
|
|
3999
4091
|
} catch (err) {
|
|
@@ -4027,9 +4119,9 @@ function migrateConfig(partial) {
|
|
|
4027
4119
|
function scaffoldConfig() {
|
|
4028
4120
|
try {
|
|
4029
4121
|
const dir = dirname24(COMPACTOR_CONFIG_PATH);
|
|
4030
|
-
if (!existsSync31(dir))
|
|
4122
|
+
if (!existsSync31(dir)) mkdirSync21(dir, { recursive: true });
|
|
4031
4123
|
if (!existsSync31(COMPACTOR_CONFIG_PATH)) {
|
|
4032
|
-
|
|
4124
|
+
writeFileSync25(COMPACTOR_CONFIG_PATH, `${JSON.stringify(DEFAULT_COMPACTOR_CONFIG, null, 2)}
|
|
4033
4125
|
`);
|
|
4034
4126
|
}
|
|
4035
4127
|
} catch {
|
|
@@ -4040,7 +4132,7 @@ var init_manager = __esm({
|
|
|
4040
4132
|
"packages/compactor/src/config/manager.ts"() {
|
|
4041
4133
|
"use strict";
|
|
4042
4134
|
init_schema();
|
|
4043
|
-
COMPACTOR_CONFIG_PATH =
|
|
4135
|
+
COMPACTOR_CONFIG_PATH = join34(homedir22(), ".unipi", "config", "compactor", "config.json");
|
|
4044
4136
|
readJson3 = (path28) => {
|
|
4045
4137
|
try {
|
|
4046
4138
|
return JSON.parse(readFileSync29(path28, "utf-8"));
|
|
@@ -5445,7 +5537,6 @@ function buildIntentSection(intentEvents) {
|
|
|
5445
5537
|
function buildResumeSnapshot(events, opts) {
|
|
5446
5538
|
const compactCount = opts?.compactCount ?? 1;
|
|
5447
5539
|
const searchTool = opts?.searchTool ?? "ctx_search";
|
|
5448
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
5449
5540
|
const fileEvents = [];
|
|
5450
5541
|
const taskEvents = [];
|
|
5451
5542
|
const ruleEvents = [];
|
|
@@ -5521,7 +5612,7 @@ function buildResumeSnapshot(events, opts) {
|
|
|
5521
5612
|
if (skills) sections.push(skills);
|
|
5522
5613
|
const intent = buildIntentSection(intentEvents);
|
|
5523
5614
|
if (intent) sections.push(intent);
|
|
5524
|
-
const header = `<session_resume events="${events.length}" compact_count="${compactCount}"
|
|
5615
|
+
const header = `<session_resume events="${events.length}" compact_count="${compactCount}">`;
|
|
5525
5616
|
const footer = `</session_resume>`;
|
|
5526
5617
|
const body = sections.join("\n\n");
|
|
5527
5618
|
if (body) {
|
|
@@ -6316,7 +6407,7 @@ __export(policy_exports, {
|
|
|
6316
6407
|
readsOrCreatesPolicy: () => readsOrCreatesPolicy
|
|
6317
6408
|
});
|
|
6318
6409
|
import { existsSync as existsSync36, readFileSync as readFileSync31 } from "node:fs";
|
|
6319
|
-
import { join as
|
|
6410
|
+
import { join as join37 } from "node:path";
|
|
6320
6411
|
function parseBashPattern(pattern) {
|
|
6321
6412
|
const match = pattern.match(/^Bash\((.+)\)$/);
|
|
6322
6413
|
return match ? match[1] : null;
|
|
@@ -6371,7 +6462,7 @@ function fileGlobToRegex(glob, caseInsensitive = false) {
|
|
|
6371
6462
|
return new RegExp(`^${regexStr}$`, caseInsensitive ? "i" : "");
|
|
6372
6463
|
}
|
|
6373
6464
|
function readsOrCreatesPolicy(cwd) {
|
|
6374
|
-
const settingsPath =
|
|
6465
|
+
const settingsPath = join37(cwd, ".pi", "settings.json");
|
|
6375
6466
|
if (!existsSync36(settingsPath)) return { allow: [], deny: [], ask: [] };
|
|
6376
6467
|
try {
|
|
6377
6468
|
const settings = JSON.parse(readFileSync31(settingsPath, "utf-8"));
|
|
@@ -6400,10 +6491,10 @@ __export(evaluator_exports, {
|
|
|
6400
6491
|
splitChainedCommands: () => splitChainedCommands
|
|
6401
6492
|
});
|
|
6402
6493
|
import { realpathSync as realpathSync2, existsSync as existsSync37, readFileSync as readFileSync32 } from "node:fs";
|
|
6403
|
-
import { resolve as resolve12, join as
|
|
6404
|
-
import { homedir as
|
|
6494
|
+
import { resolve as resolve12, join as join38 } from "node:path";
|
|
6495
|
+
import { homedir as homedir24 } from "node:os";
|
|
6405
6496
|
function loadProjectPermissions(cwd, basePolicy) {
|
|
6406
|
-
const settingsPath =
|
|
6497
|
+
const settingsPath = join38(cwd, ".pi", "settings.json");
|
|
6407
6498
|
if (!existsSync37(settingsPath)) return basePolicy;
|
|
6408
6499
|
try {
|
|
6409
6500
|
const raw = readFileSync32(settingsPath, "utf-8");
|
|
@@ -6475,7 +6566,7 @@ function evaluateFilePath(filePath, policy, cwd = process.cwd()) {
|
|
|
6475
6566
|
const resolved = resolve12(cwd, filePath);
|
|
6476
6567
|
try {
|
|
6477
6568
|
const real = realpathSync2(resolved);
|
|
6478
|
-
const home =
|
|
6569
|
+
const home = homedir24();
|
|
6479
6570
|
if (!real.startsWith(cwd) && !real.startsWith(home)) {
|
|
6480
6571
|
return "deny";
|
|
6481
6572
|
}
|
|
@@ -6649,13 +6740,13 @@ import {
|
|
|
6649
6740
|
// packages/workflow/commands.ts
|
|
6650
6741
|
init_core();
|
|
6651
6742
|
import { readFileSync as readFileSync3, readdirSync, existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
6652
|
-
import { join as
|
|
6743
|
+
import { join as join4, basename } from "path";
|
|
6653
6744
|
function suggestSpecFiles(prefix) {
|
|
6654
|
-
const specsDir =
|
|
6745
|
+
const specsDir = join4(process.cwd(), ".unipi", "docs", "specs");
|
|
6655
6746
|
if (!existsSync3(specsDir)) return [];
|
|
6656
6747
|
try {
|
|
6657
6748
|
const search2 = prefix?.trim().split(/\s+/).pop() ?? "";
|
|
6658
|
-
const files = readdirSync(specsDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(
|
|
6749
|
+
const files = readdirSync(specsDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(join4(specsDir, f)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
6659
6750
|
return files.filter((f) => !search2 || f.name.includes(search2)).map((f) => ({
|
|
6660
6751
|
value: `specs:${f.name}`,
|
|
6661
6752
|
label: basename(f.name, ".md"),
|
|
@@ -6666,11 +6757,11 @@ function suggestSpecFiles(prefix) {
|
|
|
6666
6757
|
}
|
|
6667
6758
|
}
|
|
6668
6759
|
function suggestPlanFiles(prefix) {
|
|
6669
|
-
const plansDir =
|
|
6760
|
+
const plansDir = join4(process.cwd(), ".unipi", "docs", "plans");
|
|
6670
6761
|
if (!existsSync3(plansDir)) return [];
|
|
6671
6762
|
try {
|
|
6672
6763
|
const search2 = prefix?.trim().split(/\s+/).pop() ?? "";
|
|
6673
|
-
const files = readdirSync(plansDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(
|
|
6764
|
+
const files = readdirSync(plansDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(join4(plansDir, f)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
6674
6765
|
return files.filter((f) => !search2 || f.name.includes(search2)).map((f) => ({
|
|
6675
6766
|
value: `plan:${f.name}`,
|
|
6676
6767
|
label: basename(f.name, ".md"),
|
|
@@ -6681,11 +6772,11 @@ function suggestPlanFiles(prefix) {
|
|
|
6681
6772
|
}
|
|
6682
6773
|
}
|
|
6683
6774
|
function suggestDebugFiles(prefix) {
|
|
6684
|
-
const debugDir =
|
|
6775
|
+
const debugDir = join4(process.cwd(), ".unipi", "docs", "debug");
|
|
6685
6776
|
if (!existsSync3(debugDir)) return [];
|
|
6686
6777
|
try {
|
|
6687
6778
|
const search2 = prefix?.trim().split(/\s+/).pop() ?? "";
|
|
6688
|
-
const files = readdirSync(debugDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(
|
|
6779
|
+
const files = readdirSync(debugDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(join4(debugDir, f)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
6689
6780
|
return files.filter((f) => !search2 || f.name.includes(search2)).map((f) => ({
|
|
6690
6781
|
value: `debug:${f.name}`,
|
|
6691
6782
|
label: basename(f.name, ".md"),
|
|
@@ -6696,11 +6787,11 @@ function suggestDebugFiles(prefix) {
|
|
|
6696
6787
|
}
|
|
6697
6788
|
}
|
|
6698
6789
|
function suggestChoreFiles(prefix) {
|
|
6699
|
-
const choreDir =
|
|
6790
|
+
const choreDir = join4(process.cwd(), ".unipi", "docs", "chore");
|
|
6700
6791
|
if (!existsSync3(choreDir)) return [];
|
|
6701
6792
|
try {
|
|
6702
6793
|
const search2 = prefix?.trim().split(/\s+/).pop() ?? "";
|
|
6703
|
-
const files = readdirSync(choreDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(
|
|
6794
|
+
const files = readdirSync(choreDir).filter((f) => f.endsWith(".md")).map((f) => ({ name: f, time: statSync2(join4(choreDir, f)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
6704
6795
|
return files.filter((f) => !search2 || f.name.includes(search2)).map((f) => ({
|
|
6705
6796
|
value: `chore:${f.name}`,
|
|
6706
6797
|
label: basename(f.name, ".md"),
|
|
@@ -6716,7 +6807,7 @@ function suggestWorktrees() {
|
|
|
6716
6807
|
if (worktreeSuggestionsCache?.cwd === cwd) {
|
|
6717
6808
|
return worktreeSuggestionsCache.items;
|
|
6718
6809
|
}
|
|
6719
|
-
const worktreesDir =
|
|
6810
|
+
const worktreesDir = join4(cwd, ".unipi", "worktrees");
|
|
6720
6811
|
if (!existsSync3(worktreesDir)) {
|
|
6721
6812
|
worktreeSuggestionsCache = { cwd, items: [] };
|
|
6722
6813
|
return worktreeSuggestionsCache.items;
|
|
@@ -6725,7 +6816,7 @@ function suggestWorktrees() {
|
|
|
6725
6816
|
let findWorktrees2 = function(dir, relativePath) {
|
|
6726
6817
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
6727
6818
|
for (const entry of entries) {
|
|
6728
|
-
const fullPath =
|
|
6819
|
+
const fullPath = join4(dir, entry.name);
|
|
6729
6820
|
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
6730
6821
|
if (entry.name === ".git" && entry.isFile()) {
|
|
6731
6822
|
try {
|
|
@@ -6924,7 +7015,7 @@ function registerWorkflowCommands(pi, options) {
|
|
|
6924
7015
|
}
|
|
6925
7016
|
let skillContent = "";
|
|
6926
7017
|
try {
|
|
6927
|
-
const skillPath =
|
|
7018
|
+
const skillPath = join4(
|
|
6928
7019
|
new URL("./skills", import.meta.url).pathname,
|
|
6929
7020
|
cmd.skillName,
|
|
6930
7021
|
"SKILL.md"
|
|
@@ -7513,7 +7604,7 @@ Use /unipi:ralph-resume <name> to continue`, "info");
|
|
|
7513
7604
|
// packages/ralph/tools.ts
|
|
7514
7605
|
init_core();
|
|
7515
7606
|
import { Type } from "typebox";
|
|
7516
|
-
function registerRalphTools(pi,
|
|
7607
|
+
function registerRalphTools(pi, getManager2) {
|
|
7517
7608
|
pi.registerTool({
|
|
7518
7609
|
name: RALPH_TOOLS.START,
|
|
7519
7610
|
label: "Start Ralph Loop",
|
|
@@ -7542,6 +7633,7 @@ function registerRalphTools(pi, manager2) {
|
|
|
7542
7633
|
)
|
|
7543
7634
|
}),
|
|
7544
7635
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
7636
|
+
const manager2 = getManager2(ctx);
|
|
7545
7637
|
const taskFile = `.unipi/ralph/${params.name.replace(/[^a-zA-Z0-9_-]/g, "_")}.md`;
|
|
7546
7638
|
if (manager2.loadState(params.name)?.status === "active") {
|
|
7547
7639
|
return {
|
|
@@ -7582,6 +7674,7 @@ function registerRalphTools(pi, manager2) {
|
|
|
7582
7674
|
],
|
|
7583
7675
|
parameters: Type.Object({}),
|
|
7584
7676
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
7677
|
+
const manager2 = getManager2(ctx);
|
|
7585
7678
|
if (!manager2.getCurrentLoop()) {
|
|
7586
7679
|
return {
|
|
7587
7680
|
content: [{ type: "text", text: "No active Ralph loop." }],
|
|
@@ -7639,6 +7732,38 @@ function registerRalphTools(pi, manager2) {
|
|
|
7639
7732
|
});
|
|
7640
7733
|
}
|
|
7641
7734
|
|
|
7735
|
+
// packages/ralph/reminder.ts
|
|
7736
|
+
init_core();
|
|
7737
|
+
var RALPH_REMINDER_TYPE = "unipi-ralph-loop-reminder";
|
|
7738
|
+
function buildRalphLoopReminder(state) {
|
|
7739
|
+
const iterStr = `${state.iteration}${state.maxIterations > 0 ? `/${state.maxIterations}` : ""}`;
|
|
7740
|
+
let instructions = "This snapshot supersedes all earlier Ralph loop reminders.\n";
|
|
7741
|
+
instructions += `You are in a Ralph loop working on: ${state.taskFile}
|
|
7742
|
+
`;
|
|
7743
|
+
if (state.itemsPerIteration > 0) {
|
|
7744
|
+
instructions += `- Work on ~${state.itemsPerIteration} items this iteration
|
|
7745
|
+
`;
|
|
7746
|
+
}
|
|
7747
|
+
instructions += "- Update the task file as you progress\n";
|
|
7748
|
+
instructions += `- When FULLY COMPLETE: ${RALPH_COMPLETE_MARKER}
|
|
7749
|
+
`;
|
|
7750
|
+
instructions += "- Otherwise, call ralph_done tool to proceed to next iteration";
|
|
7751
|
+
return `[RALPH LOOP - ${state.name} - Iteration ${iterStr}]
|
|
7752
|
+
|
|
7753
|
+
${instructions}`;
|
|
7754
|
+
}
|
|
7755
|
+
function latestRalphReminder(ctx) {
|
|
7756
|
+
const branch = ctx.sessionManager.getBranch();
|
|
7757
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
7758
|
+
const entry = branch[index];
|
|
7759
|
+
if (entry.type === "custom_message" && entry.customType === RALPH_REMINDER_TYPE) {
|
|
7760
|
+
return typeof entry.content === "string" ? entry.content : null;
|
|
7761
|
+
}
|
|
7762
|
+
if (entry.type === "compaction") break;
|
|
7763
|
+
}
|
|
7764
|
+
return null;
|
|
7765
|
+
}
|
|
7766
|
+
|
|
7642
7767
|
// packages/ralph/index.ts
|
|
7643
7768
|
function getInfoRegistry() {
|
|
7644
7769
|
return globalThis.__unipi_info_registry;
|
|
@@ -7655,6 +7780,7 @@ function getManager(ctx, pi) {
|
|
|
7655
7780
|
return manager;
|
|
7656
7781
|
}
|
|
7657
7782
|
function ralph_default(pi) {
|
|
7783
|
+
registerRalphTools(pi, (ctx) => getManager(ctx, pi));
|
|
7658
7784
|
registerCommands(pi);
|
|
7659
7785
|
pi.on("session_start", async (_event, ctx) => {
|
|
7660
7786
|
manager = null;
|
|
@@ -7735,24 +7861,12 @@ function ralph_default(pi) {
|
|
|
7735
7861
|
if (!currentLoop) return;
|
|
7736
7862
|
const state = mgr.loadState(currentLoop);
|
|
7737
7863
|
if (!state || state.status !== "active") return;
|
|
7738
|
-
const
|
|
7739
|
-
|
|
7740
|
-
`;
|
|
7741
|
-
if (state.itemsPerIteration > 0) {
|
|
7742
|
-
instructions += `- Work on ~${state.itemsPerIteration} items this iteration
|
|
7743
|
-
`;
|
|
7744
|
-
}
|
|
7745
|
-
instructions += `- Update the task file as you progress
|
|
7746
|
-
`;
|
|
7747
|
-
instructions += `- When FULLY COMPLETE: ${RALPH_COMPLETE_MARKER}
|
|
7748
|
-
`;
|
|
7749
|
-
instructions += `- Otherwise, call ralph_done tool to proceed to next iteration`;
|
|
7864
|
+
const content = buildRalphLoopReminder(state);
|
|
7865
|
+
if (latestRalphReminder(ctx) === content) return;
|
|
7750
7866
|
return {
|
|
7751
7867
|
message: {
|
|
7752
|
-
customType:
|
|
7753
|
-
content
|
|
7754
|
-
|
|
7755
|
-
${instructions}`,
|
|
7868
|
+
customType: RALPH_REMINDER_TYPE,
|
|
7869
|
+
content,
|
|
7756
7870
|
display: false
|
|
7757
7871
|
}
|
|
7758
7872
|
};
|
|
@@ -7766,10 +7880,6 @@ ${instructions}`,
|
|
|
7766
7880
|
}
|
|
7767
7881
|
manager = null;
|
|
7768
7882
|
});
|
|
7769
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
7770
|
-
const mgr = getManager(ctx, pi);
|
|
7771
|
-
registerRalphTools(pi, mgr);
|
|
7772
|
-
});
|
|
7773
7883
|
}
|
|
7774
7884
|
function registerCommands(pi) {
|
|
7775
7885
|
const HELP = `Ralph Loop \u2014 Long-running development loops
|
|
@@ -8779,6 +8889,36 @@ function getInfoRegistry2() {
|
|
|
8779
8889
|
return globalThis.__unipi_info_registry;
|
|
8780
8890
|
}
|
|
8781
8891
|
var VERSION3 = getPackageVersion(dirname7(fileURLToPath4(import.meta.url)));
|
|
8892
|
+
function buildMemoryRecallReminder(input) {
|
|
8893
|
+
const lines = [
|
|
8894
|
+
"## \u{1F9E0} Memory System Active",
|
|
8895
|
+
"",
|
|
8896
|
+
`You have ${input.memories.length} memories stored for project "${input.projectName}".`
|
|
8897
|
+
];
|
|
8898
|
+
if (input.canSearch && input.memories.length > 0) {
|
|
8899
|
+
const titleList = input.memories.slice(0, 20).map((memory) => `- ${memory.title}`).join("\n");
|
|
8900
|
+
const extra = input.memories.length > 20 ? `
|
|
8901
|
+
... and ${input.memories.length - 20} more` : "";
|
|
8902
|
+
lines.push(
|
|
8903
|
+
"**BEFORE starting work**, call `memory_search` with relevant keywords to check for existing context.",
|
|
8904
|
+
"",
|
|
8905
|
+
"Available memories:",
|
|
8906
|
+
titleList + extra
|
|
8907
|
+
);
|
|
8908
|
+
}
|
|
8909
|
+
if (input.canStore) {
|
|
8910
|
+
lines.push(
|
|
8911
|
+
"",
|
|
8912
|
+
"**AFTER completing the task**, if you learned something non-obvious,",
|
|
8913
|
+
"call `memory_store` to save it for future sessions."
|
|
8914
|
+
);
|
|
8915
|
+
}
|
|
8916
|
+
lines.push(
|
|
8917
|
+
"",
|
|
8918
|
+
"Guardrails: read max 10 memory results per search. Update existing memories instead of creating duplicates."
|
|
8919
|
+
);
|
|
8920
|
+
return lines.join("\n");
|
|
8921
|
+
}
|
|
8782
8922
|
var projectStorage = null;
|
|
8783
8923
|
var orphanSyncPending = false;
|
|
8784
8924
|
function ensureOrphanSync(storage) {
|
|
@@ -8932,41 +9072,17 @@ function memory_default(pi) {
|
|
|
8932
9072
|
recallDone = true;
|
|
8933
9073
|
return;
|
|
8934
9074
|
}
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
"",
|
|
8938
|
-
`You have ${projectMemories.length} memories stored for project "${projectName}".`
|
|
8939
|
-
];
|
|
8940
|
-
if (canSearch && projectMemories.length > 0) {
|
|
8941
|
-
const titleList = projectMemories.slice(0, 20).map((m) => `- ${m.title}`).join("\n");
|
|
8942
|
-
const extra = projectMemories.length > 20 ? `
|
|
8943
|
-
... and ${projectMemories.length - 20} more` : "";
|
|
8944
|
-
lines.push(
|
|
8945
|
-
"**BEFORE starting work**, call `memory_search` with relevant keywords to check for existing context.",
|
|
8946
|
-
"",
|
|
8947
|
-
"Available memories:",
|
|
8948
|
-
titleList + extra
|
|
8949
|
-
);
|
|
8950
|
-
} else {
|
|
8951
|
-
recallDone = true;
|
|
8952
|
-
}
|
|
8953
|
-
if (canStore) {
|
|
8954
|
-
lines.push(
|
|
8955
|
-
"",
|
|
8956
|
-
"**AFTER completing the task**, if you learned something non-obvious,",
|
|
8957
|
-
"call `memory_store` to save it for future sessions."
|
|
8958
|
-
);
|
|
8959
|
-
} else {
|
|
8960
|
-
storeDone = true;
|
|
8961
|
-
}
|
|
8962
|
-
lines.push(
|
|
8963
|
-
"",
|
|
8964
|
-
"Guardrails: read max 10 memory results per search. Update existing memories instead of creating duplicates."
|
|
8965
|
-
);
|
|
9075
|
+
if (!canSearch || projectMemories.length === 0) recallDone = true;
|
|
9076
|
+
if (!canStore) storeDone = true;
|
|
8966
9077
|
return {
|
|
8967
9078
|
message: {
|
|
8968
9079
|
customType: "unipi-memory-recall-reminder",
|
|
8969
|
-
content:
|
|
9080
|
+
content: buildMemoryRecallReminder({
|
|
9081
|
+
projectName,
|
|
9082
|
+
memories: projectMemories,
|
|
9083
|
+
canSearch,
|
|
9084
|
+
canStore
|
|
9085
|
+
}),
|
|
8970
9086
|
display: false
|
|
8971
9087
|
}
|
|
8972
9088
|
};
|
|
@@ -9004,9 +9120,9 @@ import { dirname as dirname10 } from "node:path";
|
|
|
9004
9120
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
9005
9121
|
|
|
9006
9122
|
// packages/info-screen/config.ts
|
|
9007
|
-
import { readFileSync as readFileSync7, writeFileSync as
|
|
9008
|
-
import { join as
|
|
9009
|
-
import { homedir as
|
|
9123
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync7 } from "node:fs";
|
|
9124
|
+
import { join as join9, dirname as dirname8 } from "node:path";
|
|
9125
|
+
import { homedir as homedir5 } from "node:os";
|
|
9010
9126
|
|
|
9011
9127
|
// packages/info-screen/types.ts
|
|
9012
9128
|
var BOOT_MODES = ["on", "auto-close", "off"];
|
|
@@ -9018,7 +9134,7 @@ var DEFAULT_SETTINGS = {
|
|
|
9018
9134
|
};
|
|
9019
9135
|
|
|
9020
9136
|
// packages/info-screen/config.ts
|
|
9021
|
-
var SETTINGS_PATH =
|
|
9137
|
+
var SETTINGS_PATH = join9(homedir5(), ".pi", "agent", "settings.json");
|
|
9022
9138
|
var SETTINGS_KEY = "unipi";
|
|
9023
9139
|
var cachedSettings = null;
|
|
9024
9140
|
function isRecord(value) {
|
|
@@ -9036,9 +9152,9 @@ function readSettingsFile() {
|
|
|
9036
9152
|
function writeSettingsFile(data) {
|
|
9037
9153
|
const dir = dirname8(SETTINGS_PATH);
|
|
9038
9154
|
if (!existsSync8(dir)) {
|
|
9039
|
-
|
|
9155
|
+
mkdirSync7(dir, { recursive: true });
|
|
9040
9156
|
}
|
|
9041
|
-
|
|
9157
|
+
writeFileSync8(SETTINGS_PATH, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
9042
9158
|
}
|
|
9043
9159
|
function getInfoSettings() {
|
|
9044
9160
|
if (cachedSettings) return cachedSettings;
|
|
@@ -9336,16 +9452,16 @@ if (!globalThis.__unipi_info_registry) {
|
|
|
9336
9452
|
// packages/info-screen/core-groups.ts
|
|
9337
9453
|
init_core();
|
|
9338
9454
|
import { readFileSync as readFileSync9, readdirSync as readdirSync6, existsSync as existsSync10 } from "node:fs";
|
|
9339
|
-
import { join as
|
|
9340
|
-
import { homedir as
|
|
9455
|
+
import { join as join11 } from "node:path";
|
|
9456
|
+
import { homedir as homedir7 } from "node:os";
|
|
9341
9457
|
init_usage_parser();
|
|
9342
9458
|
function discoverExtensions() {
|
|
9343
9459
|
const extensions = [];
|
|
9344
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE ||
|
|
9460
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || homedir7();
|
|
9345
9461
|
const cwd = process.cwd();
|
|
9346
9462
|
const settingsPaths = [
|
|
9347
|
-
|
|
9348
|
-
|
|
9463
|
+
join11(homeDir, ".pi", "agent", "settings.json"),
|
|
9464
|
+
join11(cwd, ".pi", "settings.json")
|
|
9349
9465
|
];
|
|
9350
9466
|
const counted = /* @__PURE__ */ new Set();
|
|
9351
9467
|
for (const settingsPath of settingsPaths) {
|
|
@@ -9390,8 +9506,8 @@ function discoverExtensions() {
|
|
|
9390
9506
|
}
|
|
9391
9507
|
}
|
|
9392
9508
|
const extensionDirs = [
|
|
9393
|
-
|
|
9394
|
-
|
|
9509
|
+
join11(homeDir, ".pi", "agent", "extensions"),
|
|
9510
|
+
join11(cwd, ".pi", "extensions")
|
|
9395
9511
|
];
|
|
9396
9512
|
for (const dir of extensionDirs) {
|
|
9397
9513
|
if (!existsSync10(dir)) continue;
|
|
@@ -9458,11 +9574,11 @@ function getTotalLoadTime() {
|
|
|
9458
9574
|
var extraSkillDirs = [];
|
|
9459
9575
|
function discoverSkills() {
|
|
9460
9576
|
const skills = [];
|
|
9461
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE ||
|
|
9577
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || homedir7();
|
|
9462
9578
|
const cwd = process.cwd();
|
|
9463
9579
|
const skillDirs = [
|
|
9464
|
-
|
|
9465
|
-
|
|
9580
|
+
join11(homeDir, ".pi", "agent", "skills"),
|
|
9581
|
+
join11(cwd, ".pi", "skills"),
|
|
9466
9582
|
// Add extra dirs from extensions
|
|
9467
9583
|
...extraSkillDirs
|
|
9468
9584
|
];
|
|
@@ -9475,13 +9591,13 @@ function discoverSkills() {
|
|
|
9475
9591
|
if (!entry.isDirectory()) continue;
|
|
9476
9592
|
const name = entry.name;
|
|
9477
9593
|
if (counted.has(name)) continue;
|
|
9478
|
-
const skillPath =
|
|
9594
|
+
const skillPath = join11(dir, name, "SKILL.md");
|
|
9479
9595
|
if (existsSync10(skillPath)) {
|
|
9480
9596
|
counted.add(name);
|
|
9481
9597
|
let source = "extension";
|
|
9482
|
-
if (dir.includes(
|
|
9598
|
+
if (dir.includes(join11(homeDir, ".pi"))) {
|
|
9483
9599
|
source = "global";
|
|
9484
|
-
} else if (dir ===
|
|
9600
|
+
} else if (dir === join11(cwd, ".pi", "skills")) {
|
|
9485
9601
|
source = "project";
|
|
9486
9602
|
}
|
|
9487
9603
|
skills.push({ name, source });
|
|
@@ -9532,7 +9648,7 @@ function registerCoreGroups() {
|
|
|
9532
9648
|
},
|
|
9533
9649
|
dataProvider: async () => {
|
|
9534
9650
|
const cwd = process.cwd();
|
|
9535
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE ||
|
|
9651
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || homedir7();
|
|
9536
9652
|
const shortCwd = cwd.startsWith(homeDir) ? `~${cwd.slice(homeDir.length)}` : cwd;
|
|
9537
9653
|
const announced = getAnnouncedModules();
|
|
9538
9654
|
const registeredGroups = infoRegistry.getAllGroups();
|
|
@@ -10725,10 +10841,14 @@ import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
|
10725
10841
|
import { Text } from "@earendil-works/pi-tui";
|
|
10726
10842
|
import { Type as Type3 } from "typebox";
|
|
10727
10843
|
import { existsSync as existsSync13, readdirSync as readdirSync8 } from "node:fs";
|
|
10728
|
-
import { join as
|
|
10729
|
-
import { homedir as
|
|
10844
|
+
import { join as join15 } from "node:path";
|
|
10845
|
+
import { homedir as homedir11 } from "node:os";
|
|
10730
10846
|
|
|
10731
10847
|
// packages/subagents/src/core-compat.ts
|
|
10848
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10849
|
+
import { homedir as homedir8 } from "node:os";
|
|
10850
|
+
import { join as join12 } from "node:path";
|
|
10851
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10732
10852
|
var MODULES2 = { SUBAGENTS: "subagents" };
|
|
10733
10853
|
var UNIPI_EVENTS2 = {
|
|
10734
10854
|
MODULE_READY: "unipi:module:ready",
|
|
@@ -10742,6 +10862,61 @@ function emitEvent2(pi, eventName, payload) {
|
|
|
10742
10862
|
return false;
|
|
10743
10863
|
}
|
|
10744
10864
|
}
|
|
10865
|
+
var MAX_RAW_HELPER_ARTIFACT_BYTES = 16 * 1024 * 1024;
|
|
10866
|
+
function boundHelperOutput(text, maxBytes = 64 * 1024, existingArtifactPath) {
|
|
10867
|
+
const originalBytes = Buffer.byteLength(text, "utf8");
|
|
10868
|
+
if (originalBytes <= maxBytes) return { text, truncated: false, originalBytes };
|
|
10869
|
+
let artifactPath = existingArtifactPath;
|
|
10870
|
+
let artifactWarning;
|
|
10871
|
+
if (!artifactPath && originalBytes <= MAX_RAW_HELPER_ARTIFACT_BYTES) {
|
|
10872
|
+
try {
|
|
10873
|
+
const dir = join12(homedir8(), ".unipi", "tool-results");
|
|
10874
|
+
mkdirSync9(dir, { recursive: true, mode: 448 });
|
|
10875
|
+
let stat = lstatSync2(dir);
|
|
10876
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
10877
|
+
throw new Error(`Refusing unsafe tool-result directory: ${dir}`);
|
|
10878
|
+
}
|
|
10879
|
+
if ((stat.mode & 63) !== 0) {
|
|
10880
|
+
chmodSync2(dir, 448);
|
|
10881
|
+
stat = lstatSync2(dir);
|
|
10882
|
+
if ((stat.mode & 63) !== 0) throw new Error(`Refusing non-private tool-result directory: ${dir}`);
|
|
10883
|
+
}
|
|
10884
|
+
artifactPath = join12(dir, `helper-${randomUUID2()}.txt`);
|
|
10885
|
+
writeFileSync10(artifactPath, text, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
10886
|
+
} catch (error) {
|
|
10887
|
+
artifactWarning = `Full-output artifact unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
10888
|
+
artifactPath = void 0;
|
|
10889
|
+
}
|
|
10890
|
+
} else if (!artifactPath) {
|
|
10891
|
+
artifactWarning = `Full output exceeded the ${MAX_RAW_HELPER_ARTIFACT_BYTES}-byte local artifact safety cap and was not retained.`;
|
|
10892
|
+
}
|
|
10893
|
+
const marker = [
|
|
10894
|
+
"",
|
|
10895
|
+
"--- output bounded by UniPi ---",
|
|
10896
|
+
artifactPath ? `Full output: ${artifactPath}` : artifactWarning,
|
|
10897
|
+
`Original size: ${originalBytes} bytes; model-visible ceiling: ${maxBytes} bytes.`,
|
|
10898
|
+
...artifactPath ? ["Use the read tool with offset/limit to inspect only the needed region."] : []
|
|
10899
|
+
].join("\n");
|
|
10900
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
10901
|
+
const contentBudget = Math.max(1, maxBytes - markerBytes - 80);
|
|
10902
|
+
const bytes = Buffer.from(text, "utf8");
|
|
10903
|
+
const headBytes = Math.ceil(contentBudget * 0.75);
|
|
10904
|
+
const tailBytes = Math.max(0, contentBudget - headBytes);
|
|
10905
|
+
const head = bytes.subarray(0, headBytes).toString("utf8").replace(/\uFFFD+$/u, "");
|
|
10906
|
+
const tail2 = bytes.subarray(originalBytes - tailBytes).toString("utf8").replace(/^\uFFFD+/u, "");
|
|
10907
|
+
let bounded = `${head}
|
|
10908
|
+
\u2026 ${Math.max(0, originalBytes - contentBudget)} bytes omitted \u2026
|
|
10909
|
+
${tail2}${marker}`;
|
|
10910
|
+
if (Buffer.byteLength(bounded, "utf8") > maxBytes) {
|
|
10911
|
+
bounded = Buffer.from(bounded, "utf8").subarray(0, maxBytes).toString("utf8").replace(/\uFFFD+$/u, "");
|
|
10912
|
+
}
|
|
10913
|
+
return {
|
|
10914
|
+
text: bounded,
|
|
10915
|
+
truncated: true,
|
|
10916
|
+
originalBytes,
|
|
10917
|
+
artifactPath
|
|
10918
|
+
};
|
|
10919
|
+
}
|
|
10745
10920
|
async function withHerdrBlocked2(pi, label, fn) {
|
|
10746
10921
|
emitEvent2(pi, "herdr:blocked", { active: true, label });
|
|
10747
10922
|
try {
|
|
@@ -10752,7 +10927,7 @@ async function withHerdrBlocked2(pi, label, fn) {
|
|
|
10752
10927
|
}
|
|
10753
10928
|
|
|
10754
10929
|
// packages/subagents/src/agent-manager.ts
|
|
10755
|
-
import { randomUUID } from "node:crypto";
|
|
10930
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
10756
10931
|
|
|
10757
10932
|
// packages/subagents/src/agent-runner.ts
|
|
10758
10933
|
import {
|
|
@@ -10805,7 +10980,7 @@ var BUILTIN_CONFIGS = {
|
|
|
10805
10980
|
};
|
|
10806
10981
|
|
|
10807
10982
|
// packages/subagents/src/agent-runner.ts
|
|
10808
|
-
var EXCLUDED_TOOL_NAMES = ["Agent", "get_result"];
|
|
10983
|
+
var EXCLUDED_TOOL_NAMES = ["spawn_helper", "get_helper_result", "Agent", "get_result"];
|
|
10809
10984
|
var BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
10810
10985
|
var defaultMaxTurns;
|
|
10811
10986
|
var graceTurns = 5;
|
|
@@ -10964,9 +11139,12 @@ init_model_resolver();
|
|
|
10964
11139
|
|
|
10965
11140
|
// packages/subagents/src/custom-agents.ts
|
|
10966
11141
|
import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync10, renameSync as renameSync4 } from "node:fs";
|
|
10967
|
-
import { join as
|
|
10968
|
-
import { homedir as
|
|
11142
|
+
import { join as join13 } from "node:path";
|
|
11143
|
+
import { homedir as homedir9 } from "node:os";
|
|
10969
11144
|
import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
11145
|
+
function compareCodeUnits(a, b) {
|
|
11146
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
11147
|
+
}
|
|
10970
11148
|
function backupCorrupted(filePath) {
|
|
10971
11149
|
const backupPath = filePath + ".bak";
|
|
10972
11150
|
try {
|
|
@@ -10975,10 +11153,10 @@ function backupCorrupted(filePath) {
|
|
|
10975
11153
|
}
|
|
10976
11154
|
}
|
|
10977
11155
|
function getProjectAgentsDir(cwd) {
|
|
10978
|
-
return
|
|
11156
|
+
return join13(cwd, ".unipi", "config", "agents");
|
|
10979
11157
|
}
|
|
10980
11158
|
function getGlobalAgentsDir() {
|
|
10981
|
-
return
|
|
11159
|
+
return join13(homedir9(), ".unipi", "config", "agents");
|
|
10982
11160
|
}
|
|
10983
11161
|
var BUILTIN_TOOL_NAMES2 = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
10984
11162
|
function loadAgentFromFile(filePath, source) {
|
|
@@ -11019,9 +11197,9 @@ function loadCustomAgents(cwd) {
|
|
|
11019
11197
|
const agents = /* @__PURE__ */ new Map();
|
|
11020
11198
|
const globalDir = getGlobalAgentsDir();
|
|
11021
11199
|
if (existsSync11(globalDir)) {
|
|
11022
|
-
const files = readdirSync7(globalDir).filter((f) => f.endsWith(".md"));
|
|
11200
|
+
const files = readdirSync7(globalDir).filter((f) => f.endsWith(".md")).sort(compareCodeUnits);
|
|
11023
11201
|
for (const file of files) {
|
|
11024
|
-
const agent = loadAgentFromFile(
|
|
11202
|
+
const agent = loadAgentFromFile(join13(globalDir, file), "global");
|
|
11025
11203
|
if (agent) {
|
|
11026
11204
|
agents.set(agent.name, agent);
|
|
11027
11205
|
}
|
|
@@ -11029,9 +11207,9 @@ function loadCustomAgents(cwd) {
|
|
|
11029
11207
|
}
|
|
11030
11208
|
const projectDir = getProjectAgentsDir(cwd);
|
|
11031
11209
|
if (existsSync11(projectDir)) {
|
|
11032
|
-
const files = readdirSync7(projectDir).filter((f) => f.endsWith(".md"));
|
|
11210
|
+
const files = readdirSync7(projectDir).filter((f) => f.endsWith(".md")).sort(compareCodeUnits);
|
|
11033
11211
|
for (const file of files) {
|
|
11034
|
-
const agent = loadAgentFromFile(
|
|
11212
|
+
const agent = loadAgentFromFile(join13(projectDir, file), "project");
|
|
11035
11213
|
if (agent) {
|
|
11036
11214
|
agents.set(agent.name, agent);
|
|
11037
11215
|
}
|
|
@@ -11124,6 +11302,9 @@ var FileLock = class {
|
|
|
11124
11302
|
};
|
|
11125
11303
|
|
|
11126
11304
|
// packages/subagents/src/agent-manager.ts
|
|
11305
|
+
function compareCodeUnits2(a, b) {
|
|
11306
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
11307
|
+
}
|
|
11127
11308
|
var DEFAULT_MAX_CONCURRENT = 4;
|
|
11128
11309
|
var AgentManager = class {
|
|
11129
11310
|
agents = /* @__PURE__ */ new Map();
|
|
@@ -11157,7 +11338,7 @@ var AgentManager = class {
|
|
|
11157
11338
|
...Object.keys(BUILTIN_CONFIGS).filter((type) => type !== "name-gen"),
|
|
11158
11339
|
...this.customAgents.keys(),
|
|
11159
11340
|
...Object.keys(this.typeSettings)
|
|
11160
|
-
])];
|
|
11341
|
+
])].sort(compareCodeUnits2);
|
|
11161
11342
|
}
|
|
11162
11343
|
/** A type is enabled only when both JSON config and agent frontmatter allow it. */
|
|
11163
11344
|
isTypeEnabled(type) {
|
|
@@ -11181,7 +11362,7 @@ var AgentManager = class {
|
|
|
11181
11362
|
*/
|
|
11182
11363
|
spawn(pi, ctx, type, prompt, options) {
|
|
11183
11364
|
this.assertTypeEnabled(type);
|
|
11184
|
-
const id =
|
|
11365
|
+
const id = randomUUID3().slice(0, 17);
|
|
11185
11366
|
const abortController = new AbortController();
|
|
11186
11367
|
const record = {
|
|
11187
11368
|
id,
|
|
@@ -11384,9 +11565,9 @@ var AgentManager = class {
|
|
|
11384
11565
|
};
|
|
11385
11566
|
|
|
11386
11567
|
// packages/subagents/src/config.ts
|
|
11387
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
11388
|
-
import { join as
|
|
11389
|
-
import { homedir as
|
|
11568
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync10, readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync11 } from "node:fs";
|
|
11569
|
+
import { join as join14 } from "node:path";
|
|
11570
|
+
import { homedir as homedir10 } from "node:os";
|
|
11390
11571
|
var DEFAULT_CONFIG2 = {
|
|
11391
11572
|
maxConcurrent: 4,
|
|
11392
11573
|
enabled: true,
|
|
@@ -11396,19 +11577,19 @@ var DEFAULT_CONFIG2 = {
|
|
|
11396
11577
|
}
|
|
11397
11578
|
};
|
|
11398
11579
|
function getGlobalConfigPath() {
|
|
11399
|
-
return
|
|
11580
|
+
return join14(homedir10(), ".unipi", "config", "subagents.json");
|
|
11400
11581
|
}
|
|
11401
11582
|
function getWorkspaceConfigPath(cwd) {
|
|
11402
|
-
return
|
|
11583
|
+
return join14(cwd, ".unipi", "config", "subagents.json");
|
|
11403
11584
|
}
|
|
11404
11585
|
function ensureDirExists(dirPath) {
|
|
11405
11586
|
if (!existsSync12(dirPath)) {
|
|
11406
|
-
|
|
11587
|
+
mkdirSync10(dirPath, { recursive: true });
|
|
11407
11588
|
}
|
|
11408
11589
|
}
|
|
11409
11590
|
function writeConfigAtomic(filePath, config) {
|
|
11410
11591
|
const tmpPath = filePath + ".tmp";
|
|
11411
|
-
|
|
11592
|
+
writeFileSync11(tmpPath, JSON.stringify(config, null, 2), "utf-8");
|
|
11412
11593
|
renameSync5(tmpPath, filePath);
|
|
11413
11594
|
}
|
|
11414
11595
|
function loadConfigFromPath(filePath) {
|
|
@@ -11433,17 +11614,17 @@ function repairCorrupted(filePath) {
|
|
|
11433
11614
|
}
|
|
11434
11615
|
function initConfig(cwd) {
|
|
11435
11616
|
const globalPath = getGlobalConfigPath();
|
|
11436
|
-
const globalDir =
|
|
11437
|
-
const globalAgentsDir =
|
|
11617
|
+
const globalDir = join14(homedir10(), ".unipi", "config");
|
|
11618
|
+
const globalAgentsDir = join14(homedir10(), ".unipi", "config", "agents");
|
|
11438
11619
|
ensureDirExists(globalDir);
|
|
11439
11620
|
ensureDirExists(globalAgentsDir);
|
|
11440
11621
|
let globalConfig = loadConfigFromPath(globalPath);
|
|
11441
11622
|
if (globalConfig === null) {
|
|
11442
11623
|
globalConfig = repairCorrupted(globalPath);
|
|
11443
11624
|
}
|
|
11444
|
-
const workspaceDir =
|
|
11445
|
-
const workspaceAgentsDir =
|
|
11446
|
-
if (cwd && !cwd.startsWith(
|
|
11625
|
+
const workspaceDir = join14(cwd, ".unipi", "config");
|
|
11626
|
+
const workspaceAgentsDir = join14(cwd, ".unipi", "config", "agents");
|
|
11627
|
+
if (cwd && !cwd.startsWith(homedir10())) {
|
|
11447
11628
|
ensureDirExists(workspaceDir);
|
|
11448
11629
|
ensureDirExists(workspaceAgentsDir);
|
|
11449
11630
|
}
|
|
@@ -12107,10 +12288,10 @@ function getStatusLabel(status2, error) {
|
|
|
12107
12288
|
function src_default(pi) {
|
|
12108
12289
|
const config = initConfig(process.cwd());
|
|
12109
12290
|
if (!config.enabled) return;
|
|
12110
|
-
const homeDir =
|
|
12291
|
+
const homeDir = homedir11();
|
|
12111
12292
|
const cwd = process.cwd();
|
|
12112
|
-
const globalAgentsDir =
|
|
12113
|
-
const workspaceAgentsDir =
|
|
12293
|
+
const globalAgentsDir = join15(homeDir, ".unipi", "config", "agents");
|
|
12294
|
+
const workspaceAgentsDir = join15(cwd, ".unipi", "config", "agents");
|
|
12114
12295
|
const agentActivity = /* @__PURE__ */ new Map();
|
|
12115
12296
|
let sessionEnded = false;
|
|
12116
12297
|
const manager2 = new AgentManager(
|
|
@@ -12590,17 +12771,22 @@ Use get_result to retrieve full results.`,
|
|
|
12590
12771
|
error: record.error
|
|
12591
12772
|
});
|
|
12592
12773
|
}
|
|
12774
|
+
const output = boundHelperOutput(record.result?.trim() || "No output.");
|
|
12775
|
+
record.resultArtifactPath = output.artifactPath;
|
|
12593
12776
|
return textResult(
|
|
12594
12777
|
`Agent completed in ${(durationMs / 1e3).toFixed(1)}s (${record.toolUses} tool uses${tokenText ? `, ${tokenText} tokens` : ""}).
|
|
12595
12778
|
|
|
12596
|
-
` +
|
|
12779
|
+
` + output.text,
|
|
12597
12780
|
{
|
|
12598
12781
|
status: "completed",
|
|
12599
12782
|
toolUses: record.toolUses,
|
|
12600
12783
|
tokens: tokenText,
|
|
12601
12784
|
durationMs,
|
|
12602
12785
|
turnCount: fgState.turnCount,
|
|
12603
|
-
maxTurns: fgState.maxTurns
|
|
12786
|
+
maxTurns: fgState.maxTurns,
|
|
12787
|
+
truncated: output.truncated,
|
|
12788
|
+
originalBytes: output.originalBytes,
|
|
12789
|
+
artifactPath: output.artifactPath
|
|
12604
12790
|
}
|
|
12605
12791
|
);
|
|
12606
12792
|
}
|
|
@@ -12676,7 +12862,13 @@ Description: ${record.description}
|
|
|
12676
12862
|
} else if (record.status === "error") {
|
|
12677
12863
|
output += `Error: ${record.error}`;
|
|
12678
12864
|
} else {
|
|
12679
|
-
|
|
12865
|
+
const bounded = boundHelperOutput(
|
|
12866
|
+
record.result?.trim() || "No output.",
|
|
12867
|
+
64 * 1024,
|
|
12868
|
+
record.resultArtifactPath
|
|
12869
|
+
);
|
|
12870
|
+
record.resultArtifactPath = bounded.artifactPath;
|
|
12871
|
+
output += bounded.text;
|
|
12680
12872
|
}
|
|
12681
12873
|
if (record.status !== "running" && record.status !== "queued") {
|
|
12682
12874
|
record.resultConsumed = true;
|
|
@@ -16929,8 +17121,8 @@ init_core();
|
|
|
16929
17121
|
|
|
16930
17122
|
// packages/utility/src/lifecycle/cleanup.ts
|
|
16931
17123
|
import { existsSync as existsSync16, statSync as statSync8, readdirSync as readdirSync10, unlinkSync as unlinkSync5, rmdirSync } from "node:fs";
|
|
16932
|
-
import { join as
|
|
16933
|
-
import { homedir as
|
|
17124
|
+
import { join as join18 } from "node:path";
|
|
17125
|
+
import { homedir as homedir14, tmpdir } from "node:os";
|
|
16934
17126
|
var DEFAULTS = {
|
|
16935
17127
|
dbMaxAgeDays: 14,
|
|
16936
17128
|
tempMaxAgeDays: 7,
|
|
@@ -16939,7 +17131,7 @@ var DEFAULTS = {
|
|
|
16939
17131
|
};
|
|
16940
17132
|
function expandHome(path28) {
|
|
16941
17133
|
if (path28.startsWith("~/")) {
|
|
16942
|
-
return
|
|
17134
|
+
return join18(homedir14(), path28.slice(2));
|
|
16943
17135
|
}
|
|
16944
17136
|
return path28;
|
|
16945
17137
|
}
|
|
@@ -16983,7 +17175,7 @@ function cleanDbs(options) {
|
|
|
16983
17175
|
return;
|
|
16984
17176
|
}
|
|
16985
17177
|
for (const entry of entries) {
|
|
16986
|
-
const fullPath =
|
|
17178
|
+
const fullPath = join18(dir, entry);
|
|
16987
17179
|
try {
|
|
16988
17180
|
const stats = statSync8(fullPath);
|
|
16989
17181
|
if (stats.isDirectory()) {
|
|
@@ -17019,6 +17211,36 @@ function cleanDbs(options) {
|
|
|
17019
17211
|
scanDir(unipiDir);
|
|
17020
17212
|
return result;
|
|
17021
17213
|
}
|
|
17214
|
+
function cleanToolResults(options) {
|
|
17215
|
+
const result = {
|
|
17216
|
+
category: "tool-results",
|
|
17217
|
+
removed: 0,
|
|
17218
|
+
bytesFreed: 0,
|
|
17219
|
+
paths: []
|
|
17220
|
+
};
|
|
17221
|
+
const dir = expandHome("~/.unipi/tool-results");
|
|
17222
|
+
if (!existsSync16(dir)) return result;
|
|
17223
|
+
let entries;
|
|
17224
|
+
try {
|
|
17225
|
+
entries = readdirSync10(dir);
|
|
17226
|
+
} catch {
|
|
17227
|
+
return result;
|
|
17228
|
+
}
|
|
17229
|
+
for (const entry of entries) {
|
|
17230
|
+
if (!/^(?:tool-result|mcp-[a-zA-Z0-9_-]+|helper)-[a-f0-9-]+\.txt$/.test(entry)) continue;
|
|
17231
|
+
const path28 = join18(dir, entry);
|
|
17232
|
+
try {
|
|
17233
|
+
const stats = statSync8(path28);
|
|
17234
|
+
if (!stats.isFile() || !isStale(path28, options.tempMaxAgeDays)) continue;
|
|
17235
|
+
result.paths.push(path28);
|
|
17236
|
+
result.bytesFreed += stats.size;
|
|
17237
|
+
if (!options.dryRun) unlinkSync5(path28);
|
|
17238
|
+
result.removed++;
|
|
17239
|
+
} catch {
|
|
17240
|
+
}
|
|
17241
|
+
}
|
|
17242
|
+
return result;
|
|
17243
|
+
}
|
|
17022
17244
|
function cleanTemps(options) {
|
|
17023
17245
|
const result = {
|
|
17024
17246
|
category: "temp",
|
|
@@ -17036,7 +17258,7 @@ function cleanTemps(options) {
|
|
|
17036
17258
|
}
|
|
17037
17259
|
for (const entry of entries) {
|
|
17038
17260
|
if (!patterns.some((p) => p.test(entry))) continue;
|
|
17039
|
-
const fullPath =
|
|
17261
|
+
const fullPath = join18(tmpDir, entry);
|
|
17040
17262
|
try {
|
|
17041
17263
|
const stats = statSync8(fullPath);
|
|
17042
17264
|
if (!stats.isFile()) continue;
|
|
@@ -17066,7 +17288,7 @@ function cleanSessions(options) {
|
|
|
17066
17288
|
paths: []
|
|
17067
17289
|
};
|
|
17068
17290
|
const unipiDir = expandHome("~/.unipi");
|
|
17069
|
-
const sessionsDir =
|
|
17291
|
+
const sessionsDir = join18(unipiDir, "sessions");
|
|
17070
17292
|
if (!existsSync16(sessionsDir)) return result;
|
|
17071
17293
|
let entries;
|
|
17072
17294
|
try {
|
|
@@ -17075,7 +17297,7 @@ function cleanSessions(options) {
|
|
|
17075
17297
|
return result;
|
|
17076
17298
|
}
|
|
17077
17299
|
for (const entry of entries) {
|
|
17078
|
-
const fullPath =
|
|
17300
|
+
const fullPath = join18(sessionsDir, entry);
|
|
17079
17301
|
try {
|
|
17080
17302
|
const stats = statSync8(fullPath);
|
|
17081
17303
|
if (!stats.isDirectory()) continue;
|
|
@@ -17087,7 +17309,7 @@ function cleanSessions(options) {
|
|
|
17087
17309
|
const removeRecursive = (dir) => {
|
|
17088
17310
|
const items = readdirSync10(dir);
|
|
17089
17311
|
for (const item of items) {
|
|
17090
|
-
const itemPath =
|
|
17312
|
+
const itemPath = join18(dir, item);
|
|
17091
17313
|
const itemStats = statSync8(itemPath);
|
|
17092
17314
|
if (itemStats.isDirectory()) {
|
|
17093
17315
|
removeRecursive(itemPath);
|
|
@@ -17126,7 +17348,7 @@ function cleanCache(options) {
|
|
|
17126
17348
|
return result;
|
|
17127
17349
|
}
|
|
17128
17350
|
for (const entry of entries) {
|
|
17129
|
-
const fullPath =
|
|
17351
|
+
const fullPath = join18(cacheDir, entry);
|
|
17130
17352
|
try {
|
|
17131
17353
|
const stats = statSync8(fullPath);
|
|
17132
17354
|
if (!stats.isFile()) continue;
|
|
@@ -17153,6 +17375,7 @@ function cleanupStale(options = {}) {
|
|
|
17153
17375
|
const results = [
|
|
17154
17376
|
cleanDbs(opts),
|
|
17155
17377
|
cleanTemps(opts),
|
|
17378
|
+
cleanToolResults(opts),
|
|
17156
17379
|
cleanSessions(opts),
|
|
17157
17380
|
cleanCache(opts)
|
|
17158
17381
|
];
|
|
@@ -17193,11 +17416,11 @@ function formatCleanupReport(report) {
|
|
|
17193
17416
|
|
|
17194
17417
|
// packages/utility/src/diagnostics/engine.ts
|
|
17195
17418
|
import { existsSync as existsSync17, accessSync as accessSync2, constants as constants2 } from "node:fs";
|
|
17196
|
-
import { join as
|
|
17197
|
-
import { homedir as
|
|
17419
|
+
import { join as join19 } from "node:path";
|
|
17420
|
+
import { homedir as homedir15 } from "node:os";
|
|
17198
17421
|
function expandHome2(path28) {
|
|
17199
17422
|
if (path28.startsWith("~/")) {
|
|
17200
|
-
return
|
|
17423
|
+
return join19(homedir15(), path28.slice(2));
|
|
17201
17424
|
}
|
|
17202
17425
|
return path28;
|
|
17203
17426
|
}
|
|
@@ -19263,7 +19486,7 @@ function getLifecycle(options) {
|
|
|
19263
19486
|
}
|
|
19264
19487
|
|
|
19265
19488
|
// packages/utility/src/analytics/collector.ts
|
|
19266
|
-
import { randomUUID as
|
|
19489
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
19267
19490
|
var DEFAULTS3 = {
|
|
19268
19491
|
dbPath: "~/.unipi/analytics/events.db",
|
|
19269
19492
|
maxEvents: 1e4,
|
|
@@ -19272,7 +19495,7 @@ var DEFAULTS3 = {
|
|
|
19272
19495
|
var eventBuffer = [];
|
|
19273
19496
|
var bufferFlushTimer = null;
|
|
19274
19497
|
function generateId() {
|
|
19275
|
-
return
|
|
19498
|
+
return randomUUID4();
|
|
19276
19499
|
}
|
|
19277
19500
|
function today() {
|
|
19278
19501
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -19480,6 +19703,187 @@ function registerInfoScreen(pi) {
|
|
|
19480
19703
|
});
|
|
19481
19704
|
}
|
|
19482
19705
|
|
|
19706
|
+
// packages/utility/src/prefix-cache.ts
|
|
19707
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
19708
|
+
var EMPTY_SNAPSHOT = {
|
|
19709
|
+
epoch: 0,
|
|
19710
|
+
requests: 0,
|
|
19711
|
+
prefixExtensions: 0,
|
|
19712
|
+
identicalRetries: 0,
|
|
19713
|
+
boundaries: 0,
|
|
19714
|
+
lastTransition: "none",
|
|
19715
|
+
requestFingerprint: "none",
|
|
19716
|
+
envelopeFingerprint: "none",
|
|
19717
|
+
sequenceField: "none",
|
|
19718
|
+
sequenceItems: 0,
|
|
19719
|
+
route: "unknown",
|
|
19720
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, responses: 0 }
|
|
19721
|
+
};
|
|
19722
|
+
function canonicalize(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
19723
|
+
if (value === void 0) return { $type: "undefined" };
|
|
19724
|
+
if (typeof value === "bigint") return { $type: "bigint", value: value.toString() };
|
|
19725
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
19726
|
+
return { $type: "number", value: String(value) };
|
|
19727
|
+
}
|
|
19728
|
+
if (typeof value !== "object" || value === null) return value;
|
|
19729
|
+
if (seen.has(value)) return { $type: "cycle" };
|
|
19730
|
+
seen.add(value);
|
|
19731
|
+
if (Array.isArray(value)) {
|
|
19732
|
+
const result2 = value.map((item) => canonicalize(item, seen));
|
|
19733
|
+
seen.delete(value);
|
|
19734
|
+
return result2;
|
|
19735
|
+
}
|
|
19736
|
+
if (value instanceof Uint8Array) {
|
|
19737
|
+
seen.delete(value);
|
|
19738
|
+
return { $type: "bytes", length: value.byteLength };
|
|
19739
|
+
}
|
|
19740
|
+
const record = value;
|
|
19741
|
+
const result = {};
|
|
19742
|
+
for (const key of Object.keys(record).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
19743
|
+
result[key] = canonicalize(record[key], seen);
|
|
19744
|
+
}
|
|
19745
|
+
seen.delete(value);
|
|
19746
|
+
return result;
|
|
19747
|
+
}
|
|
19748
|
+
function canonicalJson(value) {
|
|
19749
|
+
return JSON.stringify(canonicalize(value));
|
|
19750
|
+
}
|
|
19751
|
+
function routeLabel(model) {
|
|
19752
|
+
if (!model || typeof model !== "object") return "unknown";
|
|
19753
|
+
const value = model;
|
|
19754
|
+
return [value.provider, value.id, value.api].filter((part) => typeof part === "string" && part.length > 0).join("/") || "unknown";
|
|
19755
|
+
}
|
|
19756
|
+
function findSequence(payload) {
|
|
19757
|
+
for (const field of ["messages", "input", "contents"]) {
|
|
19758
|
+
if (Array.isArray(payload[field])) return { field, items: payload[field] };
|
|
19759
|
+
}
|
|
19760
|
+
return { field: "none", items: [] };
|
|
19761
|
+
}
|
|
19762
|
+
function isPrefix(previous, current) {
|
|
19763
|
+
return previous.length <= current.length && previous.every((item, index) => current[index] === item);
|
|
19764
|
+
}
|
|
19765
|
+
function usageNumber(value) {
|
|
19766
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
19767
|
+
}
|
|
19768
|
+
var PrefixCacheTracker = class {
|
|
19769
|
+
key;
|
|
19770
|
+
previous = null;
|
|
19771
|
+
seenResponses = /* @__PURE__ */ new WeakSet();
|
|
19772
|
+
snapshot = structuredClone(EMPTY_SNAPSHOT);
|
|
19773
|
+
constructor(key = randomBytes(32)) {
|
|
19774
|
+
this.key = key;
|
|
19775
|
+
}
|
|
19776
|
+
reset() {
|
|
19777
|
+
this.previous = null;
|
|
19778
|
+
this.seenResponses = /* @__PURE__ */ new WeakSet();
|
|
19779
|
+
this.snapshot = structuredClone(EMPTY_SNAPSHOT);
|
|
19780
|
+
}
|
|
19781
|
+
fingerprint(value) {
|
|
19782
|
+
return createHmac("sha256", this.key).update(canonicalJson(value)).digest("hex").slice(0, 16);
|
|
19783
|
+
}
|
|
19784
|
+
observeRequest(payload, model) {
|
|
19785
|
+
const object = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { payload };
|
|
19786
|
+
const { field, items } = findSequence(object);
|
|
19787
|
+
const route = routeLabel(model);
|
|
19788
|
+
const envelope = { route };
|
|
19789
|
+
for (const [key, value] of Object.entries(object)) {
|
|
19790
|
+
if (key !== field) envelope[key] = value;
|
|
19791
|
+
}
|
|
19792
|
+
const current = {
|
|
19793
|
+
envelopeFingerprint: this.fingerprint(envelope),
|
|
19794
|
+
requestFingerprint: this.fingerprint(object),
|
|
19795
|
+
sequenceField: field,
|
|
19796
|
+
sequenceItemFingerprints: items.map((item) => this.fingerprint(item))
|
|
19797
|
+
};
|
|
19798
|
+
let transition;
|
|
19799
|
+
let epoch = this.snapshot.epoch;
|
|
19800
|
+
if (!this.previous) {
|
|
19801
|
+
transition = "first_request";
|
|
19802
|
+
epoch = Math.max(1, epoch);
|
|
19803
|
+
} else if (current.envelopeFingerprint !== this.previous.envelopeFingerprint) {
|
|
19804
|
+
transition = "envelope_changed";
|
|
19805
|
+
epoch++;
|
|
19806
|
+
} else if (current.sequenceField !== this.previous.sequenceField) {
|
|
19807
|
+
transition = "payload_shape_changed";
|
|
19808
|
+
epoch++;
|
|
19809
|
+
} else if (current.requestFingerprint === this.previous.requestFingerprint) {
|
|
19810
|
+
transition = "identical_retry";
|
|
19811
|
+
} else if (isPrefix(this.previous.sequenceItemFingerprints, current.sequenceItemFingerprints)) {
|
|
19812
|
+
transition = "prefix_extended";
|
|
19813
|
+
} else {
|
|
19814
|
+
transition = "history_rewritten";
|
|
19815
|
+
epoch++;
|
|
19816
|
+
}
|
|
19817
|
+
this.snapshot = {
|
|
19818
|
+
...this.snapshot,
|
|
19819
|
+
epoch,
|
|
19820
|
+
requests: this.snapshot.requests + 1,
|
|
19821
|
+
prefixExtensions: this.snapshot.prefixExtensions + (transition === "prefix_extended" ? 1 : 0),
|
|
19822
|
+
identicalRetries: this.snapshot.identicalRetries + (transition === "identical_retry" ? 1 : 0),
|
|
19823
|
+
boundaries: this.snapshot.boundaries + (transition === "envelope_changed" || transition === "history_rewritten" || transition === "payload_shape_changed" ? 1 : 0),
|
|
19824
|
+
lastTransition: transition,
|
|
19825
|
+
requestFingerprint: current.requestFingerprint,
|
|
19826
|
+
envelopeFingerprint: current.envelopeFingerprint,
|
|
19827
|
+
sequenceField: current.sequenceField,
|
|
19828
|
+
sequenceItems: current.sequenceItemFingerprints.length,
|
|
19829
|
+
route
|
|
19830
|
+
};
|
|
19831
|
+
this.previous = current;
|
|
19832
|
+
return this.getSnapshot();
|
|
19833
|
+
}
|
|
19834
|
+
observeMessages(messages) {
|
|
19835
|
+
for (const message of messages) {
|
|
19836
|
+
if (!message || typeof message !== "object") continue;
|
|
19837
|
+
const value = message;
|
|
19838
|
+
if (value.role !== "assistant" || !value.usage || typeof value.usage !== "object") continue;
|
|
19839
|
+
if (value.stopReason === "error" || value.stopReason === "aborted") continue;
|
|
19840
|
+
if (this.seenResponses.has(message)) continue;
|
|
19841
|
+
this.seenResponses.add(message);
|
|
19842
|
+
const usage = value.usage;
|
|
19843
|
+
this.snapshot.usage.input += usageNumber(usage.input);
|
|
19844
|
+
this.snapshot.usage.output += usageNumber(usage.output);
|
|
19845
|
+
this.snapshot.usage.cacheRead += usageNumber(usage.cacheRead);
|
|
19846
|
+
this.snapshot.usage.cacheWrite += usageNumber(usage.cacheWrite);
|
|
19847
|
+
this.snapshot.usage.responses++;
|
|
19848
|
+
}
|
|
19849
|
+
return this.getSnapshot();
|
|
19850
|
+
}
|
|
19851
|
+
markBoundary(transition) {
|
|
19852
|
+
if (this.previous) {
|
|
19853
|
+
this.snapshot.epoch++;
|
|
19854
|
+
this.snapshot.boundaries++;
|
|
19855
|
+
}
|
|
19856
|
+
this.snapshot.lastTransition = transition;
|
|
19857
|
+
this.previous = null;
|
|
19858
|
+
}
|
|
19859
|
+
getSnapshot() {
|
|
19860
|
+
return structuredClone(this.snapshot);
|
|
19861
|
+
}
|
|
19862
|
+
};
|
|
19863
|
+
function formatPrefixCacheStats(snapshot) {
|
|
19864
|
+
const usage = snapshot.usage;
|
|
19865
|
+
const totalPrompt = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
19866
|
+
const readRate = totalPrompt > 0 ? `${(usage.cacheRead / totalPrompt * 100).toFixed(1)}%` : "n/a";
|
|
19867
|
+
return [
|
|
19868
|
+
"## Provider Prefix Cache",
|
|
19869
|
+
"",
|
|
19870
|
+
`- Epoch: ${snapshot.epoch || "not observed"}`,
|
|
19871
|
+
`- Requests observed: ${snapshot.requests}`,
|
|
19872
|
+
`- Prefix extensions: ${snapshot.prefixExtensions}`,
|
|
19873
|
+
`- Explicit boundaries: ${snapshot.boundaries}`,
|
|
19874
|
+
`- Last transition: ${snapshot.lastTransition}`,
|
|
19875
|
+
`- Route: ${snapshot.route}`,
|
|
19876
|
+
`- Request fingerprint: ${snapshot.requestFingerprint}`,
|
|
19877
|
+
`- Envelope fingerprint: ${snapshot.envelopeFingerprint}`,
|
|
19878
|
+
`- Provider cache read: ${usage.cacheRead} tokens`,
|
|
19879
|
+
`- Provider cache write: ${usage.cacheWrite} tokens`,
|
|
19880
|
+
`- Observed cache-read share: ${readRate}`,
|
|
19881
|
+
"",
|
|
19882
|
+
"Fingerprints are session-local keyed HMACs. Raw prompts, messages, tool arguments, and payloads are not retained or logged.",
|
|
19883
|
+
"A prefix extension indicates structural eligibility only; provider cache retention and routing still determine actual hits."
|
|
19884
|
+
].join("\n");
|
|
19885
|
+
}
|
|
19886
|
+
|
|
19483
19887
|
// packages/utility/src/index.ts
|
|
19484
19888
|
var VERSION6 = getPackageVersion(dirname14(fileURLToPath7(import.meta.url)));
|
|
19485
19889
|
var firstMessageSeen = false;
|
|
@@ -19496,12 +19900,44 @@ var ALL_COMMANDS = [
|
|
|
19496
19900
|
UTILITY_COMMANDS.BADGE_GEN,
|
|
19497
19901
|
UTILITY_COMMANDS.BADGE_TOGGLE,
|
|
19498
19902
|
UTILITY_COMMANDS.BADGE_SETTINGS,
|
|
19499
|
-
UTILITY_COMMANDS.UTIL_SETTINGS
|
|
19903
|
+
UTILITY_COMMANDS.UTIL_SETTINGS,
|
|
19904
|
+
UTILITY_COMMANDS.PREFIX_CACHE
|
|
19500
19905
|
].map((cmd) => `unipi:${cmd}`);
|
|
19501
19906
|
var ALL_TOOLS2 = [UTILITY_TOOLS.BATCH, UTILITY_TOOLS.ENV, UTILITY_TOOLS.SET_SESSION_NAME];
|
|
19502
19907
|
function src_default3(pi) {
|
|
19503
19908
|
const lifecycle = getLifecycle();
|
|
19504
19909
|
const analytics = getAnalyticsCollector();
|
|
19910
|
+
const prefixCache = new PrefixCacheTracker();
|
|
19911
|
+
pi.registerCommand(`unipi:${UTILITY_COMMANDS.PREFIX_CACHE}`, {
|
|
19912
|
+
description: "Show privacy-safe provider prefix-cache diagnostics",
|
|
19913
|
+
handler: async (_args, ctx) => {
|
|
19914
|
+
const report = formatPrefixCacheStats(prefixCache.getSnapshot());
|
|
19915
|
+
if (ctx.hasUI) {
|
|
19916
|
+
ctx.ui.notify(report, "info");
|
|
19917
|
+
} else {
|
|
19918
|
+
pi.sendMessage({ customType: "unipi-response", content: report, display: true }, { deliverAs: "followUp" });
|
|
19919
|
+
}
|
|
19920
|
+
}
|
|
19921
|
+
});
|
|
19922
|
+
pi.on("session_start", () => {
|
|
19923
|
+
prefixCache.reset();
|
|
19924
|
+
});
|
|
19925
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
19926
|
+
prefixCache.observeRequest(event.payload, ctx.model);
|
|
19927
|
+
});
|
|
19928
|
+
pi.on("agent_end", (event) => {
|
|
19929
|
+
prefixCache.observeMessages(event.messages);
|
|
19930
|
+
});
|
|
19931
|
+
pi.on("model_select", (event) => {
|
|
19932
|
+
if (event.previousModel && (event.previousModel.provider !== event.model.provider || event.previousModel.id !== event.model.id || event.previousModel.api !== event.model.api)) {
|
|
19933
|
+
prefixCache.markBoundary("envelope_changed");
|
|
19934
|
+
}
|
|
19935
|
+
});
|
|
19936
|
+
pi.on("thinking_level_select", (event) => {
|
|
19937
|
+
if (event.previousLevel !== event.level) prefixCache.markBoundary("envelope_changed");
|
|
19938
|
+
});
|
|
19939
|
+
pi.on("session_compact", () => prefixCache.markBoundary("history_rewritten"));
|
|
19940
|
+
pi.on("session_tree", () => prefixCache.markBoundary("history_rewritten"));
|
|
19505
19941
|
lifecycle.registerCleanup(async () => {
|
|
19506
19942
|
analytics.disable();
|
|
19507
19943
|
});
|
|
@@ -20349,9 +20785,9 @@ function renderLauncherUI(params) {
|
|
|
20349
20785
|
}
|
|
20350
20786
|
|
|
20351
20787
|
// packages/ask-user/config.ts
|
|
20352
|
-
import { readFileSync as readFileSync16, writeFileSync as
|
|
20353
|
-
import { join as
|
|
20354
|
-
import { homedir as
|
|
20788
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync16, existsSync as existsSync21 } from "node:fs";
|
|
20789
|
+
import { join as join21 } from "node:path";
|
|
20790
|
+
import { homedir as homedir17 } from "node:os";
|
|
20355
20791
|
var DEFAULT_SETTINGS3 = {
|
|
20356
20792
|
enabled: true,
|
|
20357
20793
|
allowedFormats: {
|
|
@@ -20361,7 +20797,7 @@ var DEFAULT_SETTINGS3 = {
|
|
|
20361
20797
|
},
|
|
20362
20798
|
notifyOnAsk: true
|
|
20363
20799
|
};
|
|
20364
|
-
var SETTINGS_PATH2 =
|
|
20800
|
+
var SETTINGS_PATH2 = join21(homedir17(), ".pi", "agent", "settings.json");
|
|
20365
20801
|
var SETTINGS_KEY2 = "unipi";
|
|
20366
20802
|
var cachedSettings2 = null;
|
|
20367
20803
|
function isRecord3(value) {
|
|
@@ -20381,7 +20817,7 @@ function writeSettingsFile2(data) {
|
|
|
20381
20817
|
if (!existsSync21(dir)) {
|
|
20382
20818
|
__require("node:fs").mkdirSync(dir, { recursive: true });
|
|
20383
20819
|
}
|
|
20384
|
-
|
|
20820
|
+
writeFileSync16(SETTINGS_PATH2, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
20385
20821
|
}
|
|
20386
20822
|
function getAskUserSettings() {
|
|
20387
20823
|
if (cachedSettings2) return cachedSettings2;
|
|
@@ -21807,7 +22243,7 @@ Stderr: ${this.stderrBuffer}`
|
|
|
21807
22243
|
|
|
21808
22244
|
// packages/mcp/src/bridge/translator.ts
|
|
21809
22245
|
init_core();
|
|
21810
|
-
function
|
|
22246
|
+
function compareCodeUnits3(left, right) {
|
|
21811
22247
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
21812
22248
|
}
|
|
21813
22249
|
function isObject(value) {
|
|
@@ -21817,13 +22253,13 @@ var LITERAL_VALUE_KEYWORDS = /* @__PURE__ */ new Set(["const", "default", "enum"
|
|
|
21817
22253
|
function canonicalizeValue(value, key, normalizeSchemaKeywords = true) {
|
|
21818
22254
|
if (Array.isArray(value)) {
|
|
21819
22255
|
if (normalizeSchemaKeywords && key === "required" && value.every((item) => typeof item === "string")) {
|
|
21820
|
-
return [...new Set(value)].sort(
|
|
22256
|
+
return [...new Set(value)].sort(compareCodeUnits3);
|
|
21821
22257
|
}
|
|
21822
22258
|
return value.map((item) => canonicalizeValue(item, void 0, normalizeSchemaKeywords));
|
|
21823
22259
|
}
|
|
21824
22260
|
if (!isObject(value)) return value;
|
|
21825
22261
|
const canonical = {};
|
|
21826
|
-
for (const objectKey of Object.keys(value).sort(
|
|
22262
|
+
for (const objectKey of Object.keys(value).sort(compareCodeUnits3)) {
|
|
21827
22263
|
const childNormalizesSchemaKeywords = normalizeSchemaKeywords && !LITERAL_VALUE_KEYWORDS.has(objectKey);
|
|
21828
22264
|
canonical[objectKey] = canonicalizeValue(
|
|
21829
22265
|
value[objectKey],
|
|
@@ -21873,19 +22309,26 @@ function translateMcpTool(mcpTool, serverName, client) {
|
|
|
21873
22309
|
});
|
|
21874
22310
|
}
|
|
21875
22311
|
}
|
|
21876
|
-
if (result.isError) {
|
|
21877
|
-
const errorText = blocks.map((b) => b.text).join("\n") || "Unknown error";
|
|
21878
|
-
return {
|
|
21879
|
-
content: [{ type: "text", text: `MCP tool error from ${serverName}: ${errorText}` }],
|
|
21880
|
-
details: { error: true, server: serverName, tool: mcpTool.name }
|
|
21881
|
-
};
|
|
21882
|
-
}
|
|
21883
22312
|
if (blocks.length === 0) {
|
|
21884
|
-
blocks.push({ type: "text", text: "(no output)" });
|
|
22313
|
+
blocks.push({ type: "text", text: result.isError ? "Unknown error" : "(no output)" });
|
|
21885
22314
|
}
|
|
22315
|
+
const rawText = blocks.map((block2) => block2.text).join("\n");
|
|
22316
|
+
const wrapper = result.isError ? `MCP tool error from ${serverName}: ` : "";
|
|
22317
|
+
const output = boundModelOutput(rawText, {
|
|
22318
|
+
maxBytes: Math.max(1024, MCP_DEFAULTS.MAX_MODEL_OUTPUT_BYTES - Buffer.byteLength(wrapper, "utf8")),
|
|
22319
|
+
artifactPrefix: `mcp-${serverName}-${mcpTool.name}`
|
|
22320
|
+
});
|
|
22321
|
+
const visibleText = `${wrapper}${output.text}`;
|
|
21886
22322
|
return {
|
|
21887
|
-
content:
|
|
21888
|
-
details: {
|
|
22323
|
+
content: [{ type: "text", text: visibleText }],
|
|
22324
|
+
details: {
|
|
22325
|
+
error: result.isError || void 0,
|
|
22326
|
+
server: serverName,
|
|
22327
|
+
tool: mcpTool.name,
|
|
22328
|
+
truncated: output.truncated,
|
|
22329
|
+
originalBytes: output.originalBytes,
|
|
22330
|
+
artifactPath: output.artifactPath
|
|
22331
|
+
}
|
|
21889
22332
|
};
|
|
21890
22333
|
} catch (err) {
|
|
21891
22334
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -21970,7 +22413,7 @@ var ServerRegistry = class {
|
|
|
21970
22413
|
);
|
|
21971
22414
|
const prepared = settled.filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
21972
22415
|
if (prepared.length === 0) return;
|
|
21973
|
-
const registrations = prepared.flatMap(({ entry, tools }) => tools.map((tool) => ({ entry, tool }))).sort((left, right) =>
|
|
22416
|
+
const registrations = prepared.flatMap(({ entry, tools }) => tools.map((tool) => ({ entry, tool }))).sort((left, right) => compareCodeUnits3(left.tool.name, right.tool.name));
|
|
21974
22417
|
try {
|
|
21975
22418
|
this.assertUniqueFinalToolNames(registrations.map(({ tool }) => tool.name));
|
|
21976
22419
|
} catch (error) {
|
|
@@ -21995,7 +22438,7 @@ var ServerRegistry = class {
|
|
|
21995
22438
|
} else {
|
|
21996
22439
|
const message = `${error instanceof Error ? error.message : String(error)}; some MCP tools remain registered until Pi restarts`;
|
|
21997
22440
|
for (const { entry, client, tools } of prepared) {
|
|
21998
|
-
const toolNames = tools.map((tool) => tool.name).filter((name) => registeredNames.includes(name)).sort(
|
|
22441
|
+
const toolNames = tools.map((tool) => tool.name).filter((name) => registeredNames.includes(name)).sort(compareCodeUnits3);
|
|
21999
22442
|
entry.toolNames = toolNames;
|
|
22000
22443
|
entry.state = {
|
|
22001
22444
|
...entry.state,
|
|
@@ -22012,8 +22455,8 @@ var ServerRegistry = class {
|
|
|
22012
22455
|
}
|
|
22013
22456
|
throw error;
|
|
22014
22457
|
}
|
|
22015
|
-
for (const { entry, client, tools } of [...prepared].sort((left, right) =>
|
|
22016
|
-
const toolNames = tools.map((tool) => tool.name).sort(
|
|
22458
|
+
for (const { entry, client, tools } of [...prepared].sort((left, right) => compareCodeUnits3(left.entry.name, right.entry.name))) {
|
|
22459
|
+
const toolNames = tools.map((tool) => tool.name).sort(compareCodeUnits3);
|
|
22017
22460
|
entry.toolNames = toolNames;
|
|
22018
22461
|
entry.state = {
|
|
22019
22462
|
...entry.state,
|
|
@@ -22065,7 +22508,7 @@ var ServerRegistry = class {
|
|
|
22065
22508
|
await connectedClient.connect(safeCommand, safeArgs, safeEnv);
|
|
22066
22509
|
entry.client = connectedClient;
|
|
22067
22510
|
const mcpTools = await connectedClient.listTools();
|
|
22068
|
-
const tools = mcpTools.map((tool) => translateMcpTool(tool, name, connectedClient)).sort((left, right) =>
|
|
22511
|
+
const tools = mcpTools.map((tool) => translateMcpTool(tool, name, connectedClient)).sort((left, right) => compareCodeUnits3(left.name, right.name));
|
|
22069
22512
|
return { entry, client: connectedClient, tools };
|
|
22070
22513
|
} catch (error) {
|
|
22071
22514
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -22091,7 +22534,7 @@ var ServerRegistry = class {
|
|
|
22091
22534
|
}
|
|
22092
22535
|
if (duplicates.size > 0) {
|
|
22093
22536
|
throw new Error(
|
|
22094
|
-
`Duplicate final MCP tool name(s): ${[...duplicates].sort(
|
|
22537
|
+
`Duplicate final MCP tool name(s): ${[...duplicates].sort(compareCodeUnits3).join(", ")}`
|
|
22095
22538
|
);
|
|
22096
22539
|
}
|
|
22097
22540
|
}
|
|
@@ -23038,7 +23481,7 @@ function src_default4(pi) {
|
|
|
23038
23481
|
`unipi:${MCP_COMMANDS.STATUS}`,
|
|
23039
23482
|
`unipi:${MCP_COMMANDS.RELOAD}`
|
|
23040
23483
|
],
|
|
23041
|
-
tools: activeServers.flatMap((server) => registry2?.getEntry(server.name)?.toolNames ?? []).sort(
|
|
23484
|
+
tools: activeServers.flatMap((server) => registry2?.getEntry(server.name)?.toolNames ?? []).sort(compareCodeUnits3)
|
|
23042
23485
|
});
|
|
23043
23486
|
});
|
|
23044
23487
|
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
@@ -23175,12 +23618,12 @@ import { Type as Type6 } from "typebox";
|
|
|
23175
23618
|
|
|
23176
23619
|
// packages/notify/settings.ts
|
|
23177
23620
|
init_core();
|
|
23178
|
-
import { readFileSync as readFileSync19, writeFileSync as
|
|
23179
|
-
import { dirname as dirname19, join as
|
|
23180
|
-
import { homedir as
|
|
23621
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync19, mkdirSync as mkdirSync17, existsSync as existsSync24 } from "fs";
|
|
23622
|
+
import { dirname as dirname19, join as join24 } from "path";
|
|
23623
|
+
import { homedir as homedir19 } from "os";
|
|
23181
23624
|
function resolveConfigPath() {
|
|
23182
|
-
const base = NOTIFY_DIRS.CONFIG.replace("~",
|
|
23183
|
-
return
|
|
23625
|
+
const base = NOTIFY_DIRS.CONFIG.replace("~", homedir19());
|
|
23626
|
+
return join24(base, "config.json");
|
|
23184
23627
|
}
|
|
23185
23628
|
var DEFAULT_CONFIG4 = {
|
|
23186
23629
|
defaultPlatforms: ["native"],
|
|
@@ -23231,8 +23674,8 @@ function loadConfig2() {
|
|
|
23231
23674
|
function saveConfig2(config) {
|
|
23232
23675
|
const configPath = resolveConfigPath();
|
|
23233
23676
|
const dir = dirname19(configPath);
|
|
23234
|
-
|
|
23235
|
-
|
|
23677
|
+
mkdirSync17(dir, { recursive: true });
|
|
23678
|
+
writeFileSync19(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
23236
23679
|
}
|
|
23237
23680
|
function updateConfig(partial) {
|
|
23238
23681
|
const current = loadConfig2();
|
|
@@ -23290,19 +23733,19 @@ function mergeWithDefaults(loaded) {
|
|
|
23290
23733
|
init_core();
|
|
23291
23734
|
|
|
23292
23735
|
// packages/notify/ntfy-config.ts
|
|
23293
|
-
import { readFileSync as readFileSync20, writeFileSync as
|
|
23294
|
-
import { dirname as dirname20, join as
|
|
23295
|
-
import { homedir as
|
|
23736
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync20, mkdirSync as mkdirSync18, existsSync as existsSync25 } from "fs";
|
|
23737
|
+
import { dirname as dirname20, join as join25 } from "path";
|
|
23738
|
+
import { homedir as homedir20 } from "os";
|
|
23296
23739
|
var DEFAULT_NTFY_CONFIG = {
|
|
23297
23740
|
enabled: false,
|
|
23298
23741
|
serverUrl: "https://ntfy.sh",
|
|
23299
23742
|
priority: 3
|
|
23300
23743
|
};
|
|
23301
23744
|
function getGlobalNtfyPath() {
|
|
23302
|
-
return
|
|
23745
|
+
return join25(homedir20(), ".unipi", "config", "notify", "ntfy.json");
|
|
23303
23746
|
}
|
|
23304
23747
|
function getProjectNtfyPath(cwd) {
|
|
23305
|
-
return
|
|
23748
|
+
return join25(cwd, ".unipi", "config", "notify", "ntfy.json");
|
|
23306
23749
|
}
|
|
23307
23750
|
function readNtfyJson(filePath) {
|
|
23308
23751
|
try {
|
|
@@ -23332,8 +23775,8 @@ function loadNtfyConfig(cwd) {
|
|
|
23332
23775
|
function saveNtfyConfig(scope, cwd, config) {
|
|
23333
23776
|
const filePath = scope === "project" ? getProjectNtfyPath(cwd) : getGlobalNtfyPath();
|
|
23334
23777
|
const dir = dirname20(filePath);
|
|
23335
|
-
|
|
23336
|
-
|
|
23778
|
+
mkdirSync18(dir, { recursive: true });
|
|
23779
|
+
writeFileSync20(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
23337
23780
|
}
|
|
23338
23781
|
function getNtfyConfigScope(cwd) {
|
|
23339
23782
|
if (existsSync25(getProjectNtfyPath(cwd))) {
|
|
@@ -23349,8 +23792,8 @@ function migrateFromLegacyConfig() {
|
|
|
23349
23792
|
if (existsSync25(globalNtfyPath)) {
|
|
23350
23793
|
return;
|
|
23351
23794
|
}
|
|
23352
|
-
const legacyConfigPath =
|
|
23353
|
-
|
|
23795
|
+
const legacyConfigPath = join25(
|
|
23796
|
+
homedir20(),
|
|
23354
23797
|
".unipi",
|
|
23355
23798
|
"config",
|
|
23356
23799
|
"notify",
|
|
@@ -23372,8 +23815,8 @@ function migrateFromLegacyConfig() {
|
|
|
23372
23815
|
priority: ntfySection.priority ?? 3
|
|
23373
23816
|
};
|
|
23374
23817
|
const dir = dirname20(globalNtfyPath);
|
|
23375
|
-
|
|
23376
|
-
|
|
23818
|
+
mkdirSync18(dir, { recursive: true });
|
|
23819
|
+
writeFileSync20(
|
|
23377
23820
|
globalNtfyPath,
|
|
23378
23821
|
JSON.stringify(migratedConfig, null, 2) + "\n",
|
|
23379
23822
|
"utf-8"
|
|
@@ -23387,8 +23830,8 @@ import notifier from "node-notifier";
|
|
|
23387
23830
|
|
|
23388
23831
|
// packages/notify/platforms/focus-win.ts
|
|
23389
23832
|
import { execFile } from "child_process";
|
|
23390
|
-
import { writeFileSync as
|
|
23391
|
-
import { join as
|
|
23833
|
+
import { writeFileSync as writeFileSync21, rmSync as rmSync2, mkdtempSync } from "fs";
|
|
23834
|
+
import { join as join26 } from "path";
|
|
23392
23835
|
import { tmpdir as tmpdir2 } from "os";
|
|
23393
23836
|
var POWERCHECK_SCRIPT = `
|
|
23394
23837
|
param($targetPid)
|
|
@@ -23424,9 +23867,9 @@ async function isWindowFocusedOnWindows() {
|
|
|
23424
23867
|
}
|
|
23425
23868
|
let tmpDir = null;
|
|
23426
23869
|
try {
|
|
23427
|
-
tmpDir = mkdtempSync(
|
|
23428
|
-
const scriptPath =
|
|
23429
|
-
|
|
23870
|
+
tmpDir = mkdtempSync(join26(tmpdir2(), "pi-focus-"));
|
|
23871
|
+
const scriptPath = join26(tmpDir, "check.ps1");
|
|
23872
|
+
writeFileSync21(scriptPath, POWERCHECK_SCRIPT, "utf-8");
|
|
23430
23873
|
const stdout = await new Promise((resolve14, reject) => {
|
|
23431
23874
|
execFile(
|
|
23432
23875
|
"powershell.exe",
|
|
@@ -26588,13 +27031,13 @@ function registerSessionEndHook(pi) {
|
|
|
26588
27031
|
|
|
26589
27032
|
// packages/milestone/commands.ts
|
|
26590
27033
|
init_core();
|
|
26591
|
-
import { dirname as dirname22, join as
|
|
27034
|
+
import { dirname as dirname22, join as join28 } from "node:path";
|
|
26592
27035
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
26593
27036
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
26594
|
-
var SKILLS_DIR =
|
|
27037
|
+
var SKILLS_DIR = join28(dirname22(fileURLToPath11(import.meta.url)), "skills");
|
|
26595
27038
|
function loadSkill(skillName) {
|
|
26596
27039
|
try {
|
|
26597
|
-
return readFileSync21(
|
|
27040
|
+
return readFileSync21(join28(SKILLS_DIR, skillName, "SKILL.md"), "utf-8");
|
|
26598
27041
|
} catch {
|
|
26599
27042
|
return "";
|
|
26600
27043
|
}
|
|
@@ -26647,7 +27090,7 @@ ${skillContent}
|
|
|
26647
27090
|
},
|
|
26648
27091
|
getArgumentCompletions: () => {
|
|
26649
27092
|
const cwd = process.cwd();
|
|
26650
|
-
const milestonesPath =
|
|
27093
|
+
const milestonesPath = join28(cwd, MILESTONE_DIRS.MILESTONES);
|
|
26651
27094
|
const doc = parseMilestones(milestonesPath);
|
|
26652
27095
|
const suggestions = doc.phases.map((phase) => ({
|
|
26653
27096
|
value: phase.name,
|
|
@@ -27103,6 +27546,7 @@ var COMMAND_REGISTRY = {
|
|
|
27103
27546
|
"unipi:worktree-list": "workflow",
|
|
27104
27547
|
"unipi:worktree-merge": "workflow",
|
|
27105
27548
|
"unipi:consultant": "workflow",
|
|
27549
|
+
"unipi:prefix-cache": "utility",
|
|
27106
27550
|
"unipi:quick-work": "workflow",
|
|
27107
27551
|
"unipi:gather-context": "workflow",
|
|
27108
27552
|
"unipi:document": "workflow",
|
|
@@ -27210,6 +27654,7 @@ var COMMAND_DESCRIPTIONS = {
|
|
|
27210
27654
|
"unipi:worktree-list": "List all unipi worktrees",
|
|
27211
27655
|
"unipi:worktree-merge": "Merge worktree branches back to main",
|
|
27212
27656
|
"unipi:consultant": "Expert consultation \u2014 advisory analysis",
|
|
27657
|
+
"unipi:prefix-cache": "Show privacy-safe provider prefix-cache diagnostics",
|
|
27213
27658
|
"unipi:quick-work": "Fast single-task execution \u2014 one shot",
|
|
27214
27659
|
"unipi:gather-context": "Research codebase \u2014 surface patterns",
|
|
27215
27660
|
"unipi:document": "Generate documentation \u2014 README, API docs",
|
|
@@ -27791,9 +28236,9 @@ function markAutoCompactionError(state, nowMs) {
|
|
|
27791
28236
|
// packages/compactor/src/session/db.ts
|
|
27792
28237
|
import { createHash as createHash3 } from "node:crypto";
|
|
27793
28238
|
import { execFileSync } from "node:child_process";
|
|
27794
|
-
import { existsSync as existsSync32, mkdirSync as
|
|
27795
|
-
import { homedir as
|
|
27796
|
-
import { dirname as dirname25, join as
|
|
28239
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync22 } from "node:fs";
|
|
28240
|
+
import { homedir as homedir23 } from "node:os";
|
|
28241
|
+
import { dirname as dirname25, join as join35 } from "node:path";
|
|
27797
28242
|
function getWorktreeSuffix() {
|
|
27798
28243
|
const envSuffix = process.env.COMPACTOR_SESSION_SUFFIX;
|
|
27799
28244
|
if (envSuffix !== void 0) {
|
|
@@ -27815,7 +28260,7 @@ function getWorktreeSuffix() {
|
|
|
27815
28260
|
return "";
|
|
27816
28261
|
}
|
|
27817
28262
|
function defaultDBPath(name) {
|
|
27818
|
-
return
|
|
28263
|
+
return join35(homedir23(), ".unipi", "db", "compactor", `${name}.db`);
|
|
27819
28264
|
}
|
|
27820
28265
|
var sqliteLib = null;
|
|
27821
28266
|
async function getSQLite() {
|
|
@@ -27839,7 +28284,7 @@ var SessionDB = class {
|
|
|
27839
28284
|
}
|
|
27840
28285
|
async init() {
|
|
27841
28286
|
const dir = dirname25(this.dbPath);
|
|
27842
|
-
if (!existsSync32(dir))
|
|
28287
|
+
if (!existsSync32(dir)) mkdirSync22(dir, { recursive: true });
|
|
27843
28288
|
const sqlite = await getSQLite();
|
|
27844
28289
|
const Database2 = sqlite.Database ?? sqlite.default?.Database ?? sqlite.default ?? sqlite;
|
|
27845
28290
|
this.db = new Database2(this.dbPath);
|
|
@@ -28232,8 +28677,8 @@ async function buildResumeContextMessage(db, sessionId) {
|
|
|
28232
28677
|
|
|
28233
28678
|
// packages/compactor/src/executor/executor.ts
|
|
28234
28679
|
import { spawn as spawn3, execSync, execFileSync as execFileSync2 } from "node:child_process";
|
|
28235
|
-
import { mkdtempSync as mkdtempSync2, writeFileSync as
|
|
28236
|
-
import { join as
|
|
28680
|
+
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync26, rmSync as rmSync3 } from "node:fs";
|
|
28681
|
+
import { join as join36, resolve as resolve11 } from "node:path";
|
|
28237
28682
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
28238
28683
|
|
|
28239
28684
|
// packages/compactor/src/executor/runtime.ts
|
|
@@ -28414,7 +28859,7 @@ var PolyglotExecutor = class {
|
|
|
28414
28859
|
}
|
|
28415
28860
|
async execute(opts) {
|
|
28416
28861
|
const { language, code, timeout = 3e4, background = false } = opts;
|
|
28417
|
-
const tmpDir = mkdtempSync2(
|
|
28862
|
+
const tmpDir = mkdtempSync2(join36(OS_TMPDIR, ".compactor-"));
|
|
28418
28863
|
try {
|
|
28419
28864
|
const filePath = this.#writeScript(tmpDir, code, language);
|
|
28420
28865
|
const cmd = buildCommand(this.#runtimes, language, filePath);
|
|
@@ -28464,8 +28909,8 @@ var PolyglotExecutor = class {
|
|
|
28464
28909
|
elixir: "ex"
|
|
28465
28910
|
};
|
|
28466
28911
|
const ext = extensions[language];
|
|
28467
|
-
const filePath =
|
|
28468
|
-
|
|
28912
|
+
const filePath = join36(tmpDir, `script.${ext}`);
|
|
28913
|
+
writeFileSync26(filePath, code, "utf-8");
|
|
28469
28914
|
return filePath;
|
|
28470
28915
|
}
|
|
28471
28916
|
async #spawn(cmd, cwd, tmpDir, timeout, background) {
|
|
@@ -28523,7 +28968,7 @@ var PolyglotExecutor = class {
|
|
|
28523
28968
|
});
|
|
28524
28969
|
}
|
|
28525
28970
|
async #compileAndRun(filePath, tmpDir, timeout) {
|
|
28526
|
-
const binPath =
|
|
28971
|
+
const binPath = join36(tmpDir, "script");
|
|
28527
28972
|
try {
|
|
28528
28973
|
execSync(`rustc "${filePath}" -o "${binPath}"`, {
|
|
28529
28974
|
cwd: tmpDir,
|
|
@@ -28633,15 +29078,29 @@ function searchEntries(blocks, query, opts) {
|
|
|
28633
29078
|
}
|
|
28634
29079
|
|
|
28635
29080
|
// packages/compactor/src/tools/vcc-recall.ts
|
|
29081
|
+
var MAX_RECALL_RESULTS = 50;
|
|
29082
|
+
var MAX_EXPANDED_HIT_BYTES = 16 * 1024;
|
|
29083
|
+
function truncateExpandedHit(text) {
|
|
29084
|
+
const bytes = Buffer.from(text, "utf8");
|
|
29085
|
+
if (bytes.byteLength <= MAX_EXPANDED_HIT_BYTES) return text;
|
|
29086
|
+
const omitted = bytes.byteLength - MAX_EXPANDED_HIT_BYTES;
|
|
29087
|
+
const visible = bytes.subarray(0, MAX_EXPANDED_HIT_BYTES).toString("utf8").replace(/\uFFFD+$/u, "");
|
|
29088
|
+
return `${visible}
|
|
29089
|
+
\u2026 ${omitted} bytes omitted from this hit; narrow the query to inspect more specific context \u2026`;
|
|
29090
|
+
}
|
|
28636
29091
|
function vccRecall(blocks, input) {
|
|
28637
|
-
const { query, mode = "bm25",
|
|
29092
|
+
const { query, mode = "bm25", expand = false } = input;
|
|
29093
|
+
const requestedLimit = Number.isFinite(input.limit) ? Math.floor(input.limit) : 10;
|
|
29094
|
+
const requestedOffset = Number.isFinite(input.offset) ? Math.floor(input.offset) : 0;
|
|
29095
|
+
const limit = Math.min(MAX_RECALL_RESULTS, Math.max(1, requestedLimit));
|
|
29096
|
+
const offset = Math.max(0, requestedOffset);
|
|
28638
29097
|
let hits = [];
|
|
28639
29098
|
if (mode === "bm25") {
|
|
28640
29099
|
const results = searchEntries(blocks, query, { limit: limit + offset, offset: 0 });
|
|
28641
29100
|
hits = results.map((r, i) => ({
|
|
28642
29101
|
index: r.docId,
|
|
28643
29102
|
score: r.score,
|
|
28644
|
-
text: expand ? r.text : r.text.slice(0, 200),
|
|
29103
|
+
text: expand ? truncateExpandedHit(r.text) : r.text.slice(0, 200),
|
|
28645
29104
|
kind: r.kind
|
|
28646
29105
|
}));
|
|
28647
29106
|
} else {
|
|
@@ -28653,7 +29112,7 @@ function vccRecall(blocks, input) {
|
|
|
28653
29112
|
hits.push({
|
|
28654
29113
|
index: i,
|
|
28655
29114
|
score: 1,
|
|
28656
|
-
text: expand ? text : text.slice(0, 200),
|
|
29115
|
+
text: expand ? truncateExpandedHit(text) : text.slice(0, 200),
|
|
28657
29116
|
kind: b.kind
|
|
28658
29117
|
});
|
|
28659
29118
|
}
|
|
@@ -29135,7 +29594,11 @@ var RecallParams = Type7.Object({
|
|
|
29135
29594
|
mode: Type7.Optional(Type7.Union([Type7.Literal("bm25"), Type7.Literal("regex")], {
|
|
29136
29595
|
description: "Search mode: bm25 (default) or regex fallback"
|
|
29137
29596
|
})),
|
|
29138
|
-
limit: Type7.Optional(Type7.Number({
|
|
29597
|
+
limit: Type7.Optional(Type7.Number({
|
|
29598
|
+
description: `Max results to return (default 10, hard cap ${MAX_RECALL_RESULTS})`,
|
|
29599
|
+
minimum: 1,
|
|
29600
|
+
maximum: MAX_RECALL_RESULTS
|
|
29601
|
+
})),
|
|
29139
29602
|
offset: Type7.Optional(Type7.Number({ description: "Pagination offset", minimum: 0 })),
|
|
29140
29603
|
expand: Type7.Optional(Type7.Boolean({ description: "Return full message content for hits" }))
|
|
29141
29604
|
});
|
|
@@ -29560,13 +30023,13 @@ function compactorExtension(pi) {
|
|
|
29560
30023
|
debug("before_agent_start", { sessionId: currentSessionId, configDebug: config.debug });
|
|
29561
30024
|
try {
|
|
29562
30025
|
const { existsSync: existsSync48 } = await import("node:fs");
|
|
29563
|
-
const { join:
|
|
30026
|
+
const { join: join50 } = await import("node:path");
|
|
29564
30027
|
const strategies = [
|
|
29565
30028
|
{ key: "commits", config: config.commits }
|
|
29566
30029
|
];
|
|
29567
30030
|
for (const { key, config: strat } of strategies) {
|
|
29568
30031
|
if (strat.autoDetect === "git") {
|
|
29569
|
-
const gitDir =
|
|
30032
|
+
const gitDir = join50(cwd, ".git");
|
|
29570
30033
|
if (!existsSync48(gitDir)) {
|
|
29571
30034
|
debug("autoDetect_disable", { strategy: key, reason: "no .git dir" });
|
|
29572
30035
|
strat.enabled = false;
|
|
@@ -33083,7 +33546,7 @@ init_core();
|
|
|
33083
33546
|
import { dirname as dirname27 } from "node:path";
|
|
33084
33547
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
33085
33548
|
import { existsSync as existsSync39, readFileSync as readFileSync34 } from "fs";
|
|
33086
|
-
import { join as
|
|
33549
|
+
import { join as join40 } from "path";
|
|
33087
33550
|
var PACKAGE_MAP = {
|
|
33088
33551
|
core: MODULES.CORE,
|
|
33089
33552
|
workflow: MODULES.WORKFLOW,
|
|
@@ -33109,11 +33572,11 @@ var PACKAGE_MAP = {
|
|
|
33109
33572
|
function resolveUnipiRoot() {
|
|
33110
33573
|
const dir = dirname27(fileURLToPath12(import.meta.url));
|
|
33111
33574
|
const root = findPackageRoot(dir, "@pi-unipi/unipi");
|
|
33112
|
-
return root ??
|
|
33575
|
+
return root ?? join40(dir, "..");
|
|
33113
33576
|
}
|
|
33114
33577
|
function getPackageVersion2(dir) {
|
|
33115
33578
|
try {
|
|
33116
|
-
const pkgPath =
|
|
33579
|
+
const pkgPath = join40(dir, "package.json");
|
|
33117
33580
|
if (existsSync39(pkgPath)) {
|
|
33118
33581
|
const raw = readFileSync34(pkgPath, "utf-8");
|
|
33119
33582
|
const pkg = JSON.parse(raw);
|
|
@@ -33126,7 +33589,7 @@ function getPackageVersion2(dir) {
|
|
|
33126
33589
|
function discoverReadmes() {
|
|
33127
33590
|
const root = resolveUnipiRoot();
|
|
33128
33591
|
const entries = [];
|
|
33129
|
-
const rootReadme =
|
|
33592
|
+
const rootReadme = join40(root, "README.md");
|
|
33130
33593
|
if (existsSync39(rootReadme)) {
|
|
33131
33594
|
entries.push({
|
|
33132
33595
|
name: "unipi",
|
|
@@ -33136,16 +33599,16 @@ function discoverReadmes() {
|
|
|
33136
33599
|
});
|
|
33137
33600
|
}
|
|
33138
33601
|
for (const [shortName, fullName] of Object.entries(PACKAGE_MAP)) {
|
|
33139
|
-
const workspacePath =
|
|
33140
|
-
const nodeModulesPath =
|
|
33602
|
+
const workspacePath = join40(root, "packages", shortName, "README.md");
|
|
33603
|
+
const nodeModulesPath = join40(root, "node_modules", fullName, "README.md");
|
|
33141
33604
|
let readmePath = null;
|
|
33142
33605
|
let versionDir = null;
|
|
33143
33606
|
if (existsSync39(workspacePath)) {
|
|
33144
33607
|
readmePath = workspacePath;
|
|
33145
|
-
versionDir =
|
|
33608
|
+
versionDir = join40(root, "packages", shortName);
|
|
33146
33609
|
} else if (existsSync39(nodeModulesPath)) {
|
|
33147
33610
|
readmePath = nodeModulesPath;
|
|
33148
|
-
versionDir =
|
|
33611
|
+
versionDir = join40(root, "node_modules", fullName);
|
|
33149
33612
|
}
|
|
33150
33613
|
if (readmePath && versionDir) {
|
|
33151
33614
|
entries.push({
|
|
@@ -33161,14 +33624,14 @@ function discoverReadmes() {
|
|
|
33161
33624
|
function resolveReadmePath(packageName) {
|
|
33162
33625
|
const root = resolveUnipiRoot();
|
|
33163
33626
|
if (!packageName || packageName === "unipi") {
|
|
33164
|
-
const rootReadme =
|
|
33627
|
+
const rootReadme = join40(root, "README.md");
|
|
33165
33628
|
return existsSync39(rootReadme) ? rootReadme : null;
|
|
33166
33629
|
}
|
|
33167
33630
|
const fullName = PACKAGE_MAP[packageName];
|
|
33168
33631
|
if (!fullName) return null;
|
|
33169
|
-
const workspacePath =
|
|
33632
|
+
const workspacePath = join40(root, "packages", packageName, "README.md");
|
|
33170
33633
|
if (existsSync39(workspacePath)) return workspacePath;
|
|
33171
|
-
const nodeModulesPath =
|
|
33634
|
+
const nodeModulesPath = join40(root, "node_modules", fullName, "README.md");
|
|
33172
33635
|
if (existsSync39(nodeModulesPath)) return nodeModulesPath;
|
|
33173
33636
|
return null;
|
|
33174
33637
|
}
|
|
@@ -33444,7 +33907,7 @@ import { Key as Key7, matchesKey as matchesKey15, truncateToWidth as truncateToW
|
|
|
33444
33907
|
// packages/updater/src/changelog.ts
|
|
33445
33908
|
init_core();
|
|
33446
33909
|
import { existsSync as existsSync40, readFileSync as readFileSync36 } from "fs";
|
|
33447
|
-
import { dirname as dirname28, join as
|
|
33910
|
+
import { dirname as dirname28, join as join41 } from "node:path";
|
|
33448
33911
|
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
33449
33912
|
|
|
33450
33913
|
// packages/updater/src/version.ts
|
|
@@ -33472,12 +33935,12 @@ function resolveChangelogPath() {
|
|
|
33472
33935
|
const dir = dirname28(fileURLToPath13(import.meta.url));
|
|
33473
33936
|
const root = findPackageRoot(dir, "@pi-unipi/unipi");
|
|
33474
33937
|
if (root) {
|
|
33475
|
-
const packaged =
|
|
33938
|
+
const packaged = join41(root, "CHANGELOG.md");
|
|
33476
33939
|
if (existsSync40(packaged)) return packaged;
|
|
33477
33940
|
}
|
|
33478
33941
|
} catch {
|
|
33479
33942
|
}
|
|
33480
|
-
return
|
|
33943
|
+
return join41(process.cwd(), "CHANGELOG.md");
|
|
33481
33944
|
}
|
|
33482
33945
|
var VERSION_HEADER_RE = /^## \[(.+?)\](?:\s*[-—–]\s*(.+))?$/;
|
|
33483
33946
|
var SECTION_HEADER_RE = /^### (.+)$/;
|
|
@@ -33729,9 +34192,9 @@ function renderChangelogOverlay() {
|
|
|
33729
34192
|
|
|
33730
34193
|
// packages/updater/src/settings.ts
|
|
33731
34194
|
init_core();
|
|
33732
|
-
import { readFileSync as readFileSync37, writeFileSync as
|
|
33733
|
-
import { dirname as dirname29, join as
|
|
33734
|
-
import { homedir as
|
|
34195
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync28, mkdirSync as mkdirSync24, existsSync as existsSync42 } from "fs";
|
|
34196
|
+
import { dirname as dirname29, join as join42 } from "path";
|
|
34197
|
+
import { homedir as homedir26 } from "os";
|
|
33735
34198
|
var DEFAULT_CONFIG6 = {
|
|
33736
34199
|
checkIntervalMs: 36e5,
|
|
33737
34200
|
// 1 hour
|
|
@@ -33745,8 +34208,8 @@ var VALID_INTERVALS = {
|
|
|
33745
34208
|
};
|
|
33746
34209
|
var VALID_MODES = ["disabled", "notify", "auto"];
|
|
33747
34210
|
function resolveConfigPath2() {
|
|
33748
|
-
const base = UPDATER_DIRS.CONFIG.replace("~",
|
|
33749
|
-
return
|
|
34211
|
+
const base = UPDATER_DIRS.CONFIG.replace("~", homedir26());
|
|
34212
|
+
return join42(base, "config.json");
|
|
33750
34213
|
}
|
|
33751
34214
|
function loadConfig5() {
|
|
33752
34215
|
const configPath = resolveConfigPath2();
|
|
@@ -33763,8 +34226,8 @@ function loadConfig5() {
|
|
|
33763
34226
|
function saveConfig4(config) {
|
|
33764
34227
|
const configPath = resolveConfigPath2();
|
|
33765
34228
|
const dir = dirname29(configPath);
|
|
33766
|
-
|
|
33767
|
-
|
|
34229
|
+
mkdirSync24(dir, { recursive: true });
|
|
34230
|
+
writeFileSync28(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
33768
34231
|
}
|
|
33769
34232
|
function getIntervalLabel(ms) {
|
|
33770
34233
|
for (const [label, value] of Object.entries(VALID_INTERVALS)) {
|
|
@@ -33997,12 +34460,12 @@ init_core();
|
|
|
33997
34460
|
|
|
33998
34461
|
// packages/updater/src/cache.ts
|
|
33999
34462
|
init_core();
|
|
34000
|
-
import { readFileSync as readFileSync38, writeFileSync as
|
|
34001
|
-
import { dirname as dirname30, join as
|
|
34002
|
-
import { homedir as
|
|
34463
|
+
import { readFileSync as readFileSync38, writeFileSync as writeFileSync29, mkdirSync as mkdirSync25, existsSync as existsSync43 } from "fs";
|
|
34464
|
+
import { dirname as dirname30, join as join43 } from "path";
|
|
34465
|
+
import { homedir as homedir27 } from "os";
|
|
34003
34466
|
function resolveCachePath() {
|
|
34004
|
-
const base = UPDATER_DIRS.CACHE.replace("~",
|
|
34005
|
-
return
|
|
34467
|
+
const base = UPDATER_DIRS.CACHE.replace("~", homedir27());
|
|
34468
|
+
return join43(base, "last-check.json");
|
|
34006
34469
|
}
|
|
34007
34470
|
function readLastCheck() {
|
|
34008
34471
|
const cachePath = resolveCachePath();
|
|
@@ -34021,8 +34484,8 @@ function readLastCheck() {
|
|
|
34021
34484
|
function writeLastCheck(cache) {
|
|
34022
34485
|
const cachePath = resolveCachePath();
|
|
34023
34486
|
const dir = dirname30(cachePath);
|
|
34024
|
-
|
|
34025
|
-
|
|
34487
|
+
mkdirSync25(dir, { recursive: true });
|
|
34488
|
+
writeFileSync29(cachePath, JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
34026
34489
|
}
|
|
34027
34490
|
function isCheckDue(intervalMs) {
|
|
34028
34491
|
const cache = readLastCheck();
|
|
@@ -34430,8 +34893,8 @@ init_core();
|
|
|
34430
34893
|
import { Key as Key10 } from "@earendil-works/pi-tui";
|
|
34431
34894
|
|
|
34432
34895
|
// packages/input-shortcuts/src/registers.ts
|
|
34433
|
-
import { existsSync as existsSync44, mkdirSync as
|
|
34434
|
-
import { dirname as dirname31, join as
|
|
34896
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync26, readFileSync as readFileSync39, renameSync as renameSync8, writeFileSync as writeFileSync30 } from "node:fs";
|
|
34897
|
+
import { dirname as dirname31, join as join44 } from "node:path";
|
|
34435
34898
|
|
|
34436
34899
|
// packages/input-shortcuts/src/types.ts
|
|
34437
34900
|
var DEFAULT_CONFIG7 = {
|
|
@@ -34453,7 +34916,7 @@ var RegisterStore = class {
|
|
|
34453
34916
|
filePath;
|
|
34454
34917
|
loaded = false;
|
|
34455
34918
|
constructor(baseDir) {
|
|
34456
|
-
this.filePath = baseDir ?
|
|
34919
|
+
this.filePath = baseDir ? join44(baseDir, REGISTERS_FILE) : REGISTERS_FILE;
|
|
34457
34920
|
}
|
|
34458
34921
|
/** Get the stash register contents. */
|
|
34459
34922
|
getStash() {
|
|
@@ -34503,10 +34966,10 @@ var RegisterStore = class {
|
|
|
34503
34966
|
try {
|
|
34504
34967
|
const dir = dirname31(this.filePath);
|
|
34505
34968
|
if (!existsSync44(dir)) {
|
|
34506
|
-
|
|
34969
|
+
mkdirSync26(dir, { recursive: true });
|
|
34507
34970
|
}
|
|
34508
34971
|
const tmpPath = this.filePath + ".tmp";
|
|
34509
|
-
|
|
34972
|
+
writeFileSync30(tmpPath, JSON.stringify(this.data, null, 2), "utf-8");
|
|
34510
34973
|
renameSync8(tmpPath, this.filePath);
|
|
34511
34974
|
} catch {
|
|
34512
34975
|
}
|
|
@@ -34728,8 +35191,8 @@ var ChordOverlay = class extends Container2 {
|
|
|
34728
35191
|
|
|
34729
35192
|
// packages/input-shortcuts/src/settings-overlay.ts
|
|
34730
35193
|
import { SettingsList as SettingsList3 } from "@earendil-works/pi-tui";
|
|
34731
|
-
import { existsSync as existsSync45, mkdirSync as
|
|
34732
|
-
import { dirname as dirname32, join as
|
|
35194
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync27, readFileSync as readFileSync40, renameSync as renameSync9, writeFileSync as writeFileSync31 } from "node:fs";
|
|
35195
|
+
import { dirname as dirname32, join as join45 } from "node:path";
|
|
34733
35196
|
var ALT_KEY_OPTIONS = [
|
|
34734
35197
|
"alt+a",
|
|
34735
35198
|
"alt+b",
|
|
@@ -34761,7 +35224,7 @@ var ALT_KEY_OPTIONS = [
|
|
|
34761
35224
|
var CONFLICTS = /* @__PURE__ */ new Set(["alt+e"]);
|
|
34762
35225
|
var FREE_ALT_KEYS = ALT_KEY_OPTIONS.filter((k) => !CONFLICTS.has(k));
|
|
34763
35226
|
function loadConfig6(baseDir) {
|
|
34764
|
-
const filePath = baseDir ?
|
|
35227
|
+
const filePath = baseDir ? join45(baseDir, CONFIG_FILE) : CONFIG_FILE;
|
|
34765
35228
|
try {
|
|
34766
35229
|
if (existsSync45(filePath)) {
|
|
34767
35230
|
const raw = readFileSync40(filePath, "utf-8");
|
|
@@ -34776,14 +35239,14 @@ function loadConfig6(baseDir) {
|
|
|
34776
35239
|
return { ...DEFAULT_CONFIG7 };
|
|
34777
35240
|
}
|
|
34778
35241
|
function saveConfig5(config, baseDir) {
|
|
34779
|
-
const filePath = baseDir ?
|
|
35242
|
+
const filePath = baseDir ? join45(baseDir, CONFIG_FILE) : CONFIG_FILE;
|
|
34780
35243
|
try {
|
|
34781
35244
|
const dir = dirname32(filePath);
|
|
34782
35245
|
if (!existsSync45(dir)) {
|
|
34783
|
-
|
|
35246
|
+
mkdirSync27(dir, { recursive: true });
|
|
34784
35247
|
}
|
|
34785
35248
|
const tmpPath = filePath + ".tmp";
|
|
34786
|
-
|
|
35249
|
+
writeFileSync31(tmpPath, JSON.stringify(config, null, 2), "utf-8");
|
|
34787
35250
|
renameSync9(tmpPath, filePath);
|
|
34788
35251
|
} catch {
|
|
34789
35252
|
}
|
|
@@ -35212,9 +35675,9 @@ import { Type as Type8 } from "typebox";
|
|
|
35212
35675
|
// packages/cocoindex/bridge.ts
|
|
35213
35676
|
init_core();
|
|
35214
35677
|
import { execFileSync as execFileSync3, spawn as spawn4 } from "node:child_process";
|
|
35215
|
-
import { existsSync as existsSync46, mkdirSync as
|
|
35216
|
-
import { homedir as
|
|
35217
|
-
import { join as
|
|
35678
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync28, readFileSync as readFileSync41, writeFileSync as writeFileSync32, readdirSync as readdirSync14, statSync as statSync9 } from "node:fs";
|
|
35679
|
+
import { homedir as homedir28 } from "node:os";
|
|
35680
|
+
import { join as join46 } from "node:path";
|
|
35218
35681
|
var DEFAULT_PIPELINE_DIR = ".unipi/cocoindex";
|
|
35219
35682
|
var DEFAULT_UPDATE_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
35220
35683
|
var DEFAULT_LEXICAL_SCAN_LIMIT = 5e4;
|
|
@@ -35284,13 +35747,13 @@ function resolveCocoindexBin() {
|
|
|
35284
35747
|
if (resolved) return resolved;
|
|
35285
35748
|
} catch {
|
|
35286
35749
|
}
|
|
35287
|
-
const uvToolBin =
|
|
35750
|
+
const uvToolBin = join46(homedir28(), ".local", "bin", "cocoindex");
|
|
35288
35751
|
if (existsSync46(uvToolBin)) return uvToolBin;
|
|
35289
|
-
const miseRoot =
|
|
35752
|
+
const miseRoot = join46(homedir28(), ".local", "share", "mise", "installs", "python");
|
|
35290
35753
|
try {
|
|
35291
35754
|
const versions = readdirSync14(miseRoot).sort().reverse();
|
|
35292
35755
|
for (const ver of versions) {
|
|
35293
|
-
const binPath =
|
|
35756
|
+
const binPath = join46(miseRoot, ver, "bin", "cocoindex");
|
|
35294
35757
|
if (existsSync46(binPath)) return binPath;
|
|
35295
35758
|
}
|
|
35296
35759
|
} catch {
|
|
@@ -35298,13 +35761,13 @@ function resolveCocoindexBin() {
|
|
|
35298
35761
|
return "cocoindex";
|
|
35299
35762
|
}
|
|
35300
35763
|
function getPipelineDir(projectDir) {
|
|
35301
|
-
return
|
|
35764
|
+
return join46(projectDir, DEFAULT_PIPELINE_DIR);
|
|
35302
35765
|
}
|
|
35303
35766
|
async function isPipelineInitialized(pipelineDir) {
|
|
35304
|
-
return existsSync46(
|
|
35767
|
+
return existsSync46(join46(pipelineDir, "main.py"));
|
|
35305
35768
|
}
|
|
35306
35769
|
function detectTargetStore(pipelineDir) {
|
|
35307
|
-
const mainPyPath =
|
|
35770
|
+
const mainPyPath = join46(pipelineDir, "main.py");
|
|
35308
35771
|
if (!existsSync46(mainPyPath)) return "unknown";
|
|
35309
35772
|
try {
|
|
35310
35773
|
const content = readFileSync41(mainPyPath, "utf-8");
|
|
@@ -35320,14 +35783,14 @@ function detectTargetStore(pipelineDir) {
|
|
|
35320
35783
|
async function initPipeline(projectDir) {
|
|
35321
35784
|
const pipelineDir = getPipelineDir(projectDir);
|
|
35322
35785
|
if (!existsSync46(pipelineDir)) {
|
|
35323
|
-
|
|
35786
|
+
mkdirSync28(pipelineDir, { recursive: true });
|
|
35324
35787
|
}
|
|
35325
|
-
if (existsSync46(
|
|
35788
|
+
if (existsSync46(join46(pipelineDir, "main.py"))) {
|
|
35326
35789
|
return { success: true };
|
|
35327
35790
|
}
|
|
35328
35791
|
const embeddingConfig = loadEmbeddingConfig2();
|
|
35329
35792
|
const template = generatePipelineTemplate(projectDir, embeddingConfig);
|
|
35330
|
-
|
|
35793
|
+
writeFileSync32(join46(pipelineDir, "main.py"), template, "utf-8");
|
|
35331
35794
|
return { success: true };
|
|
35332
35795
|
}
|
|
35333
35796
|
async function indexProject(projectDir) {
|
|
@@ -35341,7 +35804,7 @@ async function indexProject(projectDir) {
|
|
|
35341
35804
|
};
|
|
35342
35805
|
}
|
|
35343
35806
|
const pipelineDir = getPipelineDir(projectDir);
|
|
35344
|
-
if (!existsSync46(
|
|
35807
|
+
if (!existsSync46(join46(pipelineDir, "main.py"))) {
|
|
35345
35808
|
return {
|
|
35346
35809
|
success: false,
|
|
35347
35810
|
chunksProcessed: 0,
|
|
@@ -35452,11 +35915,11 @@ function parseChunksProcessed(output) {
|
|
|
35452
35915
|
async function status(projectDir) {
|
|
35453
35916
|
const pipelineDir = getPipelineDir(projectDir);
|
|
35454
35917
|
const cliAvailable = await isAvailable();
|
|
35455
|
-
const pipelineConfigured = existsSync46(
|
|
35918
|
+
const pipelineConfigured = existsSync46(join46(pipelineDir, "main.py"));
|
|
35456
35919
|
const targetStore = detectTargetStore(pipelineDir);
|
|
35457
35920
|
let docCount = 0;
|
|
35458
35921
|
let lastRun = null;
|
|
35459
|
-
const lancedbPath =
|
|
35922
|
+
const lancedbPath = join46(pipelineDir, ".lancedb");
|
|
35460
35923
|
if (existsSync46(lancedbPath)) {
|
|
35461
35924
|
try {
|
|
35462
35925
|
const stat = statSync9(lancedbPath);
|
|
@@ -35479,7 +35942,7 @@ async function search(projectDir, query, options) {
|
|
|
35479
35942
|
const limit = options?.limit ?? 10;
|
|
35480
35943
|
const offset = options?.offset ?? 0;
|
|
35481
35944
|
try {
|
|
35482
|
-
const lancedbPath =
|
|
35945
|
+
const lancedbPath = join46(getPipelineDir(projectDir), ".lancedb");
|
|
35483
35946
|
if (!existsSync46(lancedbPath)) {
|
|
35484
35947
|
return [];
|
|
35485
35948
|
}
|
|
@@ -35594,7 +36057,7 @@ function countOccurrences(value, needle) {
|
|
|
35594
36057
|
}
|
|
35595
36058
|
function loadEmbeddingConfig2() {
|
|
35596
36059
|
const envKey = process.env.OPENROUTER_API_KEY ?? null;
|
|
35597
|
-
const configPath =
|
|
36060
|
+
const configPath = join46(homedir28(), ".unipi", "memory", "config.json");
|
|
35598
36061
|
try {
|
|
35599
36062
|
if (existsSync46(configPath)) {
|
|
35600
36063
|
const raw = readFileSync41(configPath, "utf-8");
|
|
@@ -35801,9 +36264,22 @@ app = coco.App(
|
|
|
35801
36264
|
}
|
|
35802
36265
|
|
|
35803
36266
|
// packages/cocoindex/tools.ts
|
|
36267
|
+
var MAX_SEARCH_RESULTS = 50;
|
|
36268
|
+
function normalizeSearchPage(limit, offset) {
|
|
36269
|
+
const requestedLimit = Number.isFinite(limit) ? Math.floor(limit) : 10;
|
|
36270
|
+
const requestedOffset = Number.isFinite(offset) ? Math.floor(offset) : 0;
|
|
36271
|
+
return {
|
|
36272
|
+
limit: Math.min(MAX_SEARCH_RESULTS, Math.max(1, requestedLimit)),
|
|
36273
|
+
offset: Math.max(0, requestedOffset)
|
|
36274
|
+
};
|
|
36275
|
+
}
|
|
35804
36276
|
var SearchParams = Type8.Object({
|
|
35805
36277
|
query: Type8.String({ description: "Search query against indexed content" }),
|
|
35806
|
-
limit: Type8.Optional(Type8.Number({
|
|
36278
|
+
limit: Type8.Optional(Type8.Number({
|
|
36279
|
+
description: `Max results (default 10, hard cap ${MAX_SEARCH_RESULTS})`,
|
|
36280
|
+
minimum: 1,
|
|
36281
|
+
maximum: MAX_SEARCH_RESULTS
|
|
36282
|
+
})),
|
|
35807
36283
|
offset: Type8.Optional(Type8.Number({ description: "Pagination offset", minimum: 0 }))
|
|
35808
36284
|
});
|
|
35809
36285
|
var StatusParams = Type8.Object({});
|
|
@@ -35819,7 +36295,7 @@ function registerCocoindexTools(pi, deps) {
|
|
|
35819
36295
|
label: "CocoIndex Search",
|
|
35820
36296
|
description: "Search indexed content using semantic vector search when available, with full-text/lexical fallbacks. Diagnostic/search only: this tool never installs CocoIndex. Use /unipi:cocoindex-init to set up/install, then /unipi:cocoindex-update to index.",
|
|
35821
36297
|
parameters: SearchParams,
|
|
35822
|
-
async execute(_toolCallId, params) {
|
|
36298
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
35823
36299
|
try {
|
|
35824
36300
|
const available = await isAvailable();
|
|
35825
36301
|
if (!available) {
|
|
@@ -35828,9 +36304,10 @@ function registerCocoindexTools(pi, deps) {
|
|
|
35828
36304
|
{ cliAvailable: false }
|
|
35829
36305
|
);
|
|
35830
36306
|
}
|
|
35831
|
-
const
|
|
35832
|
-
|
|
35833
|
-
|
|
36307
|
+
const { limit, offset } = normalizeSearchPage(params.limit, params.offset);
|
|
36308
|
+
const results = await search(deps.getProjectDir(ctx), params.query, {
|
|
36309
|
+
limit,
|
|
36310
|
+
offset
|
|
35834
36311
|
});
|
|
35835
36312
|
if (results.length === 0) {
|
|
35836
36313
|
return textResult3(
|
|
@@ -35845,7 +36322,8 @@ ${r.content.slice(0, 300)}`
|
|
|
35845
36322
|
`Found ${results.length} results for "${params.query}":
|
|
35846
36323
|
|
|
35847
36324
|
${lines.join("\n\n")}`,
|
|
35848
|
-
|
|
36325
|
+
// Avoid duplicating full result content in model-visible details.
|
|
36326
|
+
{ count: results.length, query: params.query, limit, offset }
|
|
35849
36327
|
);
|
|
35850
36328
|
} catch (err) {
|
|
35851
36329
|
return textResult3(`CocoIndex search error: ${err}`, { error: true });
|
|
@@ -35857,9 +36335,9 @@ ${lines.join("\n\n")}`,
|
|
|
35857
36335
|
label: "CocoIndex Status",
|
|
35858
36336
|
description: "Check CocoIndex indexing status. Diagnostic only; use commands for interactive install/update.",
|
|
35859
36337
|
parameters: StatusParams,
|
|
35860
|
-
async execute() {
|
|
36338
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
35861
36339
|
try {
|
|
35862
|
-
const info = await status(deps.
|
|
36340
|
+
const info = await status(deps.getProjectDir(ctx));
|
|
35863
36341
|
const lines = [
|
|
35864
36342
|
`\u{1F4E6} CocoIndex Status`,
|
|
35865
36343
|
`CLI available: ${info.cliAvailable ? "\u2705" : "\u274C"}`,
|
|
@@ -35892,8 +36370,8 @@ init_core();
|
|
|
35892
36370
|
// packages/cocoindex/installer.ts
|
|
35893
36371
|
init_core();
|
|
35894
36372
|
import { execFileSync as execFileSync4, spawn as spawn5 } from "node:child_process";
|
|
35895
|
-
import { homedir as
|
|
35896
|
-
import { delimiter, join as
|
|
36373
|
+
import { homedir as homedir29 } from "node:os";
|
|
36374
|
+
import { delimiter, join as join47 } from "node:path";
|
|
35897
36375
|
var DEFAULT_STEP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
35898
36376
|
function detectShell() {
|
|
35899
36377
|
const shell = process.env.SHELL ?? "";
|
|
@@ -36154,10 +36632,10 @@ Timed out after ${step.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS}ms.`;
|
|
|
36154
36632
|
});
|
|
36155
36633
|
}
|
|
36156
36634
|
function installerEnv() {
|
|
36157
|
-
const home =
|
|
36635
|
+
const home = homedir29();
|
|
36158
36636
|
const extraPaths = [
|
|
36159
|
-
|
|
36160
|
-
|
|
36637
|
+
join47(home, ".local", "bin"),
|
|
36638
|
+
join47(home, ".local", "share", "mise", "shims")
|
|
36161
36639
|
];
|
|
36162
36640
|
const currentPath = process.env.PATH ?? "";
|
|
36163
36641
|
return {
|
|
@@ -36322,15 +36800,11 @@ ${lines.join("\n\n")}`,
|
|
|
36322
36800
|
// packages/cocoindex/index.ts
|
|
36323
36801
|
function cocoindexExtension(pi) {
|
|
36324
36802
|
registerCocoindexCommands(pi);
|
|
36803
|
+
registerCocoindexTools(pi, {
|
|
36804
|
+
getProjectDir: (ctx) => ctx.cwd ?? process.cwd()
|
|
36805
|
+
});
|
|
36325
36806
|
pi.on("session_start", async (_event, ctx) => {
|
|
36326
36807
|
const projectDir = ctx.cwd ?? process.cwd();
|
|
36327
|
-
const pipelineDir = getPipelineDir(projectDir);
|
|
36328
|
-
const initialized = await isPipelineInitialized(pipelineDir);
|
|
36329
|
-
registerCocoindexTools(pi, {
|
|
36330
|
-
projectDir,
|
|
36331
|
-
pipelineDir,
|
|
36332
|
-
initialized
|
|
36333
|
-
});
|
|
36334
36808
|
emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
|
|
36335
36809
|
name: MODULES.COCOINDEX,
|
|
36336
36810
|
version: "0.1.0",
|